nti.transactions

https://coveralls.io/repos/github/NextThought/nti.transactions/badge.svg?branch=master https://travis-ci.org/NextThought/nti.transactions.svg?branch=master Documentation Status

Extensions to the transaction package.

Transaction Management

nti.transactions.loop.TransactionsLoop is a retryable transaction manager. It is conceptually similar to the attempts context manager provided by the transaction package itself, but much more powerful and extensible via subclasses. Features include:

  • Configurable commit vetos.

  • Extensible tests for which exceptions should be retried.

  • The ability to abort the transaction and bypass a potentially expensive commit when there are expected to be no side-effects.

  • Sleeping between retries.

  • Extensive logging and timing.

The TransactionLoop can be used as-is, or it can be subclassed for customization. For use in a Pyramid tween, for example, a minimal subclass might look like this (see nti.transactions.pyramid_tween for a full-featured tween):

>>> class PyramidTransactionLoop(TransactionLoop):
...    def prep_for_retry(self, number, request):
...        request.make_body_seekable()
...    def describe_transaction(self, request):
...        return request.url

Data Managers

A few data managers are provided for convenience.

The first data manager is used to put an object in a queue (something with the full and put_nowait methods) when a transaction succeeds. If the queue is full, then the transaction will not be allowed to commit:

>>> from nti.transactions.queue import put_nowait
>>> put_nowait(queue, object)

This is a special case of the ObjectDataManager, which will call one method with any arguments when a transaction commits. It can be configured to vote on whether the transaction should be allowed to commit. or not. This is useful for, say, putting an item in a Redis queue when the transaction is successful. It can be constructed directly, but the do function is a shorthand way of joining one to the current transaction:

>>> from nti.transactions.manager import do
>>> do(print, args=("Committed"))

Caution

See the documentation of this object for numerous warnings about side-effects and its interaction with the transaction machinery. Use it with care!

nti.transactions.interfaces

Interfaces for nti.transactions.

exception AbortFailedError[source]

Bases: transaction.interfaces.TransactionError

Aborting the active transaction failed for an unknown and unexpected reason.

This is raised instead of raising very generic system exceptions such as ValueError and AttributeError.

exception CommitFailedError[source]

Bases: transaction.interfaces.TransactionError

Committing the active transaction failed for an unknown and unexpected reason.

This is raised instead of raising very generic system exceptions such as TypeError.

exception ForeignTransactionError[source]

Bases: nti.transactions.interfaces.TransactionLifecycleError

Raised when a transaction manager has its transaction changed while a controlling transaction loop believes it is in control.

The handler first aborted or committed the transaction, and then began a new one. Possibly many times.

A kind of TransactionLifecycleError. Applications must not raise this exception.

This is a programming error.

interface IAfterTransactionBegan[source]

Extends: nti.transactions.interfaces.ILoopEvent

A new transaction has begun.

tx

The transaction.

interface IExtendedTransaction[source]

Extends: transaction.interfaces.ITransaction

Extensions to the transaction api.

nti_commit()

Like commit, but produces a perfmetrics.Metric transaction.commit metric.

nti_abort()

Like abort, but produces a perfmetrics.Metric transaction.abort metric.

interface ILoopEvent[source]

Base class for event loop events.

invocation

An ILoopInvocation.

interface ILoopInvocation[source]

Description of why a loop was invoked.

handler

The handler to run.

loop

The loop doing the running.

args

The arguments passed to the handler

kwargs

The keyword arguments passed to the handler.

interface IWillAttemptTransaction[source]

Extends: nti.transactions.interfaces.ILoopEvent

Base class for attempt events.

tx

The transaction.

attempt_number

The number of the attempt. Starts at 0.

interface IWillFirstAttempt[source]

Extends: nti.transactions.interfaces.IWillAttemptTransaction

The first attempt.

interface IWillRetryAttempt[source]

Extends: nti.transactions.interfaces.IWillAttemptTransaction

A retry attempt.

interface IWillSleepBetweenAttempts[source]

Extends: nti.transactions.interfaces.ILoopEvent

Will sleep between attempts.

If the sleep_time attribute is modified, that will be the time slept.

sleep_time

The time to sleep.

exception TransactionLifecycleError[source]

Bases: transaction.interfaces.TransactionError

Raised when an application commits or aborts a transaction while the transaction controller believes it is in control.

Applications must not raise this exception.

This may have happened many times; we cannot detect that.

This is a programming error.

nti.transactions.manager

Support for data managers.

class ObjectDataManager(target=None, method_name=None, call=None, vote=None, args=(), kwargs=None)[source]

Bases: object

A generic (and therefore relatively expensive) transaction.interfaces.IDataManager that invokes a callable (usually a method of an object) when the transaction finishes successfully. The method should not raise exceptions when invoked, as they will be caught and ignored (to preserve consistency with the rest of the data managers). If there’s a chance the method could fail, then whatever actions it does take should not have side-effects.

These data managers have no guaranteed relationship to other data managers in terms of the order of which they commit, except as documented with sortKey().

Because these data managers execute exactly one operation on a complete transaction commit, implementing a savepoint is trivial: do nothing when it is rolled back. A savepoint is created to checkpoint a transaction and rolled back to reverse actions taken after the checkpoint. Only data managers that were active (joined) at the time the transaction savepoint is created are asked to create their own savepoint, and then potentially to roll it back. We do no work until the transaction is committed, so we have nothing to rollback. Moroever, if a transaction savepoint is activated before a manager joins, then that manager is not asked for its own savepoint: it is simply aborted and unjoined from the transaction if the previous savepoint is rolledback.

abort_sub(tx)[source]

Does nothing

afterCompletion(tx)

Does nothing

beforeCompletion(tx)[source]

Does nothing

commit_sub(tx)

Does nothing

sortKey()[source]

Return the string value that, when sorted, determines the order in which data managers will get to vote and commit at the end of a transaction. (See transaction.interfaces.IDataManager.sortKey()).

The default implementation of this method uses the ID of either the target object we were initialized with or the ID of the actual callable we will call. This has the property of ensuring that all calls to methods of a particular object instance (when target is given), or calls to a particular callable (when target is not given) will execute in the order in which they were added to the transaction.

Note

This relies on an implementation detail of the transaction package, which sorts using list.sort(), which is guaranteed to be stable: thus objects using the same key remain in the same relative order. (See transaction._transaction.Transaction._commitResources().)

To execute only calls to a particular method of a particular instance in the order they are added to the transaction, but allow other methods to execute before or after them, do not provide the target.

It is not advisable to use the ID of this object (self) in the implementation of this method, because the ID values are not guaranteed to be monotonically increasing and thus instances of a particular class that did this would execute in “random” order.

class OrderedNearEndObjectDataManager(target=None, method_name=None, call=None, vote=None, args=(), kwargs=None)[source]

Bases: nti.transactions.manager.ObjectDataManager

A special extension of ObjectDataManager that attempts to execute after all other data managers have executed. This is useful when an operation relies on the execution of other data managers.

New in version 1.1.

sortKey()[source]

Sort prepended with z’s in an attempt to execute after other data managers.

do(*args, **kwargs)[source]

Establishes a IDataManager in the current transaction. See ObjectDataManager for the possible arguments.

do_near_end(*args, **kwargs)[source]

Establishes a IDataManager in the current transaction that will attempt to execute after all other DataManagers have had their say. See ObjectDataManager for the possible arguments.

New in version 1.1.

nti.transactions.queue

Support for transactionally working with queues.

put_nowait(queue, obj)[source]

Transactionally puts obj in queue. The obj will only be visible in the queue after the current transaction successfully commits. If the queue cannot accept the object because it is full, the transaction will be aborted.

See gevent.queue.Queue and Queue.Full and gevent.queue.

nti.transactions.transactions (deprecated)

This module only contains backwards compatibility imports.