perf : cache
CI / Verify (push) Successful in 1m56s
CI / Build immutable images and deploy (push) Successful in 3m12s

This commit is contained in:
2026-10-08 23:21:34 +07:00 Unverified
parent 36063fc214
commit 9580407a90
58 changed files with 5797 additions and 5336 deletions
+20
View File
@@ -0,0 +1,20 @@
import "server-only";
import { cacheLife, cacheTag } from "next/cache";
import { getActiveCatalog, getCatalogOptions } from "@/lib/guides/queries";
import { CATALOG_CACHE_TAG, GLOSSARY_CACHE_TAG } from "./tags";
export async function getCachedCatalogOptions() {
"use cache: remote";
cacheLife({ stale: 0, revalidate: 300, expire: 3600 });
cacheTag(CATALOG_CACHE_TAG, GLOSSARY_CACHE_TAG);
return getCatalogOptions();
}
export async function getCachedActiveCatalog() {
"use cache: remote";
cacheLife({ stale: 0, revalidate: 300, expire: 3600 });
cacheTag(CATALOG_CACHE_TAG, GLOSSARY_CACHE_TAG);
return getActiveCatalog();
}
+29
View File
@@ -0,0 +1,29 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({ tag: vi.fn(), guide: vi.fn(), summary: vi.fn() }));
vi.mock("server-only", () => ({}));
vi.mock("next/cache", () => ({ cacheTag: mocks.tag, cacheLife: vi.fn() }));
vi.mock("@/lib/guides/queries", () => ({
getPublicGuide: mocks.guide, getPublicGuideSummary: mocks.summary, listPublicGuideCards: vi.fn(),
}));
import { getCachedPublicGuide, getCachedPublicGuideSummary } from "./public";
beforeEach(() => vi.resetAllMocks());
describe("public cache dependency boundaries", () => {
it.each([getCachedPublicGuide, getCachedPublicGuideSummary])("keeps existing guides independent of directory invalidations", async (read) => {
mocks.guide.mockResolvedValue({ id: "first" });
mocks.summary.mockResolvedValue({ id: "first" });
await read("first");
expect(mocks.tag).toHaveBeenCalledWith("buzz:page:first");
expect(mocks.tag).toHaveBeenCalledWith("buzz:catalog");
expect(mocks.tag).not.toHaveBeenCalledWith("buzz:directory");
});
it.each([getCachedPublicGuide, getCachedPublicGuideSummary])("refreshes missing guides when the directory changes", async (read) => {
mocks.guide.mockResolvedValue(null);
mocks.summary.mockResolvedValue(null);
expect(await read("missing")).toBeNull();
expect(mocks.tag).toHaveBeenCalledWith("buzz:directory");
});
});
+6 -2
View File
@@ -4,6 +4,7 @@ import { cacheLife, cacheTag } from "next/cache";
import { getPublicGuide, getPublicGuideSummary, listPublicGuideCards } from "@/lib/guides/queries";
import {
CATALOG_CACHE_TAG,
DIRECTORY_CACHE_TAG,
GLOSSARY_CACHE_TAG,
pageCacheTag,
@@ -11,6 +12,7 @@ import {
export async function getCachedPublicGuideCards() {
"use cache: remote";
cacheTag(CATALOG_CACHE_TAG);
cacheLife({ stale: 30, revalidate: 60, expire: 60 * 60 });
cacheTag(DIRECTORY_CACHE_TAG);
cacheTag(GLOSSARY_CACHE_TAG);
@@ -19,19 +21,21 @@ export async function getCachedPublicGuideCards() {
export async function getCachedPublicGuide(slug: string, section = "overview") {
"use cache: remote";
cacheTag(CATALOG_CACHE_TAG);
cacheLife({ stale: 15, revalidate: 60, expire: 60 * 60 });
const result = await getPublicGuide(slug, section);
cacheTag(GLOSSARY_CACHE_TAG);
cacheTag(DIRECTORY_CACHE_TAG);
if (result) cacheTag(pageCacheTag(result.id));
else cacheTag(DIRECTORY_CACHE_TAG);
return result;
}
export async function getCachedPublicGuideSummary(slug: string) {
"use cache: remote";
cacheTag(CATALOG_CACHE_TAG);
cacheLife({ stale: 15, revalidate: 60, expire: 60 * 60 });
cacheTag(DIRECTORY_CACHE_TAG);
const result = await getPublicGuideSummary(slug);
if (result) cacheTag(pageCacheTag(result.id));
else cacheTag(DIRECTORY_CACHE_TAG);
return result;
}
+4
View File
@@ -2,6 +2,8 @@ import { describe, expect, it } from "vitest";
import {
cacheTagsForOutboxEvent,
CATALOG_CACHE_TAG,
GLOSSARY_CACHE_TAG,
DIRECTORY_CACHE_TAG,
STYGIAN_CACHE_TAG,
} from "./tags";
@@ -11,6 +13,8 @@ describe("public cache tags", () => {
expect(cacheTagsForOutboxEvent({ eventType: "directory.updated", aggregateId: "id" })).toEqual([DIRECTORY_CACHE_TAG]);
expect(cacheTagsForOutboxEvent({ eventType: "page.updated", aggregateId: "page" })).toEqual(["buzz:page:page"]);
expect(cacheTagsForOutboxEvent({ eventType: "stygian.updated", aggregateId: "5269001" })).toEqual([STYGIAN_CACHE_TAG]);
expect(cacheTagsForOutboxEvent({ eventType: "catalog.updated", aggregateId: "catalog" })).toEqual([CATALOG_CACHE_TAG, DIRECTORY_CACHE_TAG, STYGIAN_CACHE_TAG]);
expect(cacheTagsForOutboxEvent({ eventType: "glossary.updated", aggregateId: "alias" })).toEqual([GLOSSARY_CACHE_TAG]);
expect(cacheTagsForOutboxEvent({ eventType: "admin.updated", aggregateId: "id" })).toEqual([]);
});
});
+3
View File
@@ -1,3 +1,4 @@
export const CATALOG_CACHE_TAG = "buzz:catalog";
export const DIRECTORY_CACHE_TAG = "buzz:directory";
export const GLOSSARY_CACHE_TAG = "buzz:glossary";
export const STYGIAN_CACHE_TAG = "buzz:stygian";
@@ -12,6 +13,8 @@ export type OutboxCacheEvent = {
};
export function cacheTagsForOutboxEvent(event: OutboxCacheEvent): string[] {
if (event.eventType === "catalog.updated") return [CATALOG_CACHE_TAG, DIRECTORY_CACHE_TAG, STYGIAN_CACHE_TAG];
if (event.eventType === "glossary.updated") return [GLOSSARY_CACHE_TAG];
if (event.eventType === "directory.updated") return [DIRECTORY_CACHE_TAG];
if (event.eventType === "page.updated") return [pageCacheTag(event.aggregateId)];
if (event.eventType === "stygian.updated") return [STYGIAN_CACHE_TAG];