Abstract base class for transactions. A transaction may involve multiple targets and needs to be fully successful to be marked as done.
This class gracefully handles concurrent modifications and auto-retries but embeds no tool to rollback.
Transactions may register subtransactions. This field is a list of Transaction. Sub-transactions are played after the main transactors
Transactions status may be persisted for traceability, further analysis... for this purpose, a minimal schema is embedded in this base class. When deriving, you MUST keep
- datetime field as rangekey
- status field
The hash key field may be changed to pick a more relevant name or change its type. In any case, you are responsible of setting its value. For example, if collecting rewards for a player, you may wish to keep track of related transactions by user_id hence set requester_id to user_id
Deriving class MUST set field __table__ and requester_id field
Run the transaction and, if needed, store its state to the database
set up preconditions and parameters (_setup() – only called once no matter what).
fetch all transaction steps (_get_transactors()).
for each transaction :
- fetch the target object from the DB.
- modify the target object according to the transaction’s parameters.
- save the (modified) target to the DB
run sub-transactions (if any)
save the transaction to the DB
Each transation may be retried up to MAX_RETRIES times automatically. commit uses conditional writes to avoid overwriting data in the case of concurrent transactions on the same target (see _retry()).
If the transaction is transient (transient = True), do nothing.
If the transaction is persistent (transient = False), save it to the DB, as DynamoDBModel.save().
Note: this method is called automatically from commit. You may but do not need to call it explicitly.
Set up preconditions and parameters for the transaction.
This method is only run once, regardless of how many retries happen. You should override it to fill the substransactions array or to fetch all the data that wil not be chanded by the transaction need from the database to run the transaction (e.g. the cost of a Bingo card, or the contents of a reward).
Fetch a list of targets (getter, setter) tuples. The transaction engine will walk the list. For each tuple, the getter and the setter are called successively until this step of the transaction succeed or exhaust the MAX_RETRIES.
- getter: Fetch the object on which this transaction is supposed to operate
(e.g. a User instance for UserResourceTransactions) from the DB and return it. It is important that this method actually connect to the database and retrieve a clean, up-to-date version of the object – because it will be called repeatedly if conditional updates fail due to the target object having changed. The getter takes no argument and returns a DBModel instance
- setter: Applyies the transaction to the target, modifying it in-place.
Does not attempt to save the target or the transaction to the DB. The setter takes a DBModel instance as argument. Its return value is ignored
The list is walked from 0 to len(transactors)-1. Depending on your application, Order may matter.
|If the target doesn’t exist in the DB.|
Run sub-transactions if applicable. This is called after the main transactors.
This code has been moved to its own method to ease overloading in real-world applications without re-implementing the whole commit logic.
This method should not be called directly. It may only be overloaded to handle special behaviors like callbacks.