Transactions and Concurrency - negue/LiteDB GitHub Wiki
LiteDB implements ACID transactions across multiple documents like a RDBMS database.
using(var db = new LiteDatabase("mydata.db"))
{
var col = db.GetCollection<Customer>("customer");
db.BeginTrans();
col.Insert(new Customer { Name = "John Doe" });
col.Insert(new Customer { Name = "Joanna Doe" });
col.Insert(new Customer { Name = "Foo Bar" });
db.Commit(); // all or none!
}
LiteDB use CacheService
to track pages that are read from disk. When a page change, cache keeps in memory this changes until an Commit()
is called. Until commit, no change occurs on data file, just on memory. If a Rollback()
are called, cache service discard all changed pages.
Transaction are required to LiteDB works. If omitted in write operations, like Insert()
, Update()
and Delete()
, LiteDB will create an auto transaction for each operation. This is a slow solution if you compare to use BeginTrans()
at start and Commit()
on end, because auto transaction writes on disk after each document change.
LiteDB supports multiple connections on same data file. To use this, you must use multiple instances of LiteDatabase
. Do not reuse same instance across mulitple threads/processes. LiteDatabase
implements IDisposable
, so you always need use and using
statement or call Dispose
method to guarantee that data file will be closed.
Concurrency is guaranteed by locking your data file using FileStream.Lock()
method. LiteDB implement 3 modes of locking:
-
UNLOCKED
- No locks on data file - any client can read or write data. -
SHARED
- When the first client callsBeginTrans()
data file locks to shared mode. In shared mode, any other can read but can't open new transactions.BeginTrans()
for others will be hold until first transaction finish. -
EXCLUSIVE
- WhenCommit()
all called, LiteDB start writing pages on disk. While this writing operation, data file are in exclusive lock. In exclusive lock, no others can even read data from disk. When finish write on disk, data file backs to unlocked state.
In StringConnection
you can define timeout for this hold locking. Default is 1 minute.
LiteDB supports nested transactions. You can have nested BeginTrans()
and Commit()
inside a main transaction. All nested transaction just are treated like a simple stack counter. Only main transaction Commit()
will write all changes in disk.
But, if a Rollback()
occurs during any transaction (main or nested) rollback command will be executed and transaction stack are cleared.
LiteDB throws exceptions and rollback transaction when has any error occurs during an write operation, like insert duplicate key.
using(var db = new LiteDatabase("mydata.db"))
{
var col = db.GetCollection("customer");
db.BeginTrans();
col.Insert(new Customer { Id = 1, Name = "John" });
db.BeginTrans(); // Stack +1
col.Insert(new Customer { Id = 2, Name = "Joana" });
db.Commit(); // Stack -1
col.Insert(new Customer { Id = 1, Name = "Foo Bar" }); // Causes index duplicate key
// An exception will be throw and a rollback will revert all data
// no data will be saved!
db.Commit();
}