From 38855af45d8d40758932092bd90b33e5ccbfc88b Mon Sep 17 00:00:00 2001 From: Dominic Ferrando Date: Sat, 18 Jul 2026 01:37:31 -0400 Subject: [PATCH] Improve calculator selected standards config cache coherency --- apps/api/src/services/calculator.ts | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/apps/api/src/services/calculator.ts b/apps/api/src/services/calculator.ts index 7d31db4..860ed40 100644 --- a/apps/api/src/services/calculator.ts +++ b/apps/api/src/services/calculator.ts @@ -49,19 +49,19 @@ class CalculatorStandardsService { class CalculatorStandardsConfigService { private cache = { - refreshInterval: 30_000, - lastRefreshedAt: 0, - isStale: function () { - return Date.now() - this.lastRefreshedAt >= this.refreshInterval; - }, - isIncomplete: function () { - return !this.data.id || !this.data.datasetId || !this.data.config - }, data: { id: null as string | null, datasetId: null as string | null, config: null as StandardsConfig | null }, + isStale: async () => { + const { standards_config_id: selectedConfigId } = await db.selectFrom("calculators") + .select("standards_config_id") + .where("name", "=", this.name) + .executeTakeFirstOrThrow(); + return selectedConfigId !== this.cache.data.id; + }, + isComplete: () => Object.values(this.cache.data).every((v) => v !== null), } constructor(private name: string) { } @@ -73,9 +73,10 @@ class CalculatorStandardsConfigService { } async refresh(opt: { onlyIfStale?: boolean } = {}): Promise { - if (!this.cache.isIncomplete() && (opt.onlyIfStale && !this.cache.isStale())) return; - try { + const cacheIsCoherent = this.cache.isComplete() && (opt.onlyIfStale && !(await this.cache.isStale())); + if (cacheIsCoherent) return; + const result = await db.selectFrom("calculators") .innerJoin("standards_configs", "standards_configs.id", "calculators.standards_config_id") .innerJoin("standards_datasets", "standards_datasets.id", "standards_configs.dataset_id") @@ -97,9 +98,6 @@ class CalculatorStandardsConfigService { data: result.data, params: result.params }; - // only mark as refreshed once we've actually confirmed the selected - // config; a failed attempt must not make stale data look fresh - this.cache.lastRefreshedAt = Date.now(); } catch (err) { throw new CalculatorUnavailableError("Unable to determine the calculator's standards configuration", { cause: err }); }