Every Persist object provided by Sider can be used within transactions. You can atomically commit multiple operations.
Under the hood, transaction blocks are simply looped until objects the transaction deals with haven’t been faced any conflicts with other sessions/transactions. If there are no concurrent touches to names in the following transaction:
def block(trial, transaction):
names.append(new_name)
session.transaction(block)
it will be successfully committed. Otherwise, it retries the whole transaction block. You can easily prove this by just printing trial (the first argument of the block function) inside the transaction block. It will print one or more retrial counting numbers.
This means you shouldn’t do I/O in the transaction block. Your I/O could be executed two or more times. Do I/O after or before transaction blocks instead.
There are two properties of every operation: query() or manipulative() or both. For example, Hash.get() method is a query operation. On the other hand, Set.add() method is manipulative. There is a rule of transaction: query operations can’t be used after manipulative operations. For example, the following transaction block has no problem:
# Atomically wraps an existing string value of the specific
# key of a hash.
hash_ = session.get('my_hash', Hash)
def block(trial, transaction):
current_value = hash_['my_key'] # [query operation]
updated_value = '(' + current_value + ')'
hash_['my_key'] = updated_value # [manipulative operation]
session.transaction(block)
while the following raises a CommitError:
hash_ = session.get('my_hash', Hash)
def block(trial, transaction):
current_value = hash_['my_key'] # [query operation]
updated_value = '(' + current_value + ')'
hash_['my_key'] = updated_value # [manipulative operation]
# The following statement raises CommitError because
# it contains a query operation.
current_value2 = hash_['my_key2'] # [query operation]
updated_value2 = '(' + current_value2 + ')'
hash_['my_key'] = updated_value2 # [manipulative operation]
session.transaction(block)
See also
Transaction block.
Parameters: |
|
---|
Executes a block in the transaction:
def block(trial, transaction):
list_[0] = list_[0].upper()
transaction(block)
Parameters: |
|
---|---|
Raises sider.exceptions.DoubleTransactionError: | |
when any transaction has already being executed for a session and ignore_double is False |
Parameters: |
|
---|
You can more explictly execute (and retry) a routine in the transaction than using __call__().
It returns a generator that yields an integer which represents its (re)trial count (from 0) until the transaction doesn’t face ConflictError.
For example:
for trial in transaction:
list_[0] = list_[0].upper()
Raises sider.exceptions.DoubleTransactionError: | |
---|---|
when any transaction has already being executed for a session |
list of weak references to the object (if defined)
Explicitly marks the transaction beginning to commit from this. From this to end of a transaction, any query operations will raise CommitError.
Makes commit_stack text readable. If its session.verbose_transaction_error is not True, it will simply return an empty string.
Parameters: |
|
---|---|
Returns: | the formatted commit_stack text |
Return type: | basestring |
Note
It’s totally for internal use.
Makes enter_stack text readable. If its session.verbose_transaction_error is not True, it will simply return an empty string.
Parameters: |
|
---|---|
Returns: | the formatted enter_stack text |
Return type: | basestring |
Note
It’s totally for internal use.
Watches more keys.
Parameters: |
|
---|
The decorator that marks the method manipulative.
Parameters: | function (collections.Callable) – the method to mark |
---|---|
Returns: | the marked method |
Return type: | collections.Callable |
The decorator that marks the method query.
Parameters: | function (collections.Callable) – the method to mark |
---|---|
Returns: | the marked method |
Return type: | collections.Callable |