Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions packages/cache-handler/src/data-cache/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ function loadIoredis(type: string): typeof import("ioredis").default {
}

/**
* Create adapter for ioredis (lowercase methods) to match RedisClient interface (camelCase)
* Create adapter for ioredis (lowercase methods) to match RedisClient interface (camelCase).
* Translates node-redis-style SET options `{ EX: seconds }` to ioredis positional args.
*/
function createRedisAdapter(redis: import("ioredis").default): RedisClient {
return {
get: (key) => redis.get(key),
set: (key, value, exFlag?, ttl?) => {
if (exFlag === "EX" && typeof ttl === "number") {
return redis.set(key, value, "EX", ttl) as Promise<unknown>;
set: (key, value, ...args) => {
// node-redis style: set(key, value, { EX: seconds })
const opts = args[0] as Record<string, unknown> | undefined;
if (opts && typeof opts === "object" && typeof opts.EX === "number") {
return redis.set(key, value, "EX", opts.EX) as Promise<unknown>;
}
return redis.set(key, value) as Promise<unknown>;
},
Expand Down
17 changes: 9 additions & 8 deletions packages/cache-handler/src/data-cache/redis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ class FakeRedis {
this.setCalls.push({ key, args });

let expireAt: number | undefined;
// ioredis style: set(key, value, "EX", seconds)
if (args[0] === "EX" && typeof args[1] === "number") {
expireAt = Date.now() + args[1] * 1000;
// node-redis style: set(key, value, { EX: seconds })
const opts = args[0] as Record<string, unknown> | undefined;
if (opts && typeof opts === "object" && typeof opts.EX === "number") {
expireAt = Date.now() + opts.EX * 1000;
}

this.store.set(key, { value, expireAt });
Expand Down Expand Up @@ -147,7 +148,7 @@ describe("RedisDataCacheHandler", () => {
expect(redis.setCalls).toHaveLength(1);
expect(redis.setCalls[0]).toMatchObject({
key: "nextjs:data-cache:cache-key",
args: ["EX", 120],
args: [{ EX: 120 }],
});

const result = await handler.get("cache-key", []);
Expand Down Expand Up @@ -231,7 +232,7 @@ describe("RedisDataCacheHandler", () => {
expect(redis.delCalls).toContainEqual(["nextjs:data-cache:invalidate-key"]);
});

test("sets TTL correctly with ioredis style args (fixes #16)", async () => {
test("sets TTL correctly with node-redis style options (fixes #16)", async () => {
vi.useFakeTimers();
vi.setSystemTime(BASE_TIME);

Expand All @@ -241,11 +242,11 @@ describe("RedisDataCacheHandler", () => {
const entry = createEntry("ttl-test", { expire: 60, revalidate: 30 });
await handler.set("ttl-key", Promise.resolve(entry));

// Verify the set call used ioredis style: "EX", seconds
// Verify the set call used node-redis style: { EX: seconds }
expect(redis.setCalls).toHaveLength(1);
const setCall = redis.setCalls[0];
expect(setCall.key).toBe("nextjs:data-cache:ttl-key");
expect(setCall.args).toEqual(["EX", 60]);
expect(setCall.args).toEqual([{ EX: 60 }]);
});

test("TTL causes entry to expire after specified time", async () => {
Expand Down Expand Up @@ -286,6 +287,6 @@ describe("RedisDataCacheHandler", () => {
await handler.set("default-ttl-key", Promise.resolve(entry));

expect(redis.setCalls).toHaveLength(1);
expect(redis.setCalls[0].args).toEqual(["EX", 3600]);
expect(redis.setCalls[0].args).toEqual([{ EX: 3600 }]);
});
});
8 changes: 4 additions & 4 deletions packages/cache-handler/src/data-cache/redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { DataCacheEntry, DataCacheHandler } from "./types.js";

export interface RedisDataCacheHandlerOptions {
/**
* Redis client instance (ioredis compatible)
* Redis client instance (node-redis compatible)
*/
redis: RedisClient;

Expand Down Expand Up @@ -41,8 +41,8 @@ export interface RedisDataCacheHandlerOptions {
}

/**
* Redis client interface (ioredis compatible)
* Uses ioredis-style SET with "EX" positional args: set(key, value, "EX", seconds)
* Redis client interface (node-redis compatible)
* Uses node-redis-style SET with options object: set(key, value, { EX: seconds })
*/
export interface RedisClient {
get(key: string): Promise<string | null>;
Expand Down Expand Up @@ -275,7 +275,7 @@ export function createRedisDataCacheHandler(
const ttl = entry.expire < 4294967294 ? entry.expire : defaultTTL;

// Store in Redis with TTL
await redis.set(key, JSON.stringify(serialized), "EX", Math.ceil(ttl));
await redis.set(key, JSON.stringify(serialized), { EX: Math.ceil(ttl) });

log?.("set", cacheKey, "done", { ttl });
} catch (error) {
Expand Down
Loading