Skip to content

Commit

Permalink
Merge branch 'main' into getQueryResults-before-jobs-get
Browse files Browse the repository at this point in the history
  • Loading branch information
kiraksi authored Jan 24, 2024
2 parents 55f0a24 + 39f33b2 commit d47919c
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 2 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@
[1]: https://pypi.org/project/google-cloud-bigquery/#history


## [3.17.0](https://github.com/googleapis/python-bigquery/compare/v3.16.0...v3.17.0) (2024-01-24)


### Features

* Support universe resolution ([#1774](https://github.com/googleapis/python-bigquery/issues/1774)) ([0b5c1d5](https://github.com/googleapis/python-bigquery/commit/0b5c1d597cdec3a05a16fb935595f773c5840bd4))


### Bug Fixes

* `query_and_wait` now retains unknown query configuration `_properties` ([#1793](https://github.com/googleapis/python-bigquery/issues/1793)) ([4ba4342](https://github.com/googleapis/python-bigquery/commit/4ba434287a0a25f027e3b63a80f8881a9b16723e))
* Raise `ValueError` in `query_and_wait` with wrong `job_config` type ([4ba4342](https://github.com/googleapis/python-bigquery/commit/4ba434287a0a25f027e3b63a80f8881a9b16723e))


### Documentation

* Remove unused query code sample ([#1769](https://github.com/googleapis/python-bigquery/issues/1769)) ([1f96439](https://github.com/googleapis/python-bigquery/commit/1f96439b3dbd27f11be5e2af84f290ec6094d0a4))
* Update `snippets.py` to use `query_and_wait` ([#1773](https://github.com/googleapis/python-bigquery/issues/1773)) ([d90602d](https://github.com/googleapis/python-bigquery/commit/d90602de87e58b665cb974401a327a640805822f))
* Update multiple samples to change query to query_and_wait ([#1784](https://github.com/googleapis/python-bigquery/issues/1784)) ([d1161dd](https://github.com/googleapis/python-bigquery/commit/d1161dddde41a7d35b30033ccbf6984a5de640bd))
* Update the query with no cache sample to use query_and_wait API ([#1770](https://github.com/googleapis/python-bigquery/issues/1770)) ([955a4cd](https://github.com/googleapis/python-bigquery/commit/955a4cd99e21cbca1b2f9c1dc6aa3fd8070cd61f))
* Updates `query` to `query and wait` in samples/desktopapp/user_credentials.py ([#1787](https://github.com/googleapis/python-bigquery/issues/1787)) ([89f1299](https://github.com/googleapis/python-bigquery/commit/89f1299b3164b51fb0f29bc600a34ded59c10682))

## [3.16.0](https://github.com/googleapis/python-bigquery/compare/v3.15.0...v3.16.0) (2024-01-12)


Expand Down
1 change: 1 addition & 0 deletions google/cloud/bigquery/_pyarrow_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ def pyarrow_timestamp():
pyarrow.date64().id: "DATETIME", # because millisecond resolution
pyarrow.binary().id: "BYTES",
pyarrow.string().id: "STRING", # also alias for pyarrow.utf8()
pyarrow.large_string().id: "STRING",
# The exact scale and precision don't matter, see below.
pyarrow.decimal128(38, scale=9).id: "NUMERIC",
}
Expand Down
2 changes: 1 addition & 1 deletion google/cloud/bigquery/retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def _should_retry(exc):
deadline on the retry object.
"""

job_retry_reasons = "rateLimitExceeded", "backendError"
job_retry_reasons = "rateLimitExceeded", "backendError", "jobRateLimitExceeded"


def _job_should_retry(exc):
Expand Down
2 changes: 1 addition & 1 deletion google/cloud/bigquery/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

__version__ = "3.16.0"
__version__ = "3.17.0"
80 changes: 80 additions & 0 deletions tests/unit/test_job_retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
import google.api_core.retry
import freezegun

from google.cloud.bigquery.client import Client
from google.cloud.bigquery import _job_helpers
from google.cloud.bigquery.retry import DEFAULT_JOB_RETRY

from .helpers import make_connection


Expand Down Expand Up @@ -240,3 +244,79 @@ def test_raises_on_job_retry_on_result_with_non_retryable_jobs(client):
),
):
job.result(job_retry=google.api_core.retry.Retry())


def test_query_and_wait_retries_job_for_DDL_queries():
"""
Specific test for retrying DDL queries with "jobRateLimitExceeded" error:
https://github.com/googleapis/python-bigquery/issues/1790
"""
freezegun.freeze_time(auto_tick_seconds=1)
client = mock.create_autospec(Client)
client._call_api.__name__ = "_call_api"
client._call_api.__qualname__ = "Client._call_api"
client._call_api.__annotations__ = {}
client._call_api.__type_params__ = ()
client._call_api.side_effect = (
{
"jobReference": {
"projectId": "response-project",
"jobId": "abc",
"location": "response-location",
},
"jobComplete": False,
},
google.api_core.exceptions.InternalServerError(
"job_retry me", errors=[{"reason": "jobRateLimitExceeded"}]
),
google.api_core.exceptions.BadRequest(
"retry me", errors=[{"reason": "jobRateLimitExceeded"}]
),
{
"jobReference": {
"projectId": "response-project",
"jobId": "abc",
"location": "response-location",
},
"jobComplete": True,
"schema": {
"fields": [
{"name": "full_name", "type": "STRING", "mode": "REQUIRED"},
{"name": "age", "type": "INT64", "mode": "NULLABLE"},
],
},
"rows": [
{"f": [{"v": "Whillma Phlyntstone"}, {"v": "27"}]},
{"f": [{"v": "Bhetty Rhubble"}, {"v": "28"}]},
{"f": [{"v": "Phred Phlyntstone"}, {"v": "32"}]},
{"f": [{"v": "Bharney Rhubble"}, {"v": "33"}]},
],
},
)
rows = _job_helpers.query_and_wait(
client,
query="SELECT 1",
location="request-location",
project="request-project",
job_config=None,
page_size=None,
max_results=None,
retry=DEFAULT_JOB_RETRY,
job_retry=DEFAULT_JOB_RETRY,
)
assert len(list(rows)) == 4

# Relevant docs for the REST API path: https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query
# and https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/getQueryResults
query_request_path = "/projects/request-project/queries"

calls = client._call_api.call_args_list
_, kwargs = calls[0]
assert kwargs["method"] == "POST"
assert kwargs["path"] == query_request_path

# TODO: Add assertion statements for response paths after PR#1797 is fixed

_, kwargs = calls[3]
assert kwargs["method"] == "POST"
assert kwargs["path"] == query_request_path
27 changes: 27 additions & 0 deletions tests/unit/test_retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,30 @@ def test_DEFAULT_JOB_RETRY_deadline():

# Make sure we can retry the job at least once.
assert DEFAULT_JOB_RETRY._deadline > DEFAULT_RETRY._deadline


def test_DEFAULT_JOB_RETRY_job_rate_limit_exceeded_retry_predicate():
"""Tests the retry predicate specifically for jobRateLimitExceeded."""
from google.cloud.bigquery.retry import DEFAULT_JOB_RETRY
from google.api_core.exceptions import ClientError

# Non-ClientError exceptions should never trigger a retry
assert not DEFAULT_JOB_RETRY._predicate(TypeError())

# ClientError without specific reason shouldn't trigger a retry
assert not DEFAULT_JOB_RETRY._predicate(ClientError("fail"))

# ClientError with generic reason "idk" shouldn't trigger a retry
assert not DEFAULT_JOB_RETRY._predicate(
ClientError("fail", errors=[dict(reason="idk")])
)

# ClientError with reason "jobRateLimitExceeded" should trigger a retry
assert DEFAULT_JOB_RETRY._predicate(
ClientError("fail", errors=[dict(reason="jobRateLimitExceeded")])
)

# Other retryable reasons should still work as expected
assert DEFAULT_JOB_RETRY._predicate(
ClientError("fail", errors=[dict(reason="backendError")])
)

0 comments on commit d47919c

Please sign in to comment.