1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
# Copyright 2009 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""The lp.services.database package."""
__metaclass__ = type
__all__ = [
'read_transaction',
'write_transaction',
]
from psycopg2.extensions import TransactionRollbackError
from storm.exceptions import DisconnectionError, IntegrityError
import transaction
from twisted.python.util import mergeFunctionMetadata
from canonical.database.sqlbase import (
reset_store,
)
RETRY_ATTEMPTS = 3
def retry_transaction(func):
"""Decorator used to retry database transaction failures.
The function being decorated should not have side effects outside
of the transaction.
"""
def retry_transaction_decorator(*args, **kwargs):
attempt = 0
while True:
attempt += 1
try:
return func(*args, **kwargs)
except (DisconnectionError, IntegrityError,
TransactionRollbackError), exc:
if attempt >= RETRY_ATTEMPTS:
raise # tried too many times
return mergeFunctionMetadata(func, retry_transaction_decorator)
def read_transaction(func):
"""Decorator used to run the function inside a read only transaction.
The transaction will be aborted on successful completion of the
function. The transaction will be retried if appropriate.
"""
@reset_store
def read_transaction_decorator(*args, **kwargs):
transaction.begin()
try:
return func(*args, **kwargs)
finally:
transaction.abort()
return retry_transaction(mergeFunctionMetadata(
func, read_transaction_decorator))
def write_transaction(func):
"""Decorator used to run the function inside a write transaction.
The transaction will be committed on successful completion of the
function, and aborted on failure. The transaction will be retried
if appropriate.
"""
@reset_store
def write_transaction_decorator(*args, **kwargs):
transaction.begin()
try:
ret = func(*args, **kwargs)
except:
transaction.abort()
raise
transaction.commit()
return ret
return retry_transaction(mergeFunctionMetadata(
func, write_transaction_decorator))
|