This allows for special handling of certain transactions such as deletes and the
extension of Journal extents. For most other use cases it is appropriate to use
default() here.
When creating a transaction, locks typically need to be held to prevent two or more writers
trying to make conflicting mutations at the same time. LockKeys are used for this.
NOTE: Ordering is important here! The lock manager sorts the list of locks in a transaction
to acquire them in a consistent order, but there are special cases for the Filesystem lock and
the Flush lock.
The Filesystem lock is taken by every transaction and is done so first, as part of the TxnGuard.
The Flush lock is taken when we flush an LSM tree (e.g. an object store), and is held for
several transactions. As such, it must come first in the lock acquisition ordering, so that
other transactions using the Flush lock have the same ordering as in flushing.
Mutations in a transaction can be associated with an object so that when mutations are applied,
updates can be applied to in-memory structures. For example, we cache object sizes, so when a
size change is applied, we can update the cached object size.
The journal consists of these records which will be replayed at mount time. Within a
transaction, these are stored as a set which allows some mutations to be deduplicated and found
(and we require custom comparison functions below). For example, we need to be able to find
object size changes.