Skip to content

Commit 7820ad0

Browse files
Reuse sharded tag data on regional cache fill (#1200)
* reuse sharded tag data on regional cache fill * tighten sharded tag-cache regression test
1 parent 585795d commit 7820ad0

3 files changed

Lines changed: 45 additions & 3 deletions

File tree

.changeset/green-zebras-know.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@opennextjs/cloudflare": patch
3+
---
4+
5+
fix: reuse sharded tag data when filling the regional cache.
6+
7+
The sharded tag cache miss path already reads tag data from the Durable Object before answering the request. Reuse that fetched data when populating the regional cache so a shard miss does not immediately trigger a second identical Durable Object read.

packages/cloudflare/src/api/overrides/tag-cache/do-sharded-tag-cache.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,35 @@ describe("DOShardedTagCache", () => {
269269
expect(cache.putToRegionalCache).toHaveBeenCalled();
270270
});
271271

272+
it("should only read tag data once on a regional-cache miss", async () => {
273+
const putMock = vi.fn();
274+
// @ts-expect-error - Defined on cloudfare context
275+
globalThis.caches = {
276+
open: vi.fn().mockResolvedValue({
277+
match: vi.fn().mockResolvedValue(null),
278+
put: putMock,
279+
}),
280+
};
281+
const cache = shardedDOTagCache({ baseShardSize: 4, regionalCache: true });
282+
cache.getFromRegionalCache = vi.fn().mockResolvedValueOnce([]);
283+
getTagDataMock.mockResolvedValueOnce({
284+
tag1: { revalidatedAt: 123455, stale: null, expire: null },
285+
});
286+
287+
const result = await cache.hasBeenRevalidated(["tag1"], 123456);
288+
289+
expect(result).toBe(false);
290+
expect(getTagDataMock).toHaveBeenCalledTimes(1);
291+
expect(getTagDataMock).toHaveBeenCalledWith(["tag1"]);
292+
expect(putMock).toHaveBeenCalledTimes(1);
293+
expect(putMock).toHaveBeenCalledWith(
294+
"http://local.cache/shard/tag-hard;shard-1?tag=tag1",
295+
expect.any(Response)
296+
);
297+
// @ts-expect-error - Defined on cloudfare context
298+
globalThis.caches = undefined;
299+
});
300+
272301
it("should call all the durable object instances", async () => {
273302
const cache = shardedDOTagCache();
274303
cache.getFromRegionalCache = vi.fn().mockResolvedValue([]);

packages/cloudflare/src/api/overrides/tag-cache/do-sharded-tag-cache.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,12 +322,16 @@ class ShardedDOTagCache implements NextModeTagCache {
322322
}
323323
}
324324

325-
public async putToRegionalCache(optsKey: CacheTagKeyOptions, stub: DurableObjectStub<DOShardedTagCache>) {
325+
public async putToRegionalCache(
326+
optsKey: CacheTagKeyOptions,
327+
stub: DurableObjectStub<DOShardedTagCache>,
328+
prefetchedTagData?: Record<string, TagData>
329+
) {
326330
if (!this.opts.regionalCache) return;
327331
const cache = await this.getCacheInstance();
328332
if (!cache) return;
329333
const tags = optsKey.tags;
330-
const tagData = await stub.getTagData(tags);
334+
const tagData = prefetchedTagData ?? (await stub.getTagData(tags));
331335
await Promise.all(
332336
tags.map(async (tag) => {
333337
let data = tagData[tag];
@@ -451,7 +455,9 @@ class ShardedDOTagCache implements NextModeTagCache {
451455
result.set(tag, tagData[tag] ?? null);
452456
}
453457

454-
getCloudflareContext().ctx.waitUntil(this.putToRegionalCache({ doId, tags: remainingTags }, stub));
458+
getCloudflareContext().ctx.waitUntil(
459+
this.putToRegionalCache({ doId, tags: remainingTags }, stub, tagData)
460+
);
455461
})
456462
);
457463

0 commit comments

Comments
 (0)