Skip to content

Retry transient errors in BigqueryClient.queryUnflattened - #40793

Open
mxtymoshyk wants to merge 2 commits into
apache:masterfrom
mxtymoshyk:bq-test-client-query-results-retry
Open

mxtymoshyk wants to merge 2 commits into
apache:masterfrom
mxtymoshyk:bq-test-client-query-results-retry

Conversation

@mxtymoshyk

@mxtymoshyk mxtymoshyk commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #40792.

Many BigQuery integration tests check their output with BigqueryClient.queryUnflattened. It started one query job and polled jobs.getQueryResults with no retry, so a single transient 503 backendError failed a test after its pipeline had already succeeded. This happened in StorageApiDataTriggeredSchemaUpdateIT on #40790.

The 503 there read "Error encountered during execution", which means the query job itself failed. Polling that job again returns the same error, so the retry has to start a new job.

  • queryUnflattened now runs the query through runQueryJobWithRetries. On a transient error from jobs.insert or jobs.getQueryResults, it starts a new job, with the same backoff the other BigqueryClient methods use.
  • Transient means a 5xx, a 429, a 403 rateLimitExceeded (checked with ApiErrorExtractor, as BigQueryServicesImpl does) or a failure with no HTTP response. Other 4xx errors, such as an invalid query, are thrown right away.
  • The temp table uses WRITE_TRUNCATE, so a retried job overwrites anything a failed job wrote.
  • The temporary dataset is deleted in a finally block. Before, the method skipped the delete when the query returned no rows or threw.

BigqueryClientTest has 6 new tests: a new job after a 503 from polling, a retry after a 503 from jobs.insert, a retry after a 403 rateLimitExceeded, no retry on a 400 or a non-JSON 404, and cleanup when the query returns no rows. 5 of them fail on master. The non-JSON 404 test passes on master too, because master never retries, and it guards the new retry check.

queryUnflattened polled jobs.getQueryResults with no retry, so one 503
backendError failed an integration test after its pipeline had already
succeeded. Poll through getQueryResultsWithRetries, which uses the same
backoff as the other BigqueryClient methods. It retries 5xx and 429 and
throws other 4xx errors right away.

Also delete the temporary dataset in a finally block. Before, the method
skipped the delete when the query returned no rows or threw.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📋 Review Summary

This PR improves the resilience of BigqueryClient.queryUnflattened in integration tests by adding retry logic with backoff when polling jobs.getQueryResults for transient server errors (5xx) and rate limits (429), while failing fast on other 4xx errors. Additionally, it fixes a resource leak by moving the temporary dataset deletion into a finally block.

🔍 General Feedback

  • The implementation aligns well with existing retry patterns in BigqueryClient.
  • Good catch on both the Boolean.TRUE.equals(qResponse.getJobComplete()) null-safety check and ensuring temporary datasets are cleaned up when queries return empty results or throw exceptions.
  • The new unit tests in BigqueryClientTest cleanly cover the retry behavior, fast-fail on 4xx, and dataset cleanup.

💡 Tip: Comment /gemini review on this pull request to trigger a new Gemini code review.

Polling getQueryResults again can't fix a job that itself failed with a
transient error such as a 503 backendError, because every poll returns
the same error. Each retry now starts a new query job, which also covers
a 503 from jobs.insert. The temp table uses WRITE_TRUNCATE so a retried
job overwrites anything a failed job left behind.

The retry check also treats 403 rateLimitExceeded as transient, applies
the status-code rules to non-JSON HTTP errors, and keeps the original
jobComplete check so a missing value fails instead of looping.
@github-actions

Copy link
Copy Markdown
Contributor

Assigning reviewers:

R: @ahmedabu98 for label java.
Backup reviewer: damccorm (if the main reviewer is unresponsive, you can escalate to them by tagging their username)

Note: If you would like to opt out of this review, comment assign to next reviewer.

Available commands:

  • stop reviewer notifications - opt out of the automated review tooling
  • remind me after tests pass - tag the comment author after tests pass
  • waiting on author - shift the attention set back to the author (any comment or push by the author will return the attention set to the reviewers)

The PR bot will only process comments in the main thread (not review comments).

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Failing Test]: BigqueryClient.queryUnflattened fails BigQuery ITs on a transient 503

1 participant