Skip to content

feat: Time out S3 attempts individually and retry throttled requests longer - #117

Draft
Darkheir wants to merge 4 commits into
sekoiafrom
feat/s3-get-slice-attempt-timeout
Draft

Darkheir wants to merge 4 commits into
sekoiafrom
feat/s3-get-slice-attempt-timeout

Conversation

@Darkheir

@Darkheir Darkheir commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Tip

This PR can be reviewed commit by commit
Only the last commit changes the behavior

storage_timeout_policy used to time the whole get_slice call from outside the storage, including S3's own retries and backoff sleeps, so throttling (SlowDown) turned into split failures (internal timeout on get_slice).

S3 storages now apply the policy to each attempt (request and body) inside their retry loop: waiting between attempts is no longer counted, and timed-out attempts are retried at most max_num_retries times. Other storages keep the existing wrapper.

Throttled requests also get 2 extra attempts (THROTTLING_EXTRA_ATTEMPTS), spaced by the usual exponential backoff, to outlast short throttling bursts.

Throttling (SlowDown) on a small slice, by how long the bucket keeps rejecting the request:

Throttling lasts Before Now
< 0.9s ✅ succeeds ✅ succeeds
0.9–1.75s depends on jitter (attempt 4) ✅ succeeds
1.75–7.9s ❌ internal timeout on get_slice at 2s ✅ succeeds
7.9–15.75s ❌ depends on jitter (attempt 7)
> 15.75s ❌ ❌ fails with the SlowDown error; the root's split retry still applies

Other cases:

Case Before Now
Other transient error (e.g. InternalError 500) lasting 1.9–3.75s ❌ cut at 2s depends on jitter (5 attempts, the last at 1.88–3.75s)
Backoff sleep longer than the remaining budget ❌ cancelled while sleeping ✅ sleeps aren't counted
Large slice (20 MB, 22s budget) whose body breaks at 8s Retry must finish within the remaining 14s ✅ retry gets a fresh 22s
An attempt hangs, max_num_retries: 0 (prod) ❌ fails at 2s ❌ fails at 2s (unchanged)
An attempt hangs, max_num_retries: 1 Whole call restarted from zero ✅ only the attempt is retried, with a fresh budget

How was this PR tested?

Added tests

Darkheir and others added 3 commits October 23, 2026 16:13
The S3 assembly thread pool is a setting of the node building the storage
resolver, not of the storage configs. Move it into an `S3NodeOptions` struct
handed to `StorageResolver::configured_with_s3_node_options`, so other
node-level S3 settings can be added without a new resolver constructor.

No behavior change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The same transient/permanent classification of SDK errors was repeated
in the put, upload part and get paths. Move it to `into_retry`.

No behavior change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the body of the retried closure of `get_to_bytes`, the GetObject
request and the read of its body, to `get_to_bytes_attempt`.

No behavior change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Non-CLI initialization can lose configured S3 timeout protection, and SDK timeouts can be misreported as policy exhaustion.

2 open issues
What changed in this PR

This PR moves S3 slice timeouts inside the storage retry loop so backoff delays no longer consume the download deadline. Other storage backends retain the existing timeout wrapper.

Changes:

  • Apply a fresh timeout to each S3 slice attempt, including response-body reads.
  • Allow two extra attempts for throttling errors.
  • Pass node-specific S3 options through CLI resolver setup and add retry tests.
File Description
quickwit/​quickwit-storage/​src/​storage_resolver.rs Accept node-specific S3 options.
quickwit/​quickwit-storage/​src/​object_storage/​s3_compatible_storage.rs Implement per-attempt timeouts, metrics, and tests.
quickwit/​quickwit-storage/​src/​object_storage/​s3_compatible_storage_resolver.rs Define and apply S3 node options.
quickwit/​quickwit-storage/​src/​object_storage/​mod.rs Export S3 node options.
quickwit/​quickwit-storage/​src/​lib.rs Expose S3 node options publicly.
quickwit/​quickwit-search/​src/​leaf.rs Bypass the outer timeout wrapper for S3.
quickwit/​quickwit-config/​src/​node_config/​mod.rs Extract single-attempt timeout calculation and document behavior.
quickwit/​quickwit-common/​src/​retry.rs Add throttling classification, extra attempts, and tests.
quickwit/​quickwit-cli/​src/​tool.rs Update resolver options for CLI tools.
quickwit/​quickwit-cli/​src/​service.rs Pass S3 options during service startup.
quickwit/​quickwit-cli/​src/​lib.rs Derive S3 options from node configuration.
quickwit/​quickwit-aws/​src/​retry.rs Forward throttling classification through retry adapters.
quickwit/​quickwit-aws/​src/​error.rs Identify SDK throttling errors from metadata.

Review effort: Balanced

Comment thread quickwit/quickwit-search/src/leaf.rs Outdated
Comment thread quickwit/quickwit-storage/src/object_storage/s3_compatible_storage.rs Outdated
…longer

Signed-off-by: Darkheir <raphael.cohen@sekoia.io>
@Darkheir
Darkheir force-pushed the feat/s3-get-slice-attempt-timeout branch from 96683de to 9dcd303 Compare October 23, 2026 15:44
@Darkheir
Darkheir requested a balanced review from Copilot October 23, 2026 15:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-layer timeout ownership and shared AWS retry changes warrant runtime validation of cancellation and mixed-error exhaustion behavior before human approval.

No open issues 🎉

2 resolved since last review

Review effort: Balanced

@Darkheir Darkheir changed the title Feat/s3 get slice attempt timeout feat: Time out S3 attempts individually and retry throttled requests longer Oct 23, 2026
@Darkheir
Darkheir requested a review from rdettai-sk October 23, 2026 15:49

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants