NAME
Rinci::function::Transaction - Transactional system based on functions
VERSION
version 1.1.17
SPECIFICATION VERSION
1.1
SPECIFICATION
This document describes a transactional system based on functions, where several function calls participate in a single transaction. This transactional system has the following properties:
Client/server architecture
See Riap::Transaction. Client can start more than one active transactions on the server. Each transaction-management request and the function calls are requested separately (each one is a separate Riap request).
Undo/redo
Committed transactions are still recorded in the database along with its undo information. Client can request to undo/redo the transactions. Thus the system is also an undo/redo system.
Relies on the functions for reliability/ACID properties
Server or framework provides the transaction manager (TM), but each function acts as the resource manager (RM). It is the responsibility of the functions to maintain ACID properties while modifying resources. For best results, each function should be written carefully and tested extensively, and utilizes a real, robust RM (like an RDBMS to store data or a transactional filesystem layer to read/modify files). In the absence of a real RM, some ACID properties like isolation and consistency might be compromised. For example
Function performs its action by dividing the action into a series of unit action steps, then performing the steps sequentially. For example, creating a Unix user might be comprised of these steps: adding a record to /etc/passwd, adding a record to /etc/shadow, adding a record to /etc/group, adding a record to /etc/gshadow, creating home directory, copying skeleton files. Before actually performing each step, the function should record the undo step first in the transaction manager. This way, undo/rollback/rollback recovery can be performed when needed.
Legends
Transaction statuses: I (in-progress), A (aborted), C (committed), R (rolled back), E (prepared, currently not described/reserved for two-phase commit), u (committed, undoing), d (committed, redoing), U (committed, undone), X (inconsistent, ignored). tx = transaction.
Fault tolerance when performing execution (do)
Note: Over Riap, each client request might be performed by different instances of TM (since it might be handle by different daemon child process). TM currently does not detect whether a tx with status I has crashed; that is why each step (and its undo step) needs to be idempotent and checks initial condition. TM only recovers tx's with status A by rolling them back.
- 1. Client begins tx, TM creates a tx entry in its database
-
Initial tx status is I. Crash right after this moment is okay, since there is nothing to recover. After TM is started again, during recovery it can continue only needs to mark tx status as A. No undo steps have been created. Then TM marks tx status as R.
- 2. Client A function is called, function wants to do an action step
-
Crash right after this moment is also handled like in previous case.
- 3. Function wants to do an action step (S1), records undo step (S1') to TM
-
Crash right after this moment will cause S1' to be performed by TM, even though the function has not performed S1. In this case S1' needs to do nothing. That is why it's important that function (actually, all its action and undo steps) be idempotent and checks for initial condition.
- 4. Function performs S1
-
Crash during this moment (when S1 is not completed) is also handled like in previous case. S1' needs to revert the (partial) side effects of S1.
Crash right after this moment is also handled like in previous case, but this time S1 is already performed, so S1' needs to revert its side effects.
- 5. Function wants to do another action step (S2), adds S2' to TM
-
Crash right after this moment will cause TM to mark tx status as A, then perform S2' followed by S1' during recovery. After that tx status is set as R.
- 6. Function performs S2
-
Crash right after this moment is also handled like in previous case.
- 7. Function exits
-
Crash right after this moment is also handled like in previous case.
- 8. Another function might be called
- 9. Client requests commit, TM marks tx status as C
-
Crash during this moment (when tx status C is not yet recorded in TM's database) is handled like in previous case. When TM is started again, it sweeps all tx's that have I/A statuses and rolls them back.
Crash right after this moment no longer rolls back tx since tx is already committed.
Fault tolerance when performing undo
All of these steps will be performed by the same TM, since there is only one client Riap request. Thus TM will be able to detect crash in the middle of the steps.
- 1. Client requests undo for tx2
-
TM will check first whether tx2 belongs to the client, whether tx status is C, whether client need to undo its other tx's (tx1 and possible other tx's too). If everything is OK, TX mark tx entry to u and set last-undo-step to none (no steps is done first).
For example tx to undo is tx1, its undo steps are S2' and S1'.
Crash after this moment (or each next moments before the last) will cause the next instance of TM to try to redo this tx, by performing its redo steps (S1 and then S2). Failure in the middle of redo by the functions will cause the tx status to be set to X and ignored (TM can also choose to die).
- 2. Function is called, instructed to perform undo
- 3. Function wants to performs S2', records undo step (S2'' == S2) to TM
- 4. Function performs S2'
- 5. Function wants to perform S1', records undo step (S1'' == S1) to TM
- 6. Function performs S1'
- 7. TM marks tx status to U
Fault tolerance when performing redo
This case is handled similar to when performing redo, except TM will only redo tx's with status C. At the beginning of redo TM will set tx status to d. And at the end tx status will be set to C again.
Fault tolerance when performing rollback or recovery
During startup, TM will list all tx's with status A, u, d. It will then try to roll back each of this tx, by performing its undo (or redo) steps so that tx status is (respectively) R, C, U. These are all performed by the same TM instance (since there is no client Riap request involved yet). Crash in the middle of recovery can be repeated since tx status are still A/u/d. That is why steps need to be idempotent because they might be called more than once.
Failure in the steps can cause TM to set transaction status to X (inconsistent, ignored).
SEE ALSO
AUTHOR
Steven Haryanto <stevenharyanto@gmail.com>
COPYRIGHT AND LICENSE
This software is copyright (c) 2012 by Steven Haryanto.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.