mirror of
https://github.com/gadgethd/ukmesh.git
synced 2026-10-23 14:22:47 +00:00
fix: serve charts during generation refresh
This commit is contained in:
1 parent
61d3466a4a
commit
4933928cf3
4 files changed
+30
-16
No files matched your search
@@ -72,7 +72,8 @@ test('chart snapshot store pins the schema version and refuses older overwrites'
|
||||
|
||||
const loaded = await loadStoredChartSnapshot(query, 'ukmesh', 7);
|
||||
assert.equal(loaded?.schema_version, CHART_SNAPSHOT_SCHEMA_VERSION);
|
||||
assert.deepEqual(calls[0]?.params, ['ukmesh', CHART_SNAPSHOT_SCHEMA_VERSION, 7]);
|
||||
assert.deepEqual(calls[0]?.params, ['ukmesh', CHART_SNAPSHOT_SCHEMA_VERSION]);
|
||||
assert.doesNotMatch(calls[0]?.text ?? '', /visibility_generation = \$3/);
|
||||
|
||||
assert.equal(
|
||||
await saveStoredChartSnapshot(query, 'ukmesh', payload(), 7, 6 * 60 * 60_000, NOW),
|
||||
|
||||
@@ -91,12 +91,11 @@ export async function loadStoredChartSnapshot(
|
||||
}
|
||||
const result = await query<StoredChartSnapshotRow>(
|
||||
`SELECT scope_key, schema_version, visibility_generation, generated_at, payload
|
||||
FROM stats_chart_snapshots
|
||||
FROM stats_chart_snapshots
|
||||
WHERE scope_key = $1
|
||||
AND schema_version = $2
|
||||
AND visibility_generation = $3
|
||||
LIMIT 1`,
|
||||
[scope, CHART_SNAPSHOT_SCHEMA_VERSION, visibilityGeneration],
|
||||
[scope, CHART_SNAPSHOT_SCHEMA_VERSION],
|
||||
);
|
||||
return result.rows[0] ?? null;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ test('a valid durable chart snapshot serves a cold process without analytical qu
|
||||
assert.equal(saves, 0);
|
||||
});
|
||||
|
||||
test('chart snapshots from an older privacy generation are never served or republished', async () => {
|
||||
test('a complete older-generation snapshot is served while one scope refresh runs', async () => {
|
||||
const generatedAt = new Date(Date.now() - 60_000).toISOString();
|
||||
const oldPayload = {
|
||||
snapshot: {
|
||||
@@ -150,6 +150,10 @@ test('chart snapshots from an older privacy generation are never served or repub
|
||||
},
|
||||
marker: 'old-generation',
|
||||
};
|
||||
let resolveRefresh!: (value: ReturnType<typeof emptyChartsData>) => void;
|
||||
const refresh = new Promise<ReturnType<typeof emptyChartsData>>((resolve) => {
|
||||
resolveRefresh = resolve;
|
||||
});
|
||||
let chartCalls = 0;
|
||||
const repository = {
|
||||
loadChartSnapshot: async () => ({
|
||||
@@ -162,7 +166,7 @@ test('chart snapshots from an older privacy generation are never served or repub
|
||||
saveChartSnapshot: async () => true,
|
||||
fetchChartsData: async () => {
|
||||
chartCalls += 1;
|
||||
return emptyChartsData();
|
||||
return refresh;
|
||||
},
|
||||
fetchChannelTraffic: async () => ({ rows: [] }),
|
||||
} as unknown as StatsRepository;
|
||||
@@ -178,13 +182,16 @@ test('chart snapshots from an older privacy generation are never served or repub
|
||||
maskDecodedPathNodes: () => [],
|
||||
});
|
||||
|
||||
const result = await service.getCharts('ukmesh', undefined) as {
|
||||
const first = await service.getCharts('ukmesh', undefined) as {
|
||||
snapshot: { visibilityGeneration: number };
|
||||
marker?: string;
|
||||
};
|
||||
const second = await service.getCharts('ukmesh', undefined) as typeof first;
|
||||
assert.equal(chartCalls, 1);
|
||||
assert.equal(result.snapshot.visibilityGeneration, 2);
|
||||
assert.equal(result.marker, undefined);
|
||||
assert.equal(first.snapshot.visibilityGeneration, 1);
|
||||
assert.equal(first.marker, 'old-generation');
|
||||
assert.deepEqual(second, first);
|
||||
resolveRefresh(emptyChartsData());
|
||||
});
|
||||
|
||||
test('a visibility change at chart publication fails closed without caching the result', async () => {
|
||||
@@ -267,7 +274,7 @@ test('expired persisted canonical charts are served while one refresh runs in th
|
||||
assert.equal(chartCalls, 1);
|
||||
|
||||
resolveRefresh(emptyChartsData());
|
||||
await chartsInflight.get('ukmesh:v1');
|
||||
await chartsInflight.get('ukmesh');
|
||||
assert.notEqual(chartsCache.get('ukmesh:v1')?.data, stale);
|
||||
chartsCache.shutdown();
|
||||
});
|
||||
|
||||
@@ -463,7 +463,6 @@ export function createStatsService(deps: StatsServiceDeps) {
|
||||
if (
|
||||
!row
|
||||
|| row.scope_key !== scope
|
||||
|| Number(row.visibility_generation) !== visibilityGeneration
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -472,7 +471,7 @@ export function createStatsService(deps: StatsServiceDeps) {
|
||||
scope,
|
||||
chartsSnapshotStaleTtlMs,
|
||||
Date.now(),
|
||||
visibilityGeneration,
|
||||
undefined,
|
||||
{ allowExpired: true },
|
||||
);
|
||||
const storedGeneratedAtMs = new Date(row.generated_at).getTime();
|
||||
@@ -508,7 +507,7 @@ export function createStatsService(deps: StatsServiceDeps) {
|
||||
key: string,
|
||||
visibilityGeneration: number,
|
||||
): Promise<unknown> {
|
||||
const existing = chartsInflight.get(key);
|
||||
const existing = chartsInflight.get(scope);
|
||||
if (existing) return existing;
|
||||
if (chartsInflight.size >= MAX_UNIQUE_STATS_INFLIGHT) {
|
||||
throw new StatsWorkOverloadedError();
|
||||
@@ -556,9 +555,9 @@ export function createStatsService(deps: StatsServiceDeps) {
|
||||
chartsCache.set(key, { ts: validated.generatedAtMs, data: validated.payload });
|
||||
return validated.payload;
|
||||
})().finally(() => {
|
||||
if (chartsInflight.get(key) === refresh) chartsInflight.delete(key);
|
||||
if (chartsInflight.get(scope) === refresh) chartsInflight.delete(scope);
|
||||
});
|
||||
chartsInflight.set(key, refresh);
|
||||
chartsInflight.set(scope, refresh);
|
||||
return refresh;
|
||||
}
|
||||
|
||||
@@ -587,7 +586,15 @@ export function createStatsService(deps: StatsServiceDeps) {
|
||||
// background, while observer-scoped data never crosses this boundary.
|
||||
cached = await loadPersistedCharts(scope, key, visibilityGeneration);
|
||||
}
|
||||
if (cached && Date.now() - cached.ts <= chartsSnapshotStaleTtlMs) {
|
||||
const cachedVisibilityGeneration = Number(
|
||||
(cached?.data as { snapshot?: { visibilityGeneration?: unknown } } | undefined)
|
||||
?.snapshot?.visibilityGeneration,
|
||||
);
|
||||
if (
|
||||
cached
|
||||
&& cachedVisibilityGeneration === visibilityGeneration
|
||||
&& Date.now() - cached.ts <= chartsSnapshotStaleTtlMs
|
||||
) {
|
||||
// A cold process must treat the persisted snapshot's durable max-age as
|
||||
// authoritative. The shorter in-memory cadence is only how often we
|
||||
// check freshness; it must not force a multi-minute rebuild after every
|
||||
|
||||
Reference in new issue
Block a user