chore(backup): drop the whole-string builder the streaming path replaced

Nothing calls it any more: the weekly pass and the off-host job both
walk the payload through the streaming writer. Keeping an exported
function that builds the entire record as one string invites the next
caller to reintroduce the memory profile this branch removes.
This commit is contained in:
Marc-André Bombeck committed 2026-09-05 20:23:08 +02:00
1 parent 13a9aacf20
commit 3ee3461f92
3 files changed
+2 -39

No files matched your search

-37
View File
@@ -661,43 +661,6 @@ export function isDeferredRows(value: unknown): value is DeferredRows {
(value as Record<symbol, unknown>)[DEFERRED_ROWS] === true
);
}
/**
* The same payload, already serialised, with the object graph released.
*
* Built through the streaming writer rather than by stringifying a finished
* payload, so the object graph and the string never coexist: the three
* unbounded tables go past a page at a time and every other section is
* released as soon as its JSON exists. What is left is the answer itself,
* which a caller that wants a string necessarily holds.
*
* A caller that does NOT need the string — the weekly job, which only wants
* the compressed ciphertext — should go through `packBackupBlobStreaming` and
* `streamFullBackupJson` directly and never let the whole document exist at
* all. This function is for the callers that genuinely hand a JSON body on.
*/
export async function buildFullBackupJson(
prisma: PrismaClient,
userId: string,
options: FullBackupOptions = {},
): Promise<string> {
// Imported here rather than at the top of the file: the writer imports this
// module for `buildFullBackupPayload`, and a static edge in both directions
// is a cycle. The call is once per backup, so the cost is nil.
const { streamFullBackupJson } =
await import("@/lib/export/full-backup-stream");
const pieces: string[] = [];
await streamFullBackupJson(
prisma,
userId,
(chunk) => {
pieces.push(chunk);
},
options,
);
return pieces.join("");
}
/**
* Build the canonical full-backup payload for `userId`. Portable exports omit
* tombstones and document ciphertext; disaster-recovery payloads preserve
+1 -1
View File
@@ -1,7 +1,7 @@
/**
* The full-backup payload, written out incrementally instead of built.
*
* Why it exists. `buildFullBackupJson` has to hold two things at once that
* Why it exists. the old whole-string builder (removed with the streaming path) had to hold two things at once that
* both scale with the record: the payload object graph, and the JSON string
* `JSON.stringify` makes of it. On a seeded account of 445 000 measurements,
* 30 000 mood entries and 60 000 intake events, that pair alone exhausts a
@@ -289,7 +289,7 @@ describe("off-host backup under a memory budget", () => {
it("would not fit that budget if the object were materialised", async () => {
const baseline = liveHeapBytes();
// Exactly what the job did before, with the step `buildFullBackupJson`
// Exactly what the job did before, with the step the old whole-string builder took
// takes internally spelled out: the payload graph, the JSON string it is
// stringified into, then a whole-buffer gzip-and-encrypt pass, then a
// single put of the finished buffer.