mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
refactor(measurements): drop the write-only aggregation columns (migration 0272)
Migration 0263 added three columns beside the Apple Health `stats:*` authority ladder. Only `aggregation_provenance` is ever read back: the external-measurement reconciler selects it and refuses a lower-authority write against a higher-authority canonical row, and the cumulative drain reads it too. That column stays. `aggregation_contributor_count` and `aggregation_selected_source_hash` are written by three call sites and read by none. No select, no raw SQL, no export, no backup, no API contract, no repair path. The import-time selection keeps its deterministic tiebreak, which compares an in-memory map key computed per flush rather than a value read back from the table. Both are plain nullable scalars with no index, unique, check or foreign key, so the drop is a catalog-only operation that touches no measured value.
This commit is contained in:
1 parent
a3f1d780e7
commit
f8f460bb66
9 files changed
+41
-48
No files matched your search
@@ -544,8 +544,6 @@ async function postBatch(request: NextRequest): Promise<Response> {
|
||||
.rhythmClassification as Prisma.MeasurementUncheckedCreateInput["rhythmClassification"],
|
||||
deviceType: p.row.deviceType as string | null,
|
||||
aggregationProvenance: statsRow ? "HEALTHKIT_STATISTICS" : null,
|
||||
aggregationContributorCount: null,
|
||||
aggregationSelectedSourceHash: null,
|
||||
},
|
||||
{ exactExternalMatch: statsRow ? "update" : "duplicate" },
|
||||
),
|
||||
|
||||
@@ -652,11 +652,7 @@ describe("streamParseExportXml — cumulative source-day estimates", () => {
|
||||
expect(prisma._measurements[0]).toMatchObject({
|
||||
value: 8_526,
|
||||
aggregationProvenance: "EXPORT_XML_SOURCE_MAX",
|
||||
aggregationContributorCount: 2,
|
||||
});
|
||||
expect(prisma._measurements[0]?.aggregationSelectedSourceHash).toMatch(
|
||||
/^[a-f0-9]{64}$/,
|
||||
);
|
||||
expect(result.cumulativeEstimates).toEqual({ days: 1, rows: 1 });
|
||||
});
|
||||
|
||||
@@ -675,7 +671,6 @@ describe("streamParseExportXml — cumulative source-day estimates", () => {
|
||||
type: mapping.measurementType,
|
||||
value: 30,
|
||||
aggregationProvenance: "EXPORT_XML_SOURCE_MAX",
|
||||
aggregationContributorCount: 2,
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -694,7 +689,6 @@ describe("streamParseExportXml — cumulative source-day estimates", () => {
|
||||
expect(prisma._measurements[0]).toMatchObject({
|
||||
type: mapping.measurementType,
|
||||
value: 50,
|
||||
aggregationContributorCount: 1,
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -712,7 +706,6 @@ describe("streamParseExportXml — cumulative source-day estimates", () => {
|
||||
expect(prisma._measurements).toHaveLength(1);
|
||||
expect(prisma._measurements[0]).toMatchObject({
|
||||
value: 100,
|
||||
aggregationContributorCount: 1,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1171,7 +1164,6 @@ describe("streamParseExportXml — memory ceiling", () => {
|
||||
expect(prisma._measurements).toHaveLength(1);
|
||||
expect(prisma._measurements[0]).toMatchObject({
|
||||
value: 10_000,
|
||||
aggregationContributorCount: 1,
|
||||
});
|
||||
|
||||
// Heap delta should be modest — we tolerate a generous 100 MB so a
|
||||
|
||||
@@ -40,8 +40,6 @@ function row(overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
||||
externalId: "sleep:new",
|
||||
externalSourceVersion: null,
|
||||
aggregationProvenance: null,
|
||||
aggregationContributorCount: null,
|
||||
aggregationSelectedSourceHash: null,
|
||||
glucoseContext: null,
|
||||
sleepStage: "DEEP",
|
||||
rhythmClassification: null,
|
||||
@@ -224,8 +222,6 @@ describe("reconcileExternalMeasurement", () => {
|
||||
sleepStage: null,
|
||||
externalId: "stats:HKQuantityTypeIdentifierStepCount:2026-07-20",
|
||||
aggregationProvenance: "HEALTHKIT_STATISTICS",
|
||||
aggregationContributorCount: null,
|
||||
aggregationSelectedSourceHash: null,
|
||||
});
|
||||
const { tx, measurement } = transaction([native]);
|
||||
|
||||
@@ -240,8 +236,6 @@ describe("reconcileExternalMeasurement", () => {
|
||||
sleepStage: null,
|
||||
externalId: "stats:HKQuantityTypeIdentifierStepCount:2026-07-20",
|
||||
aggregationProvenance: "EXPORT_XML_SOURCE_MAX",
|
||||
aggregationContributorCount: 2,
|
||||
aggregationSelectedSourceHash: "source-hash",
|
||||
}),
|
||||
{ exactExternalMatch: "update" },
|
||||
);
|
||||
@@ -350,8 +344,6 @@ describe("reconcileExternalMeasurement", () => {
|
||||
sleepStage: null,
|
||||
externalId: "stats:HKQuantityTypeIdentifierStepCount:2026-07-20",
|
||||
aggregationProvenance: "HEALTHKIT_STATISTICS",
|
||||
aggregationContributorCount: null,
|
||||
aggregationSelectedSourceHash: null,
|
||||
}),
|
||||
{ exactExternalMatch: "update" },
|
||||
);
|
||||
@@ -366,8 +358,6 @@ describe("reconcileExternalMeasurement", () => {
|
||||
data: expect.objectContaining({
|
||||
value: 8_600,
|
||||
aggregationProvenance: "HEALTHKIT_STATISTICS",
|
||||
aggregationContributorCount: null,
|
||||
aggregationSelectedSourceHash: null,
|
||||
syncVersion: { increment: 1 },
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -352,8 +352,6 @@ export async function drainPerSampleCumulative(
|
||||
externalId,
|
||||
deletedAt: null,
|
||||
aggregationProvenance: "LEGACY_UNKNOWN",
|
||||
aggregationContributorCount: null,
|
||||
aggregationSelectedSourceHash: null,
|
||||
syncVersion: { increment: 1 },
|
||||
...(slotIsFreeForTarget
|
||||
? { measuredAt: canonicalTimestamp }
|
||||
|
||||
@@ -680,7 +680,10 @@ export async function streamParseExportXml(
|
||||
}
|
||||
}
|
||||
if (!selected) continue;
|
||||
const [selectedSourceHash, selectedSubtotal] = selected;
|
||||
// Only the subtotal is carried forward. The source hash stays an
|
||||
// in-memory map key — it is what makes the max-subtotal selection
|
||||
// above deterministic on a tie, and it is not persisted.
|
||||
const [, selectedSubtotal] = selected;
|
||||
if (validateMeasurementRange(type, selectedSubtotal) !== null) {
|
||||
unknown[`${type}::aggregate_out_of_range`] =
|
||||
(unknown[`${type}::aggregate_out_of_range`] ?? 0) + 1;
|
||||
@@ -704,8 +707,6 @@ export async function streamParseExportXml(
|
||||
sleepStage: null,
|
||||
deviceType: null,
|
||||
aggregationProvenance: "EXPORT_XML_SOURCE_MAX",
|
||||
aggregationContributorCount: bySource.size,
|
||||
aggregationSelectedSourceHash: selectedSourceHash,
|
||||
},
|
||||
{ exactExternalMatch: "update" },
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user