@@ -82,6 +82,28 @@ def _should_retry(exc):
8282pass ``retry=bigquery.DEFAULT_RETRY.with_deadline(30)``.
8383"""
8484
85+
86+ def _should_retry_get_job_conflict (exc ):
87+ """Predicate for determining when to retry a jobs.get call after a conflict error.
88+
89+ Sometimes we get a 404 after a Conflict. In this case, we
90+ have pretty high confidence that by retrying the 404, we'll
91+ (hopefully) eventually recover the job.
92+ https://github.com/googleapis/python-bigquery/issues/2134
93+
94+ Note: we may be able to extend this to user-specified predicates
95+ after https://github.com/googleapis/python-api-core/issues/796
96+ to tweak existing Retry object predicates.
97+ """
98+ return isinstance (exc , exceptions .NotFound ) or _should_retry (exc )
99+
100+
101+ _DEFAULT_GET_JOB_CONFLICT_RETRY = retry .Retry (
102+ predicate = _should_retry_get_job_conflict , deadline = _DEFAULT_RETRY_DEADLINE
103+ )
104+ """Private, may be removed in future."""
105+
106+
85107# Note: Take care when updating DEFAULT_TIMEOUT to anything but None. We
86108# briefly had a default timeout, but even setting it at more than twice the
87109# theoretical server-side default timeout of 2 minutes was not enough for
@@ -142,6 +164,35 @@ def _job_should_retry(exc):
142164The default job retry object.
143165"""
144166
167+
168+ def _query_job_insert_should_retry (exc ):
169+ # Per https://github.com/googleapis/python-bigquery/issues/2134, sometimes
170+ # we get a 404 error. In this case, if we get this far, assume that the job
171+ # doesn't actually exist and try again. We can't add 404 to the default
172+ # job_retry because that happens for errors like "this table does not
173+ # exist", which probably won't resolve with a retry.
174+
175+ if isinstance (exc , exceptions .RetryError ):
176+ exc = exc .cause
177+
178+ if isinstance (exc , exceptions .NotFound ):
179+ message = exc .message
180+ # Don't try to retry table/dataset not found, just job not found.
181+ # The URL contains jobs, so use whitespace to disambiguate.
182+ return message is not None and " job" in message .lower ()
183+
184+ return _job_should_retry (exc )
185+
186+
187+ _DEFAULT_QUERY_JOB_INSERT_RETRY = retry .Retry (
188+ predicate = _query_job_insert_should_retry ,
189+ # jobs.insert doesn't wait for the job to complete, so we don't need the
190+ # long _DEFAULT_JOB_DEADLINE for this part.
191+ deadline = _DEFAULT_RETRY_DEADLINE ,
192+ )
193+ """Private, may be removed in future."""
194+
195+
145196DEFAULT_GET_JOB_TIMEOUT = 128
146197"""
147198Default timeout for Client.get_job().
0 commit comments