🦺 retry transient Google Sheets API errors - #30
Open
adaptcom[bot] wants to merge 1 commit into
Open
Conversation
A single 503 from the Sheets API killed the whole task: gcpde/sheets.py called gspread once with no backoff, so main_every_3h.collect_manual_fixes.collect_manual_references burned both tries on 2026-08-20 12:00 UTC. Wrap every leaf Sheets read call in a tenacity retry that backs off on 408/429/5xx and reraises client errors untouched.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by: Guilherme Amorim guilherme@wander.com
Created by: Adapt
Summary
main_every_3h.collect_manual_fixes.collect_manual_referencesfailed on both tries at 2026-08-20 12:00 UTC with:gcpde/sheets.pyhad no retry anywhere, so one transient 5xx on the spreadsheet-metadata call takes down the whole task. The DAGs run withretries: 1, so both tries burn inside the same outage window and the tables stay stale until the next 3-hourly run. The same 503 killedcollect_scraping.collect_price_match_sla_remove_liston both the 12:00 and 15:00 UTC runs the day before, so this is not specific to one pipeline: everyingest_sheet/ingest_all_tabspipeline inwandercom/data-pipelinessits on this code path.What changed
_call_api(operation)— a tenacity-wrapped leaf-call helper, mirroring the existing patterns ingcpde/bq.pyandgcpde/gcs.py. Exponential backoff (1s to 30s), 5 attempts,reraise=True, warning log between attempts._is_transient_api_errorretries onlygspread.exceptions.APIErrorwith status 408, 429, 500, 502, 503 or 504. Client errors (401/403/404, a revoked share or a deleted doc) still fail on the first attempt, so a real permission problem does not sit there backing off for 45 seconds.open_by_key,Spreadsheet.worksheet,Spreadsheet.worksheets(both in_get_worksheetsandlist_worksheets), andWorksheet.get_all_records.Two scope choices worth calling out in review:
_get_worksheetsandlist_worksheetsnow hold the spreadsheet in a local before the wrappedworksheets()call instead of chaining.replace_from_records,replace_or_create_from_records,delete_worksheet) are deliberately untouched. Retrying aclear()+update()pair mid-flight has different failure semantics than a read, and the incident is entirely on the read path. Happy to widen it if you would rather have it uniform.Test plan
make checks— ruff +mypy gcpde(strict) clean.make test— 77 passed. 8 new tests intests/unit/test_sheets.py: the transient/non-transient predicate matrix, retry-then-succeed, reraise after 5 attempts, no retry on 404, and_open_documentrecovering from a 503. Retry waits are neutralized withretry_with(wait=tenacity.wait_none())so the suite stays fast.gcpde/sheets.pyis 99%; the single uncovered line (worksheet.resizeinreplace_or_create_from_records) predates this branch.Notes
gcpderelease + bump inwandercom/data-pipelines.Note
Medium Risk
Changes how Sheets API failures are handled (retries, backoff, which status codes fail fast). Write paths are unchanged, so risk is mainly extra latency and possible nested-retry mistakes on the read path.
Overview
Retries transient Google Sheets API failures on the read path so a single 503/429 no longer takes down ingest jobs.
Adds
_call_api(tenacity, exponential backoff 1–30s, 5 attempts) and_is_transient_api_error, which retries onlyAPIErrorwith 408/429/5xx. Client errors (401/403/404) still fail immediately.Wraps leaf calls only:
open_by_key,worksheet,worksheets, andget_all_records. Nested wrappers are avoided so attempts do not multiply. Write helpers (replace_*,delete_worksheet) are left without extra retries. Unit tests cover the status-code matrix, retry-then-succeed, max attempts, and no retry on 404.Reviewed by Cursor Bugbot for commit 06b856c. Bugbot is set up for automated code reviews on this repo. Configure here.