NAME

Rinci::function::Transaction - Transactional system based on functions

VERSION

version 1.1.18

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 requests calling function F1, TM creates entry in its database

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 a rollback (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 is important that function (actually, all its steps and undo steps) be idempotent and checks for initial condition.

Failure in performing rollback (S1') will cause the transaction status to become U (unknown) and ignored. The transaction will be deleted during cleanup.

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 (after S1 is completed) is also handled like in previous case; this time S1 is already performed, so S1' needs to revert its side effects.

5. Function wants to do another action step (S2), records 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 marks the transaction

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.

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).

FAQ

Why is this useful?

The protocol is a pretty generic and simple way to build transactional system, even on heterogenous, multiuser environment. If the functions are written carefully, the system can be reliable. And even if some of the ACID properties are compromised due to lack of real RM, the system is still useful for its undo/redo capability.

SEE ALSO

Riap::Transaction

Rinci

Rinci::function::Undo

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.