-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ADR 040: New DB interface (#9573)
<!-- The default pull request template is for types feat, fix, or refactor. For other templates, add one of the following parameters to the url: - template=docs.md - template=other.md --> ## New DB interface to replace `tm-db` This introduces a new interface to wrap the backend KV store DB while supporting versioning and ACID transactions. Part of [ADR-040](https://github.com/cosmos/cosmos-sdk/blob/eb7d939f86c6cd7b4218492364cdda3f649f06b5/docs/architecture/adr-040-storage-and-smt-state-commitments.md) changes. This has been revised to consist of only the interface types. All implementations and utilities will be in follow-up PRs. Partially resolves: vulcanize#2 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - N/A - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [x] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
- Loading branch information
Showing
4 changed files
with
214 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
go 1.15 | ||
|
||
module github.com/cosmos/cosmos-sdk/db | ||
|
||
require ( | ||
github.com/stretchr/testify v1.7.0 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
package db | ||
|
||
import "errors" | ||
|
||
var ( | ||
// ErrBatchClosed is returned when a closed or written batch is used. | ||
ErrBatchClosed = errors.New("batch has been written or closed") | ||
|
||
// ErrKeyEmpty is returned when attempting to use an empty or nil key. | ||
ErrKeyEmpty = errors.New("key cannot be empty") | ||
|
||
// ErrValueNil is returned when attempting to set a nil value. | ||
ErrValueNil = errors.New("value cannot be nil") | ||
|
||
// ErrVersionDoesNotExist is returned when a DB version does not exist. | ||
ErrVersionDoesNotExist = errors.New("version does not exist") | ||
|
||
// ErrOpenTransactions is returned when open transactions exist which must | ||
// be discarded/committed before an operation can complete. | ||
ErrOpenTransactions = errors.New("open transactions exist") | ||
|
||
// ErrReadOnly is returned when a write operation is attempted on a read-only transaction. | ||
ErrReadOnly = errors.New("cannot modify read-only transaction") | ||
|
||
// ErrInvalidVersion is returned when an operation attempts to use an invalid version ID. | ||
ErrInvalidVersion = errors.New("invalid version") | ||
) | ||
|
||
// DBConnection represents a connection to a versioned database. | ||
// Records are accessed via transaction objects, and must be safe for concurrent creation | ||
// and read and write access. | ||
// Past versions are only accessible read-only. | ||
type DBConnection interface { | ||
// Opens a read-only transaction at the current working version. | ||
Reader() DBReader | ||
|
||
// Opens a read-only transaction at a specified version. | ||
// Returns ErrVersionDoesNotExist for invalid versions. | ||
ReaderAt(uint64) (DBReader, error) | ||
|
||
// Opens a read-write transaction at the current version. | ||
ReadWriter() DBReadWriter | ||
|
||
// Opens a write-only transaction at the current version. | ||
Writer() DBWriter | ||
|
||
// Returns all saved versions | ||
Versions() (VersionSet, error) | ||
|
||
// Saves the current contents of the database and returns the next version ID, which will be | ||
// `Versions().Last()+1`. | ||
// Returns an error if any open DBWriter transactions exist. | ||
// TODO: rename to something more descriptive? | ||
SaveNextVersion() (uint64, error) | ||
|
||
// Attempts to save database at a specific version ID, which must be greater than or equal to | ||
// what would be returned by `SaveNextVersion`. | ||
// Returns an error if any open DBWriter transactions exist. | ||
SaveVersion(uint64) error | ||
|
||
// Deletes a saved version. Returns ErrVersionDoesNotExist for invalid versions. | ||
DeleteVersion(uint64) error | ||
|
||
// Close closes the database connection. | ||
Close() error | ||
} | ||
|
||
// DBReader is a read-only transaction interface. It is safe for concurrent access. | ||
// Callers must call Discard when done with the transaction. | ||
// | ||
// Keys cannot be nil or empty, while values cannot be nil. Keys and values should be considered | ||
// read-only, both when returned and when given, and must be copied before they are modified. | ||
type DBReader interface { | ||
// Get fetches the value of the given key, or nil if it does not exist. | ||
// CONTRACT: key, value readonly []byte | ||
Get([]byte) ([]byte, error) | ||
|
||
// Has checks if a key exists. | ||
// CONTRACT: key, value readonly []byte | ||
Has(key []byte) (bool, error) | ||
|
||
// Iterator returns an iterator over a domain of keys, in ascending order. The caller must call | ||
// Close when done. End is exclusive, and start must be less than end. A nil start iterates | ||
// from the first key, and a nil end iterates to the last key (inclusive). Empty keys are not | ||
// valid. | ||
// CONTRACT: No writes may happen within a domain while an iterator exists over it. | ||
// CONTRACT: start, end readonly []byte | ||
Iterator(start, end []byte) (Iterator, error) | ||
|
||
// ReverseIterator returns an iterator over a domain of keys, in descending order. The caller | ||
// must call Close when done. End is exclusive, and start must be less than end. A nil end | ||
// iterates from the last key (inclusive), and a nil start iterates to the first key (inclusive). | ||
// Empty keys are not valid. | ||
// CONTRACT: No writes may happen within a domain while an iterator exists over it. | ||
// CONTRACT: start, end readonly []byte | ||
// TODO: replace with an extra argument to Iterator()? | ||
ReverseIterator(start, end []byte) (Iterator, error) | ||
|
||
// Discards the transaction, invalidating any future operations on it. | ||
Discard() | ||
} | ||
|
||
// DBWriter is a write-only transaction interface. | ||
// It is safe for concurrent writes, following an optimistic (OCC) strategy, detecting any write | ||
// conflicts and returning an error on commit, rather than locking the DB. | ||
// This can be used to wrap a write-optimized batch object if provided by the backend implementation. | ||
type DBWriter interface { | ||
// Set sets the value for the given key, replacing it if it already exists. | ||
// CONTRACT: key, value readonly []byte | ||
Set([]byte, []byte) error | ||
|
||
// Delete deletes the key, or does nothing if the key does not exist. | ||
// CONTRACT: key readonly []byte | ||
Delete([]byte) error | ||
|
||
// Flushes pending writes and discards the transaction. | ||
Commit() error | ||
|
||
// Discards the transaction, invalidating any future operations on it. | ||
Discard() | ||
} | ||
|
||
// DBReadWriter is a transaction interface that allows both reading and writing. | ||
type DBReadWriter interface { | ||
DBReader | ||
DBWriter | ||
} | ||
|
||
// Iterator represents an iterator over a domain of keys. Callers must call Close when done. | ||
// No writes can happen to a domain while there exists an iterator over it, some backends may take | ||
// out database locks to ensure this will not happen. | ||
// | ||
// Callers must make sure the iterator is valid before calling any methods on it, otherwise | ||
// these methods will panic. This is in part caused by most backend databases using this convention. | ||
// | ||
// As with DBReader, keys and values should be considered read-only, and must be copied before they are | ||
// modified. | ||
// | ||
// Typical usage: | ||
// | ||
// var itr Iterator = ... | ||
// defer itr.Close() | ||
// | ||
// for ; itr.Valid(); itr.Next() { | ||
// k, v := itr.Key(); itr.Value() | ||
// ... | ||
// } | ||
// if err := itr.Error(); err != nil { | ||
// ... | ||
// } | ||
type Iterator interface { | ||
// Domain returns the start (inclusive) and end (exclusive) limits of the iterator. | ||
// CONTRACT: start, end readonly []byte | ||
Domain() (start []byte, end []byte) | ||
|
||
// Next moves the iterator to the next key in the database, as defined by order of iteration; | ||
// returns whether the iterator is valid. Once invalid, it remains invalid forever. | ||
Next() bool | ||
|
||
// Key returns the key at the current position. Panics if the iterator is invalid. | ||
// CONTRACT: key readonly []byte | ||
Key() (key []byte) | ||
|
||
// Value returns the value at the current position. Panics if the iterator is invalid. | ||
// CONTRACT: value readonly []byte | ||
Value() (value []byte) | ||
|
||
// Error returns the last error encountered by the iterator, if any. | ||
Error() error | ||
|
||
// Close closes the iterator, relasing any allocated resources. | ||
Close() error | ||
} | ||
|
||
// VersionSet specifies a set of existing versions | ||
type VersionSet interface { | ||
// Last returns the most recent saved version, or 0 if none. | ||
Last() uint64 | ||
// Count returns the number of saved versions. | ||
Count() int | ||
// Iterator returns an iterator over all saved versions. | ||
Iterator() VersionIterator | ||
// Equal returns true iff this set is identical to another. | ||
Equal(VersionSet) bool | ||
// Exists returns true if a saved version exists. | ||
Exists(uint64) bool | ||
} | ||
|
||
type VersionIterator interface { | ||
// Next advances the iterator to the next element. | ||
// Returns whether the iterator is valid; once invalid, it remains invalid forever. | ||
Next() bool | ||
// Value returns the version ID at the current position. | ||
Value() uint64 | ||
} |