Bank transfers without distributed transactions
Let there be multiple physically distributed databases where each database contains ("owns") a set
of bank accounts. A bank account is owned by exactly one participating database.
It is possible to achieve high performance, strongly consistent monetary transfers between accounts
with the following properties:
- All transactions are local to a single database, there is no need for
distributed transactions
(atomic commitment protocols) like 2PC.
- While a thread executes a database transaction on one of the participating databases, there
are no messages sent or received from other nodes on the network. Therefore failure of other
nodes or a partition in the network never blocks a transaction.
- Every transaction involves double entry bookkeeping []
on each participating database, ensuring the ledger balances.
- System wide consistency doesn't depend on transaction durability, apart from the constraint
that persistent queue records are not sent to peers until
they are made durable.
For example, a database can even crash and recover back to an earlier state - i.e. where some
of its previously committed local transactions have been lost.
The transfers that have been committed is a well-defined function of the states across all the
participating databases.
- Performance is extremely high. Transaction throughput depends on I/O bandwidth, not the
network latency. The approach minimises the time taken for transfers to complete.
- It can even impose the constraint that no accounts are overdrawn (have negative balances).
This can be achieved by having each participating database contain:
- a persistent queue of deposit records for each of its peers.
A deposit record represent a promise to transfer money to an account on the peer.
- a sequence number for the number of deposit records it has processed from each peer
persistent queue.
The persistent queues are regarded as holding money (just like bank accounts) for the purposes of
double entry bookkeeping.
A local transaction on one of the participating databases can atomically do any combination of the
following as long as it conforms to double entry book-keeping (a transaction never gains or loses
money overall):
- Add or remove money from the balances of the bank accounts it owns
- Send money to peer accounts by pushing new deposit records on its local persistent
queues
- Receive money from peers by incrementing local sequence numbers recording the number of
deposit records on peer persistent queues that have been processed.
It is easy to impose the constraint that no accounts are overdrawn (have negative balances) because
we only have deposit records (a promise to send money), never a promise to receive money.
Example
The following example shows how $10 is transferred between two banks without needing distributed transactions.
Initial state
Let bank B1 own account A1 and bank B2 own account A2.
Initially both accounts have a balance of $100.
Bank B1 has a persistent queue Q2 which holds deposit records to be sent to bank B2.
Q2 is initially empty.
Bank B2 has a persistent sequence number NQ2 which records the number of deposit records
in Q2 it has processed.
Local atomic transaction on bank B1
In a local atomic transaction on the database of bank B1, $10 is removed from A1 and converted into a
deposit record which is appended to Q2.
Local atomic transaction on bank B2
In a local atomic transaction on the database of bank B2, the deposit record is marked as processed by incrementing
NQ2 and $10 is added to A2.