Skip to content

fix(restore): retry ResourceExhausted during backup restore - #723

Open
ivanauth wants to merge 1 commit into
authzed:mainfrom
ivanauth:fix/restore-retry-resource-exhausted
Open

ivanauth wants to merge 1 commit into
authzed:mainfrom
ivanauth:fix/restore-retry-resource-exhausted

Conversation

@ivanauth

@ivanauth ivanauth commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

zed backup restore aborts the entire run when the server briefly cannot acquire a datastore write connection:

error finalizing write of 10 batches: rpc error: code = ResourceExhausted
desc = error acquiring connection from pool: failed to acquire in time

A restore aborts entirely when the server cannot acquire a datastore
write connection within its acquisition timeout, surfacing as:

    error finalizing write of 10 batches: rpc error: code = ResourceExhausted
    desc = error acquiring connection from pool: failed to acquire in time

The condition is transient, and zed already treats ResourceExhausted as
retryable for unary calls in DialOptsFromFlags. ImportBulkRelationships is
excluded from the stream retry interceptor because the interceptor cannot
transparently replay batches already sent on the stream, so the restore
performs its own retries -- but isRetryableError never classified this
code, sending it to the unrecoverable branch and ending the run.

Classify ResourceExhausted as retryable so the batch is retried through
writeBatchesWithRetry. This is safe: the acquisition failure happens before
BeginTx, so the failed attempt committed no rows. Callback errors are
rolled back, and the transaction does not partially commit.

gRPC reports oversized messages with the same code, which retrying cannot
resolve, so those remain unretryable. The exclusion covers the message-size
templates gRPC emits on the send side, the receive side, and after
decompression -- isRetryableError also drives the backup export loops,
where a permanent send-side size failure must not be retried.

Note this does not make restores unconditionally succeed. The manual loop
still stops after defaultMaxRetries retries, and --max-retries does not
configure that outer ceiling.

Signed-off-by: ivanauth <ivan@authzed.com>
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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