Safe Haskell | None |
---|---|
Language | Haskell2010 |
Ouroboros.Consensus.Storage.LedgerDB.V1.DbChangelog
Description
A DbChangelog
is the component of the
LedgerDB
implementation that
responsible for:
- Maintaining the last k in-memory ledger states without on-disk parks.
- Holding the in-memory ledger state that a snapshot would write to the disk.
- Providing sequences of differences from said state to any requested state
in the last k ledger states, which combined with the values in the
BackingStore
, can provideLedgerTable
s at any of those ledger states.
A DbChangelog
is said to be anchored at a BackingStore
when
the slot of the values in the backing store is the predecesor of the slots in
the sequence of differences, with the overall sequence of slots being defined
by the blocks on the chain.
This design is based on the technical report "Storing the Cardano ledger state on disk: API design concepts" by Duncan Coutts and Douglas Wilson.
Implementation details
The DbChangelog
is in fact a pure data structure, of which the LedgerDB
will carry a value in some mutable state, see
LedgerDBState
.
Carrying states
The DbChangelog
contains an instantiation of the AnchoredSeq
data type to
hold the last k in-memory ledger states. This data type is impemented
using the finger tree data structure and has the following time
complexities:
- Appending a new ledger state to the end in constant time.
- Rolling back to a previous ledger state in logarithmic time.
- Looking up a past ledger state by its point in logarithmic time.
One can think of AnchoredSeq
as a Seq
from Data.Sequence with a custom
finger tree measure allowing for efficient lookups by point, combined with
an anchor. When fully saturated, the sequence will contain k ledger
states. In case of a complete rollback of all k blocks and thus ledger
states, the sequence will become empty. A ledger state is still needed, i.e.,
one corresponding to the most recent immutable block that cannot be rolled
back. The ledger state at the anchor plays this role.
Appending in-memory states
When a new ledger state is appended to a fully saturated DbChangelog
(i.e.
that contains k states), the ledger state at the anchor is dropped and
the oldest element in the sequence becomes the new anchor, as it has become
immutable. Note that we only refer here to the in-memory states, as the diffs
from the anchor will remain in the DbChangelog
until flushing happens. This
maintains the invariant that only the last k in-memory ledger states are
stored, excluding the ledger state at the anchor. This means that in
practice, k+1 ledger states will be kept in memory. When the
DbChangelog
contains fewer than k elements, new ones are appended
without shifting the anchor until it is saturated.
Getting and appending differences
For the differences, the DbChangelog
contains a SeqDiffMK
(see
Ouroboros.Consensus.Storage.LedgerDB.V1.DiffSeq) which in turn is just an
instantiation of a root-measured finger tree (see
fingertree-rm)
which is a specialization of the finger trees that carries a root-measure
which is the monoidal sum of all the measures of all the elements.
This allows us to very efficiently lookup the combined difference of the
whole DbChangelog
, while still having a good complexity when splitting this
tree.
When a block is to be applied to a ledger state (which must be in the
DbChangelog
or application would directly fail), applying the root-measure
of the sub-sequence of differences from the backing store slot up to the
requested slot to the values read from the backing store will provide the
LedgerTable
s needed for applying the block.
Once a new ledger state is appended to the DbChangelog
, said ledger state
will carry DiffMK
tables (obtained by diffing the input and output ledger
tables when calling the Ledger rules). Adding those differences to the
DbChangelog
is just a matter of extending the carried SeqDiffMK
.
Only when flushing, the SeqDiffMK
is pruned, by extracting the differences
in between the last flushed state and the current immutable tip.
Synopsis
- data DbChangelog (l โท (Type โ Type โ Type) โ Type) = DbChangelog {
- changelogLastFlushedState โท !(l EmptyMK)
- changelogDiffs โท !(LedgerTables l SeqDiffMK)
- changelogStates โท !(AnchoredSeq (WithOrigin SlotNo) (l EmptyMK) (l EmptyMK))
- type DbChangelog' blk = DbChangelog (ExtLedgerState blk)
- empty โท (HasLedgerTables l, GetTip l) โ l EmptyMK โ DbChangelog l
- pruneToImmTipOnly โท โ (l โท LedgerStateKind). GetTip l โ DbChangelog l โ DbChangelog l
- reapplyThenPush โท (Monad m, ApplyBlock l blk) โ LedgerDbCfg l โ blk โ KeySetsReader m l โ DbChangelog l โ m (DbChangelog l)
- withKeysReadSets โท (HasLedgerTables l, Monad m, GetTip l) โ l EmptyMK โ KeySetsReader m l โ DbChangelog l โ LedgerTables l KeysMK โ (l ValuesMK โ m a) โ m a
- type KeySetsReader (m โท Type โ Type) (l โท (Type โ Type โ Type) โ Type) = l EmptyMK โ LedgerTables l KeysMK โ m (UnforwardedReadSets l)
- data UnforwardedReadSets (l โท LedgerStateKind) = UnforwardedReadSets {
- ursSeqNo โท !(WithOrigin SlotNo)
- ursValues โท !(LedgerTables l ValuesMK)
- ursKeys โท !(LedgerTables l KeysMK)
- readKeySets โท IOLike m โ LedgerBackingStore m l โ KeySetsReader m l
- readKeySetsWith โท Monad m โ LedgerBackingStoreValueHandle m l โ KeySetsReader m l
- trivialKeySetsReader โท (Monad m, LedgerTablesAreTrivial l) โ WithOrigin SlotNo โ KeySetsReader m l
- data RewindReadFwdError = RewindReadFwdError {
- rrfBackingStoreAt โท !(WithOrigin SlotNo)
- rrfDbChangelogAt โท !(WithOrigin SlotNo)
- forwardTableKeySets โท โ (l โท LedgerStateKind). (HasLedgerTables l, GetTip l) โ DbChangelog l โ UnforwardedReadSets l โ Either RewindReadFwdError (LedgerTables l ValuesMK)
- forwardTableKeySets' โท โ (l โท LedgerStateKind). HasLedgerTables l โ WithOrigin SlotNo โ LedgerTables l SeqDiffMK โ UnforwardedReadSets l โ Either RewindReadFwdError (LedgerTables l ValuesMK)
- data DiffsToFlush (l โท LedgerStateKind) = DiffsToFlush {
- toFlushDiffs โท !(LedgerTables l DiffMK)
- toFlushState โท !(l EmptyMK, l EmptyMK)
- toFlushSlot โท !SlotNo
- splitForFlushing โท โ (l โท LedgerStateKind). (GetTip l, HasLedgerTables l) โ DbChangelog l โ (Maybe (DiffsToFlush l), DbChangelog l)
- anchor โท DbChangelog l โ l EmptyMK
- current โท GetTip l โ DbChangelog l โ l EmptyMK
- flushableLength โท โ (l โท LedgerStateKind). (HasLedgerTables l, GetTip l) โ DbChangelog l โ Word64
- getPastLedgerAt โท (HasHeader blk, IsLedger l, HeaderHash l ~ HeaderHash blk, StandardHash l, HasLedgerTables l) โ Point blk โ DbChangelog l โ Maybe (l EmptyMK)
- rollback โท โ blk (l โท LedgerStateKind). (HasHeader blk, IsLedger l, HeaderHash l ~ HeaderHash blk, StandardHash l, HasLedgerTables l) โ Point blk โ DbChangelog l โ Maybe (DbChangelog l)
- snapshots โท DbChangelog l โ [(Word64, l EmptyMK)]
- tip โท โ (l โท LedgerStateKind). GetTip l โ DbChangelog l โ Point l
- volatileStatesBimap โท Anchorable (WithOrigin SlotNo) a b โ (l EmptyMK โ a) โ (l EmptyMK โ b) โ DbChangelog l โ AnchoredSeq (WithOrigin SlotNo) a b
- extend โท (GetTip l, HasLedgerTables l) โ l DiffMK โ DbChangelog l โ DbChangelog l
- immutableTipSlot โท โ (l โท LedgerStateKind). GetTip l โ DbChangelog l โ WithOrigin SlotNo
- isSaturated โท โ (l โท LedgerStateKind). GetTip l โ SecurityParam โ DbChangelog l โ Bool
- maxRollback โท โ (l โท LedgerStateKind). GetTip l โ DbChangelog l โ Word64
- prune โท โ (l โท LedgerStateKind). GetTip l โ LedgerDbPrune โ DbChangelog l โ DbChangelog l
- rollbackN โท โ (l โท LedgerStateKind). (GetTip l, HasLedgerTables l) โ Word64 โ DbChangelog l โ Maybe (DbChangelog l)
- rollbackToAnchor โท โ (l โท LedgerStateKind). (GetTip l, HasLedgerTables l) โ DbChangelog l โ DbChangelog l
- rollbackToPoint โท โ (l โท LedgerStateKind). (StandardHash l, GetTip l, HasLedgerTables l) โ Point l โ DbChangelog l โ Maybe (DbChangelog l)
- reapplyThenPush' โท ApplyBlock l blk โ LedgerDbCfg l โ blk โ KeySetsReader Identity l โ DbChangelog l โ DbChangelog l
- reapplyThenPushMany' โท โ (l โท LedgerStateKind) blk. (ApplyBlock l blk, LedgerTablesAreTrivial l) โ LedgerDbCfg l โ [blk] โ DbChangelog l โ DbChangelog l
- switch โท (ApplyBlock l blk, Monad m) โ LedgerDbCfg l โ Word64 โ [blk] โ KeySetsReader m l โ DbChangelog l โ m (Either ExceededRollback (DbChangelog l))
- switch' โท โ (l โท LedgerStateKind) blk. (ApplyBlock l blk, LedgerTablesAreTrivial l) โ LedgerDbCfg l โ Word64 โ [blk] โ DbChangelog l โ Maybe (DbChangelog l)
The DbChangelog
data DbChangelog (l โท (Type โ Type โ Type) โ Type) Source #
Holds a sequence of split ledger states, where the in-memory part is in a
sequence and the on-disk part is represented by a sequence of differences
that need a BackingStore
as an anchor point.
We illustrate its contents below, where k = 3
(for a state Li
, the
corresponding set of differences is Di
):
lastFlushed | states | tableDiffs |
---|---|---|
L0 | L0 :> [ ] | [ ] |
L0 | L0 :> [ L1 ] | [ D1 ] |
L0 | L0 :> [ L1, L2 ] | [ D1, D2 ] |
L0 | L0 :> [ L1, L2, L3 ] | [ D1, D2, D3 ] |
L0 | L1 :> [ L2, L3, L4 ] | [ D1, D2, D3, D4 ] |
L0 | L2 :> [ L3, L4, L5 ] | [ D1, D2, D3, D4, D5 ] -- (*) |
L2 | L2 :> [ L3, L4, L5 ] | [ D3, D4, D5 ] -- flush (**) |
L2 | L3 :> [ L4, L5, L6 ] | [ D3, D4, D5, D6 ] |
Notice that length states
is usually k
except when rollbacks or data
corruption take place and will be less than k
when we just loaded a
snapshot. We cannot roll back more than k
blocks. This means that after a
rollback of k
blocks at (*)
, the changelog will look something like this:
L0 | L2 :> [ ] | [ D1, D2 ] |
And a rollback of k
blocks at (**)
will look something like this:
L2 | L2 :> [ ] | [ ] |
Notice how the states list always contains the in-memory state of the anchor, but the table differences might not contain the differences for that anchor if they have been flushed to the backend.
As said above, this DbChangelog
has to be coupled with a BackingStore
which provides the pointers to the on-disk data.
Constructors
DbChangelog | |
Fields
|
Instances
type DbChangelog' blk = DbChangelog (ExtLedgerState blk) Source #
Construction
empty โท (HasLedgerTables l, GetTip l) โ l EmptyMK โ DbChangelog l Source #
Creates an empty DbChangelog
.
pruneToImmTipOnly โท โ (l โท LedgerStateKind). GetTip l โ DbChangelog l โ DbChangelog l Source #
When creating a new DbChangelog
, we should load whichever snapshot we
find and then replay the chain up to the immutable tip. When we get there,
the DbChangelog
will have a k
-long sequence of states, which all come
from immutable blocks, so we just prune all of them and only keep the last
one as an anchor, as it is the immutable tip. Then we can proceed with
opening the VolatileDB.
If we didn't do this step, the DbChangelog
would accept rollbacks into the
immutable part of the chain, which must never be possible.
lastFlushed | states | tableDiffs |
---|---|---|
L0 | L0 :> [ L1, L2, L3, L4 ] | [ D1, D2, D3, D4 ] |
>> pruneToImmTipOnly | ||
L0 | L4 :> [ ] | [ D1, D2, D3, D4 ] |
Updating a DbChangelog
Applying blocks
Applying blocks to the DbChangelog
will extend it if the result is
successful.
In order to do so, we first need to find the particular block, then prepare the ledger tables by hydrating the ledger state and then finally call the ledger, which might throw errors.
reapplyThenPush โท (Monad m, ApplyBlock l blk) โ LedgerDbCfg l โ blk โ KeySetsReader m l โ DbChangelog l โ m (DbChangelog l) Source #
Apply a block on top of the ledger state and extend the DbChangelog with the result ledger state.
Hydrating the ledger state
When trying to get tables at a specific ledger state, we must follow a process we call hydrating the ledger state. This process consists of 3 steps:
- Rewind the requested keys to the beginning of the DbChangelog. For UTxO entries this just means that we record at which slot the db changelog was when rewinding.
- Query the
BackingStore
for the actual values for the requested keys. - Forward those values by applying the differences in the
DbChangelog
up to the requested point.
withKeysReadSets โท (HasLedgerTables l, Monad m, GetTip l) โ l EmptyMK โ KeySetsReader m l โ DbChangelog l โ LedgerTables l KeysMK โ (l ValuesMK โ m a) โ m a Source #
Read
type KeySetsReader (m โท Type โ Type) (l โท (Type โ Type โ Type) โ Type) = l EmptyMK โ LedgerTables l KeysMK โ m (UnforwardedReadSets l) Source #
data UnforwardedReadSets (l โท LedgerStateKind) Source #
Constructors
UnforwardedReadSets | |
Fields
|
readKeySets โท IOLike m โ LedgerBackingStore m l โ KeySetsReader m l Source #
readKeySetsWith โท Monad m โ LedgerBackingStoreValueHandle m l โ KeySetsReader m l Source #
trivialKeySetsReader โท (Monad m, LedgerTablesAreTrivial l) โ WithOrigin SlotNo โ KeySetsReader m l Source #
Forward
data RewindReadFwdError Source #
The DbChangelog and the BackingStore got out of sync. This is a critical error, we cannot recover from this.
Constructors
RewindReadFwdError | |
Fields
|
Instances
Show RewindReadFwdError Source # | |
Defined in Ouroboros.Consensus.Storage.LedgerDB.V1.DbChangelog Methods showsPrec โท Int โ RewindReadFwdError โ ShowS # show โท RewindReadFwdError โ String # showList โท [RewindReadFwdError] โ ShowS # |
forwardTableKeySets โท โ (l โท LedgerStateKind). (HasLedgerTables l, GetTip l) โ DbChangelog l โ UnforwardedReadSets l โ Either RewindReadFwdError (LedgerTables l ValuesMK) Source #
forwardTableKeySets' โท โ (l โท LedgerStateKind). HasLedgerTables l โ WithOrigin SlotNo โ LedgerTables l SeqDiffMK โ UnforwardedReadSets l โ Either RewindReadFwdError (LedgerTables l ValuesMK) Source #
Flushing
data DiffsToFlush (l โท LedgerStateKind) Source #
A container for differences that are inteded to be flushed to a
BackingStore
Constructors
DiffsToFlush | |
Fields
|
splitForFlushing โท โ (l โท LedgerStateKind). (GetTip l, HasLedgerTables l) โ DbChangelog l โ (Maybe (DiffsToFlush l), DbChangelog l) Source #
" Flush " the DbChangelog
by splitting it into the diffs that should be
flushed and the new DbChangelog
.
lastFlushed | states | tableDiffs |
---|---|---|
L2 | L3 :> [ L4, L5, L6 ] | [ D2, D3, D4, D5, D6 ] |
>> splitForFlushing | ||
L2 | -- | [ D2, D3 ] -- this is a |
L3 | L3 :> [ L4, L5, L6 ] | [ D4, D5, D6 ] |
Queries
anchor โท DbChangelog l โ l EmptyMK Source #
The ledger state at the anchor of the Volatile chain (i.e. the immutable tip).
current โท GetTip l โ DbChangelog l โ l EmptyMK Source #
The ledger state at the tip of the chain
flushableLength โท โ (l โท LedgerStateKind). (HasLedgerTables l, GetTip l) โ DbChangelog l โ Word64 Source #
How many diffs we can flush to the backing store?
NOTE: This will be wrong once we have more than one table.
getPastLedgerAt โท (HasHeader blk, IsLedger l, HeaderHash l ~ HeaderHash blk, StandardHash l, HasLedgerTables l) โ Point blk โ DbChangelog l โ Maybe (l EmptyMK) Source #
rollback โท โ blk (l โท LedgerStateKind). (HasHeader blk, IsLedger l, HeaderHash l ~ HeaderHash blk, StandardHash l, HasLedgerTables l) โ Point blk โ DbChangelog l โ Maybe (DbChangelog l) Source #
snapshots โท DbChangelog l โ [(Word64, l EmptyMK)] Source #
All snapshots currently stored by the ledger DB (new to old)
This also includes the snapshot at the anchor. For each snapshot we also return the distance from the tip.
tip โท โ (l โท LedgerStateKind). GetTip l โ DbChangelog l โ Point l Source #
Reference to the block at the tip of the chain
volatileStatesBimap โท Anchorable (WithOrigin SlotNo) a b โ (l EmptyMK โ a) โ (l EmptyMK โ b) โ DbChangelog l โ AnchoredSeq (WithOrigin SlotNo) a b Source #
Transform the underlying volatile AnchoredSeq
using the given functions.
๐งช Testing
Internal
extend โท (GetTip l, HasLedgerTables l) โ l DiffMK โ DbChangelog l โ DbChangelog l Source #
Extending the DbChangelog with a valid ledger state.
L2 | L2 :> [ L3, L4, L5 ] | [ D3, D4, D5 ] |
>> extend L6 (D6) | ||
L2 | L2 :> [ L3, L4, L5, L6 ] | [ D3, D4, D5, D6 ] |
immutableTipSlot โท โ (l โท LedgerStateKind). GetTip l โ DbChangelog l โ WithOrigin SlotNo Source #
isSaturated โท โ (l โท LedgerStateKind). GetTip l โ SecurityParam โ DbChangelog l โ Bool Source #
Have we seen at least k
blocks?
maxRollback โท โ (l โท LedgerStateKind). GetTip l โ DbChangelog l โ Word64 Source #
How many blocks can we currently roll back?
prune โท โ (l โท LedgerStateKind). GetTip l โ LedgerDbPrune โ DbChangelog l โ DbChangelog l Source #
Prune oldest ledger states until at we have at most k
in the DbChangelog,
excluding the one stored at the anchor.
lastFlushed | states | tableDiffs |
---|---|---|
L0 | L0 :> [ L1, L2, L3, L4 ] | [ D1, D2, D3, D4 ] |
>> prune (SecurityParam 3) | ||
L0 | L2 :> [ L3, L4 ] | [ D1, D2, D3, D4 ] |
rollbackN โท โ (l โท LedgerStateKind). (GetTip l, HasLedgerTables l) โ Word64 โ DbChangelog l โ Maybe (DbChangelog l) Source #
Rollback n
ledger states.
Returns Nothing
if maximum rollback (usually k
, but can be less on
startup or under corruption) is exceeded.
lastFlushed | states | tableDiffs |
---|---|---|
L2 | L3 :> [ L4, L5, L6 ] | [ D2, D3, D4, D5, D6 ] |
>> rollback 3 | ||
L2 | L3 :> [ ] | [ D2, D3 ] |
rollbackToAnchor โท โ (l โท LedgerStateKind). (GetTip l, HasLedgerTables l) โ DbChangelog l โ DbChangelog l Source #
Rollback the volatile states up to the volatile anchor.
rollbackToPoint โท โ (l โท LedgerStateKind). (StandardHash l, GetTip l, HasLedgerTables l) โ Point l โ DbChangelog l โ Maybe (DbChangelog l) Source #
Roll back the volatile states up to the specified point.
Testing
reapplyThenPush' โท ApplyBlock l blk โ LedgerDbCfg l โ blk โ KeySetsReader Identity l โ DbChangelog l โ DbChangelog l Source #
reapplyThenPushMany' โท โ (l โท LedgerStateKind) blk. (ApplyBlock l blk, LedgerTablesAreTrivial l) โ LedgerDbCfg l โ [blk] โ DbChangelog l โ DbChangelog l Source #
switch โท (ApplyBlock l blk, Monad m) โ LedgerDbCfg l โ Word64 โ [blk] โ KeySetsReader m l โ DbChangelog l โ m (Either ExceededRollback (DbChangelog l)) Source #
switch' โท โ (l โท LedgerStateKind) blk. (ApplyBlock l blk, LedgerTablesAreTrivial l) โ LedgerDbCfg l โ Word64 โ [blk] โ DbChangelog l โ Maybe (DbChangelog l) Source #