refactor(cache): keep the closed-block key helpers in a leaf module

Readers that tag a cell as a closed block no longer import the
invalidation module, which several tests replace wholesale. The cache
route test counts the slice's pre-fold cell apart from the route's two
cells, and the provenance test reads the shared pick's ORDER BY over its
grouped rows.
This commit is contained in:
Marc-André Bombeck committed 2026-10-22 09:09:44 +02:00
1 parent 4c86b68363
commit 83661319d2
7 files changed
+48 -36

No files matched your search

+1 -1
View File
@@ -28,7 +28,7 @@
import { createHash } from "node:crypto";
import { Prisma } from "@/generated/prisma/client";
import { closedBlockKeyPart } from "@/lib/cache/invalidate";
import { closedBlockKeyPart } from "@/lib/cache/closed-block";
import { caches, type ServerCache } from "@/lib/cache/server-cache";
import { prisma } from "@/lib/db";
import { dayWeightedRowsSql, foldMeanSql } from "@/lib/measurements/day-mean";
+2 -5
View File
@@ -1,10 +1,7 @@
import { beforeEach, describe, expect, it } from "vitest";
import {
closedBlockKeyPart,
closedBlockUntouched,
invalidateUserMeasurements,
} from "../invalidate";
import { closedBlockKeyPart, closedBlockUntouched } from "../closed-block";
import { invalidateUserMeasurements } from "../invalidate";
import { caches, type ServerCache } from "../server-cache";
const analytics = caches.analytics as ServerCache<{ v: number }>;
+23
View File
@@ -0,0 +1,23 @@
/**
* The key part that marks an analytics entry as a closed block: a value that
* depends only on readings measured before `before`. Such a block is the
* same however many syncs land after it, as long as none of them touches a
* reading that old, so a measurement write that says how far back it
* reached (`touchedFrom`) leaves it in place. Every other invalidation, and
* every write that does not say, drops it as before.
*/
export function closedBlockKeyPart(before: Date | number): string {
const ms = typeof before === "number" ? before : before.getTime();
return `before:${ms}`;
}
const CLOSED_BLOCK_RE = /\|before:(\d+)(?:\||$)/;
/**
* Whether `key` is a closed block that a write reaching back to
* `touchedFrom` cannot have changed. Exported for its test.
*/
export function closedBlockUntouched(key: string, touchedFrom: Date): boolean {
const match = CLOSED_BLOCK_RE.exec(key);
return match !== null && Number(match[1]) <= touchedFrom.getTime();
}
+1 -24
View File
@@ -20,6 +20,7 @@
import { forgetCoverageProbe } from "@/lib/rollups/coverage-inflight";
import { closedBlockUntouched } from "./closed-block";
import { caches } from "./server-cache";
/**
@@ -48,30 +49,6 @@ export function invalidateUserDashboardSnapshot(userId: string): void {
caches.analytics.deleteByPrefix(dashboardSnapshotCacheKey(userId));
}
/**
* The key part that marks an analytics entry as a closed block: a value that
* depends only on readings measured before `before`. Such a block is the
* same however many syncs land after it, as long as none of them touches a
* reading that old, so a measurement write that says how far back it
* reached (`touchedFrom`) leaves it in place. Every other invalidation, and
* every write that does not say, drops it as before.
*/
export function closedBlockKeyPart(before: Date | number): string {
const ms = typeof before === "number" ? before : before.getTime();
return `before:${ms}`;
}
const CLOSED_BLOCK_RE = /\|before:(\d+)(?:\||$)/;
/**
* Whether `key` is a closed block that a write reaching back to
* `touchedFrom` cannot have changed. Exported for its test.
*/
export function closedBlockUntouched(key: string, touchedFrom: Date): boolean {
const match = CLOSED_BLOCK_RE.exec(key);
return match !== null && Number(match[1]) <= touchedFrom.getTime();
}
/**
* Invalidate every cache that may reflect a user's measurement set.
*
+1 -1
View File
@@ -31,7 +31,7 @@ import type { MeasurementType } from "@/generated/prisma/enums";
import type { DayNotable } from "@/lib/day/contract";
import { readLocalDailyStats, type DailyStats } from "@/lib/day/daily-stats";
import { closedBlockKeyPart } from "@/lib/cache/invalidate";
import { closedBlockKeyPart } from "@/lib/cache/closed-block";
import { caches, type ServerCache } from "@/lib/cache/server-cache";
import { prisma } from "@/lib/db";
import { startOfLocalDayKey } from "@/lib/tz/local-day";
@@ -115,10 +115,10 @@ describe("the provenance query orders like the canonical pick", () => {
`ORDER BY date_trunc('day', "measured_at"), (${rank}), "source"::text`,
),
);
// The shared pick groups to (type, day, source) first and orders the
// groups by the same keys, the UTC day named `d`.
expect(squash(canonicalMeasurementsCte(rank))).toContain(
squash(
`ORDER BY "type", date_trunc('day', "measured_at"), (${rank}), "source"::text`,
),
squash(`ORDER BY "type", d, (${rank}), "source"::text`),
);
// Values are bound, never spliced.
expect(userId).toBe("u1");
+17 -2
View File
@@ -117,8 +117,21 @@ function countBuildsPerKey(cache: {
return builds;
}
/**
* The slice's pre-fold remainder is a cell of its own in the same bucket (a
* closed block, `pre-fold-remainder.ts`), built when the slice takes the
* rollup path; it is counted apart from the route's two cells.
*/
function preFoldBuildsOf(builds: Map<string, number>, userId: string) {
return [...builds.entries()]
.filter(([k]) => k.startsWith(`${userId}|pre-fold-remainder|`))
.reduce((n, [, c]) => n + c, 0);
}
function buildsOf(builds: Map<string, number>, userId: string) {
const entries = [...builds.entries()].filter(([k]) => k.startsWith(userId));
const entries = [...builds.entries()].filter(
([k]) => k.startsWith(userId) && !k.includes("|pre-fold-remainder|"),
);
return {
defaultBody: entries
.filter(([k]) => k.startsWith(`${userId}|default|`))
@@ -230,7 +243,9 @@ describe("v1.4.34 IW-G — server-cache route integration", () => {
summaries: 2,
keys: 2,
});
expect(caches.analytics.stats().misses).toBe(4);
expect(caches.analytics.stats().misses).toBe(
4 + preFoldBuildsOf(builds, user.id),
);
// Sanity: the body reflects the new write.
const body = (await third.json()) as {