Skip to content

Bug: Idempotency cache persistence never deduplicates or expires records with the Valkey Glide client #5716

Description

@svozza

Expected Behavior

Using CachePersistenceLayer with @valkey/valkey-glide, as shown in the documentation, should behave the same as with @redis/client: a second invocation with the same payload returns the stored result without running the function, and records expire after expiresAfterSeconds.

Current Behavior

CachePersistenceLayer passes { NX: true, EX: seconds } to client.set(). That is the @redis/client option shape. Glide's set() accepts { conditionalSet, expiry } and ignores unknown keys, so every write is an unconditional SET with no TTL. The CacheClient interface types the options argument as unknown, so nothing flags the mismatch.

Consequences with Glide:

  • saveInProgress() always succeeds, so concurrent and repeated invocations all run the function. Idempotency is silently disabled.
  • Records are written without a TTL and are never removed.

Verified against Valkey 8 in Docker with @valkey/valkey-glide 2.5.2: two identical makeIdempotent calls executed the function twice, and the stored key reported a TTL of -1. The same test with @redis/client 6.2.1 against the same server executed once with the expected TTL.

Code snippet

import { GlideClient } from '@valkey/valkey-glide';

const client = await GlideClient.createClient({
  addresses: [{ host: '127.0.0.1', port: 6379 }],
});

// What the adapter sends today: both writes succeed, no TTL.
console.log(await client.set('k', 'a', { EX: 10, NX: true })); // OK
console.log(await client.set('k', 'b', { EX: 10, NX: true })); // OK (should be null)
console.log(await client.ttl('k')); // -1 (should be 10)

// What Glide expects.
console.log(
  await client.set('k2', 'a', {
    conditionalSet: 'onlyIfDoesNotExist',
    expiry: { type: 'EX', count: 10 },
  })
); // OK
console.log(
  await client.set('k2', 'b', {
    conditionalSet: 'onlyIfDoesNotExist',
    expiry: { type: 'EX', count: 10 },
  })
); // null

Steps to Reproduce

  1. Start a Valkey or Redis instance and install the idempotency package and @valkey/valkey-glide.
  2. Wrap a function with makeIdempotent using new CachePersistenceLayer({ client }) where client is a GlideClient, as in the documentation example.
  3. Invoke it twice with the same payload. The function runs both times.
  4. Inspect the key. TTL returns -1.
  5. As a control, repeat with @redis/client. The function runs once and the TTL is set.

Possible Solution

Translate the write options per client. Either detect a Glide client by its set signature and map NX/PX to conditionalSet and expiry, or provide a documented Glide adapter that implements CacheClient. Tighten the CacheClient.set options type so a mismatch is a compile-time error. Add a test that exercises the example against a real Glide client, since the current example test invokes the handler once and cannot detect the missing deduplication.

Powertools for AWS Lambda (TypeScript) version

2.35.0

AWS Lambda function runtime

22.x

Packaging format used

npm

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmedThe scope is clear, ready for implementationidempotencyThis item relates to the Idempotency Utility

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions