Repository navigation
Conversation
Every BlobStore::s3 built a new reqwest client, so each get paid a fresh TCP and TLS handshake. On 2026-10-23 those handshakes were the transport: successful s3 gets were idle for nearly their whole duration, p95 about 1.5s with a 2.5s tail, while semaphore wait stayed around 5ms. Gets still idle at about 8s were cancelled before they recorded an outcome, and the File response failed. Share one process-wide client (pool idle 45s, connect timeout 2s) and retry a buffered GET once inside a 7s budget. HTTP statuses are not retried. Puts and streams share the client without the retry.
| retryable: false, | ||
| transport: BlobTransportError::message(message), | ||
| })?; | ||
| let mut request = self.http.client().get(&url).timeout(timeout); |
There was a problem hiding this comment.
read_object_once applies the three-second timeout to the entire download, not just a stalled connection. File $value reads use this buffered path. A healthy transfer of 20 MB at 5 MB/s is therefore cancelled on both attempts, failing a read that previously had a five-minute budget. Bound connection or idle waits separately, or preserve a longer budget for downloading the body.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/temper-server/src/blob_store/s3_transport.rs
Line: 156
Comment:
**Healthy downloads time out**
`read_object_once` applies the three-second timeout to the entire download, not just a stalled connection. File `$value` reads use this buffered path. A healthy transfer of 20 MB at 5 MB/s is therefore cancelled on both attempts, failing a read that previously had a five-minute budget. Bound connection or idle waits separately, or preserve a longer budget for downloading the body.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| let client = reqwest::Client::builder() | ||
| .connect_timeout(attempt_timeout) | ||
| .pool_max_idle_per_host(0) |
There was a problem hiding this comment.
Both new tests use S3Http::for_test, which creates a separate client and disables connection pooling. They can stay green if production stores start creating separate clients again, bringing back the repeated handshakes this PR fixes. Add a test that reads through two separately constructed BlobStore::s3 instances at the same endpoint and checks that they reuse one TCP connection.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/temper-server/src/blob_store/s3_transport.rs
Line: 65-67
Comment:
**Connection reuse is untested**
Both new tests use `S3Http::for_test`, which creates a separate client and disables connection pooling. They can stay green if production stores start creating separate clients again, bringing back the repeated handshakes this PR fixes. Add a test that reads through two separately constructed `BlobStore::s3` instances at the same endpoint and checks that they reuse one TCP connection.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Why
On 2026-10-23, TemperPaw S3
getcalls were slow but still succeeding, and every Temper blob-transport monitor flapped all day. Some File$valuereads failed outright.Root cause
Every
BlobStore::s3built its ownreqwest::Client, so each get paid for a new TCP and TLS handshake. That client had only a 10s connect timeout, no connection pool and no retry.Change
Tests
cargo test -p temper-server --lib blob_store::: 18 passed. Two of them are new:Note
The five Katagami
read_app502s between 6:34 and 6:49 PM ET are a separate failure. They come fromfuel_exhaustedinwasm.invoke.runduringgit-upload-pack, not from blob transport, and this PR doesn't fix them.Fix the timeout for healthy, longer downloads before merging.
Fix with agent prompt
Summary
Shares one S3 HTTP client across blob stores and adds a bounded retry for buffered GETs.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Buffered blob GET] --> B[Shared S3 client] B --> C[Attempt: three seconds including body] C --> D{Result} D -->|Success or 404| E[Return result] D -->|Other HTTP status| F[Return error] D -->|Retryable transport error| G{Attempt remaining?} G -->|Yes, within budget| C G -->|No| FReviews (1) · Last reviewed commit: "fix(server): reuse the S3 client and ret..." · Reviewed by Greptile