Caution: the Architecture Guide is not updated in lockstep with the code base and is not necessarily correct or complete for any specific release.
Transaction snapshots are an important mechanism for providing strong isolation guarantees in WiredTiger. WiredTiger will often maintain multiple versions of the users data throughout its lifetime, with which snapshots provide the mechanisms to ensure particular versions of that data are made accessible. Snapshots are importantly leveraged by WiredTiger to define the visibility rules for data such as isolating concurrent transactions from reading each others updates.
Within the context of a WiredTiger transaction, a snapshot ensures specific versions of a record are accessible based on the time the snapshot was created. In particular, these accessibility rules are defined by a transactions isolation level. Transactions more specifically use snapshots for the following isolation levels:
snapshot:
A snapshot is created at the beginning of a snapshot-level isolated transaction and is sustained throughout the entire life cycle of the transaction. In effect, a transaction using snapshot
isolation will only read versions of records before the transaction started. The default isolation level is snapshot
.read-committed
: Read-committed transactions ensure a transaction is able to see committed updates by other transactions at the time of reading. To achieve this, under a read-committed isolation, a snapshot is created when performing a search. The same snapshot is used for all subsequent reads until another search is called.read-uncommitted
: Read-uncommitted transactions do not take a snapshot, and allow dirty reads (uncommitted modifications made by other transactions).See Transactional applications for further details about how WiredTiger transactions utilize snapshots to create isolation guarantees and implement concurrency control.
Snapshots are implemented by capturing the global state of transactions in the system at the time of snapshot creation. Snapshots are managed within per-session transaction contexts, encapsulated by the WT_TXN
data structure. The main data points that encapsulate a given snapshot include:
An example of building out the aforementioned components of a snapshot can be seen in the below figure. This figure illustrates constructing a snapshot for a transaction with ID 8
. Consider that at the given time, the concurrent running transactions are 3
, 5
, 7
, 8
and the global current transaction ID is 9
.
When determining if a transaction can read a given record, WiredTiger applies a visibility function to discern whether the committed value is visible. When using snapshot
and read-committed
isolated transactions, WiredTiger's visibility function will leverage a transactions snapshot information to determine if a given record is visible. When determining if a given transaction ID is visible to a snapshot, WiredTiger applies the below checks in the following order:
The early ordering of checking whether the transaction ID sits outside the maximum and minimum bounds is an important detail since the concurrent transaction list can be empty i.e. snapshot was taken at a time when no concurrent transactions were running.
Checkpoints require a snapshot to write a transactionally consistent view of the data. A checkpoint maintains its own special global transaction and snapshot state. This being independent from the application's running transactions.
When checkpoints are running, application transactions using snapshots will also store the checkpoints transaction ID as a concurrent transaction. This allowing a snapshot to ignore any uncommitted changes the checkpoint has written to the metadata.
It is possible that a read-uncommitted reader can not reconstruct a full value. This is because a snapshot isolation writer can abort the updates in parallel and leave the reader with an update list that does not contain enough information to reconstruct the full value. It is impossible to distinguish if an aborted modify or update happened prior to the call of the function, or if it is being done in parallel. In this case, we will return back to the user with a WT_ROLLBACK error. Once the user reads the record again, the update chain will contain the aborted operations already.