mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
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:
1 parent
4c86b68363
commit
83661319d2
7 files changed
+48
-36
No files matched your search
@@ -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";
|
||||
|
||||
@@ -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 }>;
|
||||
|
||||
Vendored
+23
@@ -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();
|
||||
}
|
||||
Vendored
+1
-24
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in new issue
Block a user