13
13
# limitations under the License.
14
14
15
15
"""DB-API Connection for the Google Cloud Spanner."""
16
- import time
17
16
import warnings
18
17
19
18
from google .api_core .exceptions import Aborted
20
19
from google .api_core .gapic_v1 .client_info import ClientInfo
21
20
from google .cloud import spanner_v1 as spanner
22
21
from google .cloud .spanner_dbapi .batch_dml_executor import BatchMode , BatchDmlExecutor
23
22
from google .cloud .spanner_dbapi .parsed_statement import ParsedStatement , Statement
23
+ from google .cloud .spanner_dbapi .transaction_helper import TransactionRetryHelper
24
24
from google .cloud .spanner_v1 import RequestOptions
25
- from google .cloud .spanner_v1 .session import _get_retry_delay
26
25
from google .cloud .spanner_v1 .snapshot import Snapshot
27
26
from deprecated import deprecated
28
27
29
- from google .cloud .spanner_dbapi .checksum import _compare_checksums
30
- from google .cloud .spanner_dbapi .checksum import ResultsChecksum
31
28
from google .cloud .spanner_dbapi .cursor import Cursor
32
29
from google .cloud .spanner_dbapi .exceptions import (
33
30
InterfaceError ,
37
34
from google .cloud .spanner_dbapi .version import DEFAULT_USER_AGENT
38
35
from google .cloud .spanner_dbapi .version import PY_VERSION
39
36
40
- from google .rpc .code_pb2 import ABORTED
41
-
42
37
43
38
CLIENT_TRANSACTION_NOT_STARTED_WARNING = (
44
39
"This method is non-operational as a transaction has not been started."
45
40
)
46
- MAX_INTERNAL_RETRIES = 50
47
41
48
42
49
43
def check_not_closed (function ):
@@ -99,9 +93,6 @@ def __init__(self, instance, database=None, read_only=False):
99
93
self ._transaction = None
100
94
self ._session = None
101
95
self ._snapshot = None
102
- # SQL statements, which were executed
103
- # within the current transaction
104
- self ._statements = []
105
96
106
97
self .is_closed = False
107
98
self ._autocommit = False
@@ -118,6 +109,7 @@ def __init__(self, instance, database=None, read_only=False):
118
109
self ._spanner_transaction_started = False
119
110
self ._batch_mode = BatchMode .NONE
120
111
self ._batch_dml_executor : BatchDmlExecutor = None
112
+ self ._transaction_helper = TransactionRetryHelper (self )
121
113
122
114
@property
123
115
def autocommit (self ):
@@ -281,76 +273,6 @@ def _release_session(self):
281
273
self .database ._pool .put (self ._session )
282
274
self ._session = None
283
275
284
- def retry_transaction (self ):
285
- """Retry the aborted transaction.
286
-
287
- All the statements executed in the original transaction
288
- will be re-executed in new one. Results checksums of the
289
- original statements and the retried ones will be compared.
290
-
291
- :raises: :class:`google.cloud.spanner_dbapi.exceptions.RetryAborted`
292
- If results checksum of the retried statement is
293
- not equal to the checksum of the original one.
294
- """
295
- attempt = 0
296
- while True :
297
- self ._spanner_transaction_started = False
298
- attempt += 1
299
- if attempt > MAX_INTERNAL_RETRIES :
300
- raise
301
-
302
- try :
303
- self ._rerun_previous_statements ()
304
- break
305
- except Aborted as exc :
306
- delay = _get_retry_delay (exc .errors [0 ], attempt )
307
- if delay :
308
- time .sleep (delay )
309
-
310
- def _rerun_previous_statements (self ):
311
- """
312
- Helper to run all the remembered statements
313
- from the last transaction.
314
- """
315
- for statement in self ._statements :
316
- if isinstance (statement , list ):
317
- statements , checksum = statement
318
-
319
- transaction = self .transaction_checkout ()
320
- statements_tuple = []
321
- for single_statement in statements :
322
- statements_tuple .append (single_statement .get_tuple ())
323
- status , res = transaction .batch_update (statements_tuple )
324
-
325
- if status .code == ABORTED :
326
- raise Aborted (status .details )
327
-
328
- retried_checksum = ResultsChecksum ()
329
- retried_checksum .consume_result (res )
330
- retried_checksum .consume_result (status .code )
331
-
332
- _compare_checksums (checksum , retried_checksum )
333
- else :
334
- res_iter , retried_checksum = self .run_statement (statement , retried = True )
335
- # executing all the completed statements
336
- if statement != self ._statements [- 1 ]:
337
- for res in res_iter :
338
- retried_checksum .consume_result (res )
339
-
340
- _compare_checksums (statement .checksum , retried_checksum )
341
- # executing the failed statement
342
- else :
343
- # streaming up to the failed result or
344
- # to the end of the streaming iterator
345
- while len (retried_checksum ) < len (statement .checksum ):
346
- try :
347
- res = next (iter (res_iter ))
348
- retried_checksum .consume_result (res )
349
- except StopIteration :
350
- break
351
-
352
- _compare_checksums (statement .checksum , retried_checksum )
353
-
354
276
def transaction_checkout (self ):
355
277
"""Get a Cloud Spanner transaction.
356
278
@@ -443,11 +365,11 @@ def commit(self):
443
365
if self ._spanner_transaction_started and not self ._read_only :
444
366
self ._transaction .commit ()
445
367
except Aborted :
446
- self .retry_transaction ()
368
+ self ._transaction_helper . retry_transaction ()
447
369
self .commit ()
448
370
finally :
449
371
self ._release_session ()
450
- self ._statements = []
372
+ self ._transaction_helper . reset ()
451
373
self ._transaction_begin_marked = False
452
374
self ._spanner_transaction_started = False
453
375
@@ -467,7 +389,7 @@ def rollback(self):
467
389
self ._transaction .rollback ()
468
390
finally :
469
391
self ._release_session ()
470
- self ._statements = []
392
+ self ._transaction_helper . reset ()
471
393
self ._transaction_begin_marked = False
472
394
self ._spanner_transaction_started = False
473
395
@@ -486,7 +408,7 @@ def run_prior_DDL_statements(self):
486
408
487
409
return self .database .update_ddl (ddl_statements ).result ()
488
410
489
- def run_statement (self , statement : Statement , retried = False ):
411
+ def run_statement (self , statement : Statement ):
490
412
"""Run single SQL statement in begun transaction.
491
413
492
414
This method is never used in autocommit mode. In
@@ -506,17 +428,11 @@ def run_statement(self, statement: Statement, retried=False):
506
428
checksum of this statement results.
507
429
"""
508
430
transaction = self .transaction_checkout ()
509
- if not retried :
510
- self ._statements .append (statement )
511
-
512
- return (
513
- transaction .execute_sql (
514
- statement .sql ,
515
- statement .params ,
516
- param_types = statement .param_types ,
517
- request_options = self .request_options ,
518
- ),
519
- ResultsChecksum () if retried else statement .checksum ,
431
+ return transaction .execute_sql (
432
+ statement .sql ,
433
+ statement .params ,
434
+ param_types = statement .param_types ,
435
+ request_options = self .request_options ,
520
436
)
521
437
522
438
@check_not_closed
0 commit comments