From 67fcd69eb8af92ec3e9eaf43fe38333bf69a0b5f Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Fri, 7 Aug 2026 19:30:58 +0200 Subject: [PATCH] fix: inverted tag expiration in Redis handler + saveItemOptions board name - Fix critical bug: tag expiration comparison was inverted (< should be >) in Redis cache handler, which would prevent revalidateTag from clearing stale entries - Pass board name to invalidateBoardCache in saveItemOptions - Add clarifying comment about two-tier invalidation helpers --- packages/api/src/cache-invalidation.ts | 6 ++++++ packages/api/src/router/widgets/options.ts | 6 +++++- packages/next-cache-handler/src/redis-handler.ts | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/api/src/cache-invalidation.ts b/packages/api/src/cache-invalidation.ts index 5dd7941e0..9fc0693ef 100644 --- a/packages/api/src/cache-invalidation.ts +++ b/packages/api/src/cache-invalidation.ts @@ -1,6 +1,12 @@ // ponytail: invalidation is wired now; `cacheTag()` on RSC read paths is Phase 1 follow-up. // Until reads use `"use cache"` + `cacheTag()`, these calls affect only Next.js-internal // cached segments (layout shells, static params) via cacheComponents, not cross-request DB queries. +// +// Two-tier invalidation: +// - `invalidateIntegrationCache`: for integration CONFIG changes (CRUD, permissions). +// Clears module cache + Next tags + Redis session store. +// - `invalidateIntegrationDataCache` (from integration-data-cache.ts): for integration DATA changes +// (widget interact mutations). Clears module cache only — pair with requestHandler.invalidateCache(). import { revalidateTag } from "next/cache"; diff --git a/packages/api/src/router/widgets/options.ts b/packages/api/src/router/widgets/options.ts index 6f56567a6..408bc2147 100644 --- a/packages/api/src/router/widgets/options.ts +++ b/packages/api/src/router/widgets/options.ts @@ -53,6 +53,10 @@ export const optionsRouter = createTRPCRouter({ .set({ options: SuperJSON.stringify(options) }) .where(eq(items.id, input.itemId)); - invalidateBoardCache(input.boardId); + const board = await ctx.db.query.boards.findFirst({ + where: eq(boards.id, input.boardId), + columns: { name: true }, + }); + invalidateBoardCache(input.boardId, board?.name); }), }); diff --git a/packages/next-cache-handler/src/redis-handler.ts b/packages/next-cache-handler/src/redis-handler.ts index cddcbd93e..d6b7b3f2c 100644 --- a/packages/next-cache-handler/src/redis-handler.ts +++ b/packages/next-cache-handler/src/redis-handler.ts @@ -57,7 +57,7 @@ export class RedisCacheHandler implements CacheHandler { const allTags = [...parsed.tags, ...softTags]; const expiration = await this.getExpiration(allTags); - if (expiration !== 0 && expiration < parsed.timestamp) { + if (expiration !== 0 && expiration > parsed.timestamp) { return undefined; }