Repository navigation
Preflight all externalized payload sizes before starting uploads #820
Description
Externalized payload sizes are currently validated inside individual upload operations. A multi-field message can therefore upload valid earlier fields before a later field is rejected for a size that was already known before any I/O. If the response is then replaced by a failed completion, the uploaded references can be discarded, leaving blobs the backend never recorded.
Current source evidence
Inspected main at 9fc9b6c5ec9ca8317c31294e831c4e8533ff46d3.
ExternalizeOrchestratorResponseAsynccalculates eligible field sizes and queues individual operations, including custom status before actions.ExternalizePayloadAsyncchecksMaxPayloadBytesonly when the individual operation runs, immediately before calling the store.- The permanent-failure handler clears actions/custom status and creates a failed completion. It does not roll back earlier uploads.
This is a storage-side-effect issue, not a claim that the original business request partially commits. When a successful upload's reference is never submitted to or recorded by the backend, tombstone-based auto-purge has no recorded reference from which to discover that blob.
Focused regression recipe
In the existing AzureBlobPayloadsSideCarInterceptorTests harness:
TrackingPayloadStore store = new();
LargePayloadStorageOptions options = new()
{
ThresholdBytes = 1024,
MaxPayloadBytes = 2048,
};
AzureBlobPayloadsSideCarInterceptor interceptor = new(store, options);
P.OrchestratorResponse response = new()
{
InstanceId = "size-preflight-repro",
CustomStatus = new string('s', 1024),
};
response.Actions.Add(new P.OrchestratorAction
{
Id = 1,
ScheduleTask = new P.ScheduleTaskAction
{
Name = "activity",
Input = new string('x', 2049),
},
});
await ExternalizeAsync(interceptor, response, CancellationToken.None);
store.UploadCount.Should().Be(0);
response.Actions.Should().ContainSingle();
response.Actions[0].CompleteOrchestration.OrchestrationStatus
.Should().Be(P.OrchestrationStatus.Failed);A store that completes the valid first upload immediately demonstrates the current sequencing: custom status is uploaded, the later action fails its local size check, and failure conversion removes the uploaded token from the returned response. Cover an entity batch with a valid state followed by an oversized operation as well: it should reject without uploading or mutating any payload field.
Evidence boundary: this is source-confirmed missing preflight in the current .NET implementation. The .NET recipe above has not been run for this report. The equivalent JavaScript scenarios were actually reproduced with an in-memory store, then fixed with zero-upload regression assertions.
Acceptance Criteria
- Snapshot eligible nonempty fields and their UTF-8 byte sizes, then validate every eligible size before starting any upload.
- On a deterministic maximum-size violation, perform zero store uploads and no payload-token substitutions for the entire message.
- Keep the maximum scoped to fields selected for externalization; preserve inclusive thresholds, defaults, and inline/empty/absent behavior.
- Keep validation within the existing failure-conversion boundary: client/entity requests propagate the size error, and activity/orchestrator responses retain the appropriate non-retriable failure representation.
- For eligible work, honor precancellation before deterministic size validation; preserve canceled no-op behavior.
- Once size validation passes, retain bounded concurrency, drainage, and message-order selection of actual I/O failures.
- Add regressions for valid-first/oversized-later orchestration and entity messages, cancellation, and all-valid messages.
- Document that deterministic size validation now precedes hypothetical earlier upload failures because no I/O starts.
Non-goals
This does not provide a cross-blob transaction, recover existing orphan blobs, or guarantee zero orphan uploads after real network failures, cancellation during I/O, or uncertain backend submission. Do not add unconditional catch-and-delete rollback: an uncertain submission is not proof the backend never accepted a reference.
Related
- Equivalent JavaScript fix: preflight payload sizes before uploads.
- Existing .NET payload-failure work in PR 816 has related response preparation, but its inspected head
f9baad3still lacks a message-wide zero-upload size-preflight pass.
Activity