/usr/share/pyshared/transaction/tests/convenience.txt is in python-transaction 1.1.1-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | Transaction convenience support
===============================
(We *really* need to write proper documentation for the transaction
package, but I don't want to block the conveniences documented here
for that.)
with support
------------
We can now use the with statement to define transaction boundaries.
>>> import transaction.tests.savepointsample
>>> dm = transaction.tests.savepointsample.SampleSavepointDataManager()
>>> dm.keys()
[]
We can use the transaction module directly:
>>> with transaction as t:
... dm['z'] = 1
... t.note('test 1')
>>> dm['z']
1
>>> dm.last_note
'test 1'
>>> with transaction:
... dm['z'] = 2
... xxx
Traceback (most recent call last):
...
NameError: name 'xxx' is not defined
>>> dm['z']
1
We can use it with a manager:
>>> with transaction.manager as t:
... dm['z'] = 3
... t.note('test 3')
>>> dm['z']
3
>>> dm.last_note
'test 3'
>>> with transaction:
... dm['z'] = 4
... xxx
Traceback (most recent call last):
...
NameError: name 'xxx' is not defined
>>> dm['z']
3
Retries
-------
Commits can fail for transient reasons, especially conflicts.
Applications will often retry transactions some number of times to
overcome transient failures. This typically looks something like::
for i in range(3):
try:
with transaction:
... some something ...
except SomeTransientException:
contine
else:
break
This is rather ugly.
Transaction managers provide a helper for this case. To show this,
we'll use a contrived example:
>>> ntry = 0
>>> with transaction:
... dm['ntry'] = 0
>>> import transaction.interfaces
>>> class Retry(transaction.interfaces.TransientError):
... pass
>>> for attempt in transaction.manager.attempts():
... with attempt as t:
... t.note('test')
... print dm['ntry'], ntry
... ntry += 1
... dm['ntry'] = ntry
... if ntry % 3:
... raise Retry(ntry)
0 0
0 1
0 2
The raising of a subclass of TransientError is critical here. It's
what signals that the transaction should be retried. It is generally
up to the data manager to signal that a transaction should try again
by raising a subclass of TransientError (or TransientError itself, of
course).
You shouldn't make any assumptions about the object returned by the
iterator. (It isn't a transaction or transaction manager, as far as
you know. :) If you use the ``as`` keyword in the ``with`` statement,
a transaction object will be assigned to the variable named.
By default, it tries 3 times. We can tell it how many times to try:
>>> for attempt in transaction.manager.attempts(2):
... with attempt:
... ntry += 1
... if ntry % 3:
... raise Retry(ntry)
Traceback (most recent call last):
...
Retry: 5
It it doesn't succeed in that many times, the exception will be
propagated.
Of course, other errors are propagated directly:
>>> ntry = 0
>>> for attempt in transaction.manager.attempts():
... with attempt:
... ntry += 1
... if ntry == 3:
... raise ValueError(ntry)
Traceback (most recent call last):
...
ValueError: 3
We can use the default transaction manager:
>>> for attempt in transaction.attempts():
... with attempt as t:
... t.note('test')
... print dm['ntry'], ntry
... ntry += 1
... dm['ntry'] = ntry
... if ntry % 3:
... raise Retry(ntry)
3 3
3 4
3 5
Sometimes, a data manager doesn't raise exceptions directly, but
wraps other other systems that raise exceptions outside of it's
control. Data managers can provide a should_retry method that takes
an exception instance and returns True if the transaction should be
attempted again.
>>> class DM(transaction.tests.savepointsample.SampleSavepointDataManager):
... def should_retry(self, e):
... if 'should retry' in str(e):
... return True
>>> ntry = 0
>>> dm2 = DM()
>>> with transaction:
... dm2['ntry'] = 0
>>> for attempt in transaction.manager.attempts():
... with attempt:
... print dm['ntry'], ntry
... ntry += 1
... dm['ntry'] = ntry
... dm2['ntry'] = ntry
... if ntry % 3:
... raise ValueError('we really should retry this')
6 0
6 1
6 2
>>> dm2['ntry']
3
|