Replies: 10 comments 49 replies
-
Thanks for creating this thread @roysc.
|
Beta Was this translation helpful? Give feedback.
-
Interface for historical versionsThis section is correct - it's exactly what we were testing. See here for example: #8297 (comment), and other threads and tests in this discussion. |
Beta Was this translation helpful? Give feedback.
-
I have not seen any justification nor use cases for why we should be storing |
Beta Was this translation helpful? Give feedback.
-
I have expressed this elsewhere, but I believe we should allow direct access to both For existing modules, I think we should expose a |
Beta Was this translation helpful? Give feedback.
-
According to the ADR, the SC layer will not need historical versions, and the SMT will be pruned of orphaned nodes, leaving only the current version of the tree. However, if a query of past state requests a proof, it seems that we will need the old version to generate the proof. Am I misunderstanding @robert-zaremba? |
Beta Was this translation helpful? Give feedback.
-
@roysc , I created a PR to update ADR-40 with regards to Multistore and database setup for |
Beta Was this translation helpful? Give feedback.
-
To support this implementation, I've drafted a new interface for the backing key-value DB. Please see the new synopsis here (for simplicity this is currently within the
There are some design questions still to resolve; chiefly, how should
I arrived at this design after reviewing tendermint/tendermint#6425 and tendermint/tendermint#6032, and it may rightly belong to another ADR, but as this work depends on it I wanted to propose it here and get any feedback before moving forward. |
Beta Was this translation helpful? Give feedback.
-
@roysc , @i-norden Let's have a sync about the ADR-40 implementation. Do you need anything from the SDK team? In particular, I would like to check on and consult with you:
|
Beta Was this translation helpful? Give feedback.
-
@i-norden and I want to propose storing just
One motivation is that this would work much better with IPLD, as it requires storing just the proper content hash for a value. It would also avoid ambiguity in the case where a stored I'll open a PR for this as well. (edit: PR #9680) |
Beta Was this translation helpful? Give feedback.
-
The work is being pushed in a sequence of PRs, most of which are ready for review. The dependency looks something like:
|
Beta Was this translation helpful? Give feedback.
-
We'd like to start a conversation on how to implement ADR-040, present an overview of our current plans, and resolve some design questions. Here we cover:
MapStore
Interface for historical versions
The backend DB will need to expose an interface for accessing and viewing historical versions of records. The simplest way to do this may be to supply a new interface type representing a read-only DB, and a new method like
DB.AtVersion(uint64) (ReadOnlyDB, error)
. It may also be convenient to provide aDB.GetAt([]byte, uint64)
method to access a single record.This can implemented like so:
DB.NewTransactionAt()
is used to view DB state at a version.Checkpoint
can be created for each commit in a path corresponding to the version integer, and then opened as a read-only copy of the DB.ABCI state-sync snapshots
Approach
We will attempt to make all needed changes conform to the same interface used in the
Snapshot
/Restore
methods implemented byrootmulti.Store
. This is the only type that will need to be serialized for snapshots, hence the only type to implement theSnapshotter
interface. Adapting this pattern for the new backends will mostly be a matter of defining the serialization units for export/import.The most straightforward way to do this is to just define a new
SnapshotItem
message type encapsulating a KV-pair, and stream all entries from a store in this format. This will allow the SDK stores to be completely agnostic to the backend used, while the existing export/import procedure will remain mostly the same.Interface
We can define new generic interface types to export and import encoded messages, and implement these for all relevant persistent stores. These will mimic the current export interface for IAVL, but deal directly with
SnapshotItem
s (or another generic type). The existing logic can then be refactored easily.Implementation
BadgerDB
As described above, he
DB.NewTransactionAt
method provides a view of the DB at a specific version. In "managed" mode, the version (timestamp) can be an arbitrary integer.CommitAt
also allows restoring state at a past version, although we seem to have no use case for this as of yet.RocksDB
RocksDB
Snapshot
s are an ephemeral view of the DB at a specific point in time. This is enough to create a snapshot directly after a commit.Checkpoint
s as described above.Encoding
We define the following new protobuf message and add it as a subtype of
SnapshotItem
:The
Exporter
/Importer
implementations for BadgerDB and RocksDB both stream instances ofSnapshotKVItem
. TheIAVL
handling code can also be wrapped so that therootmulti
code only has to deal with this generic interface.SMT/MapStore updates
We will need to add some functionality to the LazyLedger SMT and the
MapStore
interface it uses, to expose needed features from the backing DB.Batch support
If we want to implement efficient batched writes to the SMT, that could be supported in the following way. The tree will need to be able to read as well as write to the batch itself while building it, using something closer to a transaction object.
WriteBatch
does not supportGet
s, but, as it is a relatively simple wrapper around a transaction, we could just useTxn
.These writable batch types would be wrapped in the
MapStore
interface. The SMT would need a new interface type to represent the batch (e.g.WriteBatch
). This would wrap a derivedSparseMerkleTree
object using the transaction object as itsMapStore
. All normal SMT operations would then be available on the batch.Versioning and snapshots
Creating state sync snapshots is not required for SMT data.
Versioning may not be needed either (?); however, if it is desired, it could be implemented with a
MapStore.GetAt([]byte, uint64)
method, which forwards to aDB.GetAt()
method as described above.Decoupling SC and SS
KVStore
sIn the state machine, we need to settle on the pattern used to decouple the state commitment and storage concerns. From the ADR-040 proposal, the store buckets consist of:
A few options for how implement this are outlined below. The best course of action will depend on the order of priorities.
If we want to prioritize SDK user experience, we should wrap this logic in a single
KVStore
interface which exposes storage data as KV pairs and only exposes SC data as Merkle root hashes (option no. 1 below). This only makes sense if users are not expected to need to access or iterate over hashes of individual records or the inverted index mappings. (This is the approach taken in the open SMT store PR.)If the higher priority is to provide SDK users full visibility and control of all component KV buckets, these should be left as loosely coupled as possible (no. 2). The SC, SS, and index buckets will only be used in tandem at the multi-store level. The multi-store will then expose these as separate
CommitKVStore
s, so that the user is still able to unambiguously access all underlying data, including data and hashed records, and wrap each store in atracekv.Store
,gaskv.Store
or another layer. However, if the multi-store is planned to be removed in the future, users will then be responsible for managing the new state machine semantics unless a new wrapper type is created.We suggest a third option if all of the above are considered high priorities: introduce a new interface which expands on
KVStore
(no. 3). Ideally, this will make as much as possible of the underlying store structure available for tooling, while still presenting an SDK interface that is friendly to users.1. Write a unified
KVStore
that generates bucket entries and directs writes/reads to both SS and SC.Pros:
CommitKVStore
for each substoreCons:
Set
andDelete
have side effects, rather than simply setting or deleting the provide KV pair they generate two other KV pairs to set/delete2. Maintain separate
KVStore
s for SC and SSPros:
Set
andDelete
operations remain pureCons:
3. Create a new KVStore subtype
With option no. 1 above, the primary issue is not being able to wrap the underlying stores. With no. 2, the primary issue is that we disrupt the current 1-to-1 mapping of
StoreKey
to a KVStore. We can avoid both by defining a new interface and some supporting types:Pros:
Cons:
StoreKind
enumExample synopsis:
For all purposes other than wrapping, we can use the
StateStore
as any otherKVStore
. Methods that need to wrap an underlying store after loading it into theStateStore
will need to use the new methods (e.g.MultiStore.GetKVStore
when tracing or listening is enabled).cc/ @robert-zaremba @marbar3778 @i-norden
Beta Was this translation helpful? Give feedback.
All reactions