Next: Using BTrees, Previous: Rules about Persistent Classes, Up: Tutorial
Elephant uses the Berkeley DB Transactional Data Store. This means most destructive operations need to be protected by transactions. By default Elephant does this:
* *auto-commit* => T
Most real applications will want to control their own transactions because you will want one or more read-modify-update operations to happen as an atomic unit. This is guaranteed by the use of a transaction, but auto commits will only protect each individual update irrespective of whether the read value has changed.
If, for some reason, you want to turn off an implicit transaction when no explicit transactions are in effect, you can do
* (setq *auto-commit* nil) => NIL
but note you'll have to wrap many of your operations in transactions or they will fail.
All database operations within the course of a transaction are ACID: atomic, consistent, isolated, and durable, if the transaction succeeds. After starting a transaction, you commit it, unless something fails, in which case you abort. The most common reason (other than programmer error) for transaction failure is deadlocks, which the Sleepycat deadlock resolver can resolve by signalling an error to force a retry.
All of this is packaged up in with-transaction
. It starts a
new transaction, executes the body, then tries to commit the
transaction. If anywhere along the way there is a deadlock, the
transaction is aborted, and it attempts to retry (a fixed number of
times) by re-executing the whole body.
* (with-transaction () (setf (slot1 foo) 123456789101112) (setf (slot2 foo) "onetwothree...")) => "onetwothree..."
You can manually start a transaction as follows:
* (start-transaction) => implementation-dependent
When you're done modifying the DB, commit with
* (commit-transaction)
or abort with
* (abort-transaction)
with-transaction
and start-transaction
may be nested
(e.g. child transactions are supported) but not interleaved. To
interleave transactions you have to manually maintain the transaction
handles. The persistent objects look for transactions in the
*current-transaction*
special.
* (setq *current-transaction* (controller-transaction-begin store-controller ))
To commit:
* (controller-transaction-commit store-controller *current-transaction*) NIL
If for some reason (like db error) you decide to abort, you can do so
via (controller-transaction-abort store-controller *current-transaction*)
.