Skip to content

Commit 4a11747

Browse files
authored
fix: bypass next tag cache when there are no tags to check (#988)
1 parent 23ed1df commit 4a11747

File tree

3 files changed

+38
-24
lines changed

3 files changed

+38
-24
lines changed

.changeset/rude-trains-know.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/aws": patch
3+
---
4+
5+
fix: bypass next tag cache when there are no tags to check

packages/open-next/src/utils/cache.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export async function hasBeenRevalidated(
2323
}
2424
const lastModified = cacheEntry.lastModified ?? Date.now();
2525
if (globalThis.tagCache.mode === "nextMode") {
26-
return await globalThis.tagCache.hasBeenRevalidated(tags, lastModified);
26+
return tags.length === 0
27+
? false
28+
: await globalThis.tagCache.hasBeenRevalidated(tags, lastModified);
2729
}
2830
// TODO: refactor this, we should introduce a new method in the tagCache interface so that both implementations use hasBeenRevalidated
2931
const _lastModified = await globalThis.tagCache.getLastModified(

packages/tests-unit/tests/adapters/cache.test.ts

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable sonarjs/no-duplicate-string */
22
import Cache, { SOFT_TAG_PREFIX } from "@opennextjs/aws/adapters/cache.js";
3-
import { vi } from "vitest";
3+
import { type Mock, vi } from "vitest";
44

55
declare global {
66
var openNextConfig: {
@@ -40,6 +40,7 @@ describe("CacheHandler", () => {
4040
.fn()
4141
.mockResolvedValue(new Date("2024-01-02T00:00:00Z").getTime()),
4242
writeTags: vi.fn(),
43+
getPathsByTags: undefined as Mock | undefined,
4344
};
4445
globalThis.tagCache = tagCache;
4546

@@ -71,6 +72,8 @@ describe("CacheHandler", () => {
7172
},
7273
};
7374
globalThis.isNextAfter15 = false;
75+
tagCache.mode = "original";
76+
tagCache.getPathsByTags = undefined;
7477
});
7578

7679
describe("get", () => {
@@ -147,11 +150,13 @@ describe("CacheHandler", () => {
147150
tagCache.mode = "nextMode";
148151
tagCache.hasBeenRevalidated.mockResolvedValueOnce(true);
149152

150-
const result = await cache.get("key", { kind: "FETCH" });
153+
const result = await cache.get("key", {
154+
kind: "FETCH",
155+
tags: ["tag"],
156+
});
151157
expect(getFetchCacheSpy).toHaveBeenCalled();
158+
expect(tagCache.hasBeenRevalidated).toHaveBeenCalled();
152159
expect(result).toBeNull();
153-
// Reset the tagCache mode
154-
tagCache.mode = "original";
155160
});
156161

157162
it("Should return null when incremental cache throws", async () => {
@@ -198,16 +203,20 @@ describe("CacheHandler", () => {
198203
incrementalCache.get.mockResolvedValueOnce({
199204
value: {
200205
type: "route",
206+
meta: {
207+
headers: {
208+
"x-next-cache-tags": "tag",
209+
},
210+
},
201211
},
202212
lastModified: Date.now(),
203213
});
204214

205215
const result = await cache.get("key", { kindHint: "app" });
206216

207217
expect(getIncrementalCache).toHaveBeenCalled();
218+
expect(tagCache.hasBeenRevalidated).toHaveBeenCalled();
208219
expect(result).toBeNull();
209-
// Reset the tagCache mode
210-
tagCache.mode = "original";
211220
});
212221

213222
it("Should return value when cache data type is route", async () => {
@@ -536,10 +545,10 @@ describe("CacheHandler", () => {
536545
});
537546

538547
it("Should call tagCache.writeTags", async () => {
539-
globalThis.tagCache.getByTag.mockResolvedValueOnce(["/path"]);
548+
tagCache.getByTag.mockResolvedValueOnce(["/path"]);
540549
await cache.revalidateTag("tag");
541550

542-
expect(globalThis.tagCache.getByTag).toHaveBeenCalledWith("tag");
551+
expect(tagCache.getByTag).toHaveBeenCalledWith("tag");
543552

544553
expect(tagCache.writeTags).toHaveBeenCalledTimes(1);
545554
expect(tagCache.writeTags).toHaveBeenCalledWith([
@@ -551,8 +560,8 @@ describe("CacheHandler", () => {
551560
});
552561

553562
it("Should call invalidateCdnHandler.invalidatePaths", async () => {
554-
globalThis.tagCache.getByTag.mockResolvedValueOnce(["/path"]);
555-
globalThis.tagCache.getByPath.mockResolvedValueOnce([]);
563+
tagCache.getByTag.mockResolvedValueOnce(["/path"]);
564+
tagCache.getByPath.mockResolvedValueOnce([]);
556565
await cache.revalidateTag(`${SOFT_TAG_PREFIX}path`);
557566

558567
expect(tagCache.writeTags).toHaveBeenCalledTimes(1);
@@ -567,7 +576,7 @@ describe("CacheHandler", () => {
567576
});
568577

569578
it("Should not call invalidateCdnHandler.invalidatePaths for fetch cache key ", async () => {
570-
globalThis.tagCache.getByTag.mockResolvedValueOnce(["123456"]);
579+
tagCache.getByTag.mockResolvedValueOnce(["123456"]);
571580
await cache.revalidateTag("tag");
572581

573582
expect(tagCache.writeTags).toHaveBeenCalledTimes(1);
@@ -582,7 +591,7 @@ describe("CacheHandler", () => {
582591
});
583592

584593
it("Should only call writeTags for nextMode", async () => {
585-
globalThis.tagCache.mode = "nextMode";
594+
tagCache.mode = "nextMode";
586595
await cache.revalidateTag(["tag1", "tag2"]);
587596

588597
expect(tagCache.writeTags).toHaveBeenCalledTimes(1);
@@ -591,18 +600,16 @@ describe("CacheHandler", () => {
591600
});
592601

593602
it("Should not call writeTags when the tag list is empty for nextMode", async () => {
594-
globalThis.tagCache.mode = "nextMode";
603+
tagCache.mode = "nextMode";
595604
await cache.revalidateTag([]);
596605

597606
expect(tagCache.writeTags).not.toHaveBeenCalled();
598607
expect(invalidateCdnHandler.invalidatePaths).not.toHaveBeenCalled();
599608
});
600609

601610
it("Should call writeTags and invalidateCdnHandler.invalidatePaths for nextMode that supports getPathsByTags", async () => {
602-
globalThis.tagCache.mode = "nextMode";
603-
globalThis.tagCache.getPathsByTags = vi
604-
.fn()
605-
.mockResolvedValueOnce(["/path"]);
611+
tagCache.mode = "nextMode";
612+
tagCache.getPathsByTags = vi.fn().mockResolvedValueOnce(["/path"]);
606613
await cache.revalidateTag("tag");
607614

608615
expect(tagCache.writeTags).toHaveBeenCalledTimes(1);
@@ -619,8 +626,6 @@ describe("CacheHandler", () => {
619626
],
620627
},
621628
]);
622-
// Reset the getPathsByTags
623-
globalThis.tagCache.getPathsByTags = undefined;
624629
});
625630
});
626631

@@ -662,7 +667,7 @@ describe("CacheHandler", () => {
662667
});
663668

664669
it("Should not bypass tag cache validation when shouldBypassTagCache is false", async () => {
665-
globalThis.tagCache.mode = "nextMode";
670+
tagCache.mode = "nextMode";
666671
incrementalCache.get.mockResolvedValueOnce({
667672
value: {
668673
kind: "FETCH",
@@ -688,7 +693,7 @@ describe("CacheHandler", () => {
688693
});
689694

690695
it("Should not bypass tag cache validation when shouldBypassTagCache is undefined", async () => {
691-
globalThis.tagCache.mode = "nextMode";
696+
tagCache.mode = "nextMode";
692697
tagCache.hasBeenRevalidated.mockResolvedValueOnce(false);
693698
incrementalCache.get.mockResolvedValueOnce({
694699
value: {
@@ -762,11 +767,12 @@ describe("CacheHandler", () => {
762767
});
763768

764769
it("Should not bypass tag cache validation when shouldBypassTagCache is false", async () => {
765-
globalThis.tagCache.mode = "nextMode";
770+
tagCache.mode = "nextMode";
766771
incrementalCache.get.mockResolvedValueOnce({
767772
value: {
768773
type: "route",
769774
body: "{}",
775+
meta: { headers: { "x-next-cache-tags": "tag" } },
770776
},
771777
lastModified: Date.now(),
772778
shouldBypassTagCache: false,
@@ -780,12 +786,13 @@ describe("CacheHandler", () => {
780786
});
781787

782788
it("Should return null when tag cache indicates revalidation and shouldBypassTagCache is false", async () => {
783-
globalThis.tagCache.mode = "nextMode";
789+
tagCache.mode = "nextMode";
784790
tagCache.hasBeenRevalidated.mockResolvedValueOnce(true);
785791
incrementalCache.get.mockResolvedValueOnce({
786792
value: {
787793
type: "route",
788794
body: "{}",
795+
meta: { headers: { "x-next-cache-tags": "tag" } },
789796
},
790797
lastModified: Date.now(),
791798
shouldBypassTagCache: false,

0 commit comments

Comments
 (0)