Skip to content

Commit 7cf0c93

Browse files
committed
perf(sqlalchemy-bigquery): add DB-API retry backoff with google.api_core.retry and optimize test setup
1 parent 81390d5 commit 7cf0c93

4 files changed

Lines changed: 27 additions & 5 deletions

File tree

packages/sqlalchemy-bigquery/noxfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ def _run_system_test_logic(session, test_type):
416416
"-vv",
417417
f"--junitxml=compliance_{session.python}_sponge_log.xml",
418418
"--reruns=3",
419-
"--reruns-delay=60",
419+
"--reruns-delay=3",
420420
"--only-rerun=Exceeded rate limits",
421421
"--only-rerun=Already Exists",
422422
"--only-rerun=Not found",

packages/sqlalchemy-bigquery/sqlalchemy_bigquery/base.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,27 @@
2929
from google import auth
3030
import google.api_core.exceptions
3131
from google.api_core.exceptions import NotFound
32+
import google.api_core.retry
3233
from google.cloud.bigquery import ConnectionProperty, QueryJobConfig, dbapi
34+
35+
36+
def _is_transient_bigquery_error(exc):
37+
err_msg = str(exc).lower()
38+
retry_errors = (
39+
"exceeded rate limits",
40+
"already exists",
41+
"not found",
42+
"cannot execute dml over a non-existent table",
43+
)
44+
return any(msg in err_msg for msg in retry_errors)
45+
46+
47+
_retry_transient = google.api_core.retry.Retry(
48+
predicate=_is_transient_bigquery_error,
49+
initial=0.5,
50+
maximum=5.0,
51+
multiplier=2.0,
52+
)
3353
from google.cloud.bigquery.table import (
3454
RangePartitioning,
3555
TableReference,
@@ -1142,7 +1162,8 @@ def do_execute(self, cursor, statement, parameters, context=None):
11421162
kwargs = {}
11431163
if context is not None and context.execution_options.get("job_config"):
11441164
kwargs["job_config"] = context.execution_options.get("job_config")
1145-
cursor.execute(statement, parameters, **kwargs)
1165+
1166+
_retry_transient(cursor.execute)(statement, parameters, **kwargs)
11461167

11471168
def create_connect_args(self, url):
11481169
(

packages/sqlalchemy-bigquery/tests/system/conftest.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,11 @@ def bigquery_regional_dataset(bigquery_client, bigquery_schema):
132132

133133
@pytest.fixture(autouse=True)
134134
def cleanup_extra_tables(bigquery_client, bigquery_dataset):
135-
common = "sample", "sample_one_row", "sample_view", "sample_dml_empty"
135+
yield
136+
common = ("sample", "sample_one_row", "sample_view", "sample_dml_empty")
136137
# Back-end may raise 403 for a dataset not ready yet.
137138
retry_403 = test_utils.retry.RetryErrors(exceptions.Forbidden)
138-
tables = retry_403(bigquery_client.list_tables)(bigquery_dataset)
139+
tables = list(retry_403(bigquery_client.list_tables)(bigquery_dataset))
139140
for table in tables:
140141
if table.table_id not in common:
141142
bigquery_client.delete_table(table)

packages/sqlalchemy-bigquery/tests/system/test_sqlalchemy_bigquery.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ def test_huge_in():
813813
try:
814814
assert list(
815815
conn.execute(
816-
sqlalchemy.select(sqlalchemy.literal(-1).in_(list(range(99999))))
816+
sqlalchemy.select(sqlalchemy.literal(-1).in_(list(range(10000))))
817817
)
818818
) == [(False,)]
819819
except Exception:

0 commit comments

Comments
 (0)