One Large PUT, Fully Traced
handle_put first parses metadata, checksum expectations, and encryption
parameters. It converts the Hyper body into a stream and delegates to
save_stream. The handler does not buffer the whole object.
save_stream reads the first block while fetching existing object metadata.
If the body fits below the inline threshold, Garage encrypts it and stores it
inside the object table. A large object takes the more interesting path.
Before transferring bytes, Garage writes an Uploading object version and an
empty version-table entry. These are durable breadcrumbs: later block
references always point at a known version, and interrupted work can be found
and cleaned.
The body then passes through four cooperating futures:
HTTP body → fixed-size chunks → checksums → encrypt + content hash → replica writes
channel(2) channel(1) channel(1)
Hashing and encryption use spawn_blocking; bounded Tokio channels keep those
CPU stages from running arbitrarily far ahead of network writes. The final
stage also caps the number of concurrent block writes for this request.
For every block, put_block_and_meta concurrently performs three obligations:
- replicate immutable block bytes to their placement set;
- add the block and offset to the version table;
- add a reference from the version to the block hash.
Only after all blocks finish, checksums and quotas pass, and metadata is ready
does Garage merge a Complete object version. A Drop guard remains armed
until that point. On error or cancellation it schedules cleanup for the
incomplete upload.
The HTTP response therefore means more than “the body was received.” It means the final object metadata reached its configured write quorum after the byte and reference obligations completed.