Skip to content

revalidateTag causing 'use cache' not to work with redis #32

@ChristianThingKnudsen

Description

@ChristianThingKnudsen

Package

@mrjasonroy/cache-components-cache-handler

Package Version

16.1.6

Next.js Version

16.1.6

Bug Description

I have been using your plugin with redis in sentinel mode.

The function was cached the first many times and also fetched new data when data had to be revalidated. However, after the branches tag has been revalidated using: revalidateTag("branches", "max") it would never cache the function again.

In the logs i could see it ended up in the case:
log?.("get", cacheKey, "had expired tag", tag);

I managed to fix it by overwrite the get function and replacing this bit from redis.ts:

  // Check tag expiration
  let revalidate = entry.revalidate;
  for (const tag of entry.tags) {
    const tagData = await redis.hGetAll(getTagKey(tag));
  
    if (tagData.expired) {
      const expired = Number.parseInt(tagData.expired, 10);
      if (expired > entry.timestamp) {
        log?.("get", cacheKey, "had expired tag", tag);
        await redis.del(key);
        return undefined;
      }
    }
  
    if (tagData.stale) {
      const stale = Number.parseInt(tagData.stale, 10);
      if (stale > entry.timestamp) {
        log?.("get", cacheKey, "had stale tag", tag);
        revalidate = -1;
      }
    }
  }

With:

   // Check tag expiration
  let revalidate = entry.revalidate;
  for (const tag of entry.tags) {
      const tagData = await redisAdapter.hGetAll(getTagKey(tag));

      // Determine the revalidation timestamp.
      // revalidateTag('tag', 'max') sets both stale=now and expired=now+huge
      // revalidateTag('tag') sets only expired=now
      // Use stale when present (it's always the actual revalidation time when using 'max').
      // Fall back to expired only when stale is absent (immediate expiration).
      const revalidatedAt = tagData.stale
          ? Number.parseInt(tagData.stale, 10)
          : tagData.expired
            ? Number.parseInt(tagData.expired, 10)
            : 0;

      if (revalidatedAt && revalidatedAt > entry.timestamp) {
          log?.('get', cacheKey, 'tag revalidated after entry was cached', tag, {
              revalidatedAt,
              entryTimestamp: entry.timestamp
          });
          await redisAdapter.del(key);
          return undefined;
      }
  }

Steps to Reproduce

Create a function similar to:

const tagsBranches: string = ["branches", "data", "all"]
export const getBranches = async (searchTerm: string | undefined = undefined): Promise<BranchListResponse> => {
    'use cache';
    cacheLife("branches");
    cacheTag(...tagsBranches);

    const rand = Math.random().toString(36).substring(2, 8);
    console.log("rand:", rand);

};

Call revalidateTag("branches", "max")

See if the function with 'use cache' is cached when called multiple times.

Expected Behavior

After revalidate revalidateTag("branches", "max");

I do not expect my function with 'use cache' to be cached the first time, but on the second and third time, I do.

Actual Behavior

'use cache' functions does not work after revalidate tag

Error Logs

Configuration

Environment

Development (next dev)

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions