Repository navigation
feat(deploy): auto-drop large deployment payload blobs after a successful deploy - #1496
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements a payload reclamation feature that drops the deployment payload tarball for successful, large deployments to reclaim storage, while retaining audit metadata. It introduces a configurable threshold deployment_payloadRetention_maxSize (defaulting to 10 MiB) and adds comprehensive unit and integration tests. The review feedback suggests updating the integration test to pass the configuration value as a string to properly verify the coercion logic.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
Reviewed; no blockers found. |
Ethan-Arrowood
left a comment
There was a problem hiding this comment.
LGTM — this is a clean, well-reasoned fix for the payload accumulation problem.
What I checked and liked:
dropPayload()is tight: no-ops if finished or already blob-less, returns the freed size, and only mutates the in-memory record so the null folds intofinish()'s single terminal write — nicely avoids an extra put and the #1170 out-of-order-revert window.- The two retention guards are exactly the right call. Failed deploys never reach the branch, and gating on
getFailedPeers().length === 0means a deploy that only "succeeded" becauseignore_replication_errorsmasked a down peer keeps its payload for retry/inspection. getPayloadRetentionMaxSize()coercion is thorough — boolean/array/unset/blank fall back to the default, numeric strings are honored, explicit0means always-drop. Good defensive handling.- Great test coverage: the unit cases cover drop / no-op / unknown-size / post-finish / sealed-terminal-write, and the integration test forces the threshold to
'1'as a string (exercising the coercion path) and polls to terminal status instead of sleeping.
I'm on board with the cluster-wide reclaim-via-replication semantic — for the post-deploy case the payload's job is genuinely done everywhere, and keeping it on failed-peer deploys is the right conservative default. Scoping the disk-pressure hook and retroactive reclaim to Part 2 makes sense.
One non-blocking note: the two red integration shards (Bun 5/6, Node 24 6/6) are unrelated job-queue flakes — csv_file_load 120s job timeout, concurrent claim, Job queue lifecycle, none of which touch deployment-payload code. Worth a re-run to get a green board before merge, but not a code concern.
Approving — nice work.
sent with Claude Opus 4.8
…sful deploy Once a deploy completes successfully and every peer has installed from the replicated payload_blob, the tarball is no longer needed. For payloads above deployment_payloadRetention_maxSize (default 10 MiB) the origin drops the payload_blob as part of the recorder's terminal write: this unlinks the file locally and replicates the null so peers drop their copies too, reclaiming the bytes cluster-wide. Metadata (payload_size, payload_hash, event_log) is retained for the audit trail, and a payload_dropped event is recorded. The drop is gated on success and on no failed peers, so a deploy that only reached success because ignore_replication_errors masked a failed peer keeps its payload for inspection/retry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…test Cross-model review (Gemini) follow-ups: - getPayloadRetentionMaxSize now only accepts a number or numeric string; a boolean/array/blank value falls back to the default instead of Number() coercing true→1 or false/[]/""→0 (a near-/always-drop threshold). - Replace the fixed sleep(200) in deploy-payload-reclaim with a poll on get_deployment until the row reaches a terminal status. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ercion Exercises getPayloadRetentionMaxSize's string-coercion path end-to-end, the shape an env-var/string-sourced config override actually arrives in. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9787ce8 to
810cb13
Compare
Closes #1495 (part 1 of 2).
Summary
After a deploy succeeds and every peer has installed from the replicated
payload_blob, the upload tarball is no longer needed. For payloads larger thandeployment_payloadRetention_maxSize(new config param, default 10 MiB) the origin dropspayload_blobas part of theDeploymentRecorder's single terminal write. Because that null reference replicates, the blob is reclaimed cluster-wide — each node unlinks its local file via the existingRecordEncoderretained-blob check (the #641 path). Metadata (payload_size,payload_hash,event_log, status) is retained for the audit trail, and apayload_droppedevent is recorded.Purpose
We never actually reclaimed deploy payload blobs after replication — they accumulated on every node (observed: hundreds of orphaned payloads, multiple GB on a preprod cluster). This adds the threshold-based reclamation we'd intended.
Where to look / things to weigh
components/operations.js(success branch ofdeployComponent): the drop is gated onpayload_size > thresholdandgetFailedPeers().length === 0, so a deploy that only reached "success" becauseignore_replication_errorsmasked a failed peer keeps its payload for inspection/retry.0means "always drop"; non-numeric/blank/unset falls back to the default.DeploymentRecorder.dropPayload()only mutates the in-memory record so the null folds intofinish()'s terminal write (avoids an extra put and the Replication: out-of-order full update can revert a newer record (non-convergence) #1170 out-of-order-revert window).Open follow-up (Part 2)
Issue #1495 also asks for a disk-pressure storage-reclamation hook (evict oldest payloads when space is low). The framework already exists (
server/storageReclamation.tsonStorageReclamation); the eviction policy and the "one node's disk pressure reclaims audit blobs cluster-wide" semantics deserve their own PR + decision.Gemini review also flagged a related gap that Part 2 should cover: a deploy where a peer was offline/failed keeps its payload (correct — that peer may still need it), but there is no later trigger to reclaim it once the cluster converges. The conservative keep-on-failure here is deliberate for Part 1; eventual/retroactive reclamation belongs in the Part 2 hook.
Tests
DeploymentRecorder.dropPayload(drop / no-op / after-finish / metadata-retention / sealed-terminal-write).deploy-payload-reclaim.test.tsforces the threshold to 1 byte and assertspayload_blob_present === falsewith metadata +payload_droppedevent retained after a successful deploy (pollsget_deploymentto a terminal status — no fixed sleep). Retain-side (default threshold keeps small payloads) is covered by the existingdeploy-tracking.test.ts.Cross-model reviewed (Codex + Gemini). All actionable findings fixed: ignore-errors gate, blank/coerced-config zero-threshold, and the integration-test sleep flake. Declined with rationale:
record/rowrename (pre-existing class convention) and human-readable byte units (raw bytes matches the siblingoperationsApi_componentFile_maxSize).🤖 Generated by Claude Opus 4.8.