Documentation
¶
Index ¶
- Variables
- func InternalRegisterDBCreator(backend BackendType, creator dbCreator, force bool)
- func IsKeyInDomain(key, start, end []byte) bool
- type BackendType
- type Batch
- type BatchCollector
- func (c *BatchCollector) Drain(dst Batch) error
- func (c *BatchCollector) Holding() bool
- func (c *BatchCollector) Len() int
- func (c *BatchCollector) NewBatch() Batch
- func (c *BatchCollector) Reset()
- func (c *BatchCollector) SetHolding(on bool)
- func (c *BatchCollector) Size() int
- func (c *BatchCollector) TakeRefusedIterator() bool
- func (c *BatchCollector) WriteTo(db DB) error
- type CollectingDB
- func (c *CollectingDB) Close() error
- func (c *CollectingDB) Delete(key []byte) error
- func (c *CollectingDB) DeleteSync(key []byte) error
- func (c *CollectingDB) Get(key []byte) ([]byte, error)
- func (c *CollectingDB) Has(key []byte) (bool, error)
- func (c *CollectingDB) Iterator(start, end []byte) (Iterator, error)
- func (c *CollectingDB) NewBatch() Batch
- func (c *CollectingDB) NewBatchWithSize(int) Batch
- func (c *CollectingDB) NewSnapshot() (Snapshot, error)
- func (c *CollectingDB) Print() error
- func (c *CollectingDB) ReverseIterator(start, end []byte) (Iterator, error)
- func (c *CollectingDB) Set(key, value []byte) error
- func (c *CollectingDB) SetSync(key, value []byte) error
- func (c *CollectingDB) Stats() map[string]string
- type DB
- type HoldingDB
- func (h *HoldingDB) Close() error
- func (h *HoldingDB) Delete(key []byte) error
- func (h *HoldingDB) DeleteSync(key []byte) error
- func (h *HoldingDB) Get(key []byte) ([]byte, error)
- func (h *HoldingDB) Has(key []byte) (bool, error)
- func (h *HoldingDB) HeldBytes() int
- func (h *HoldingDB) Hold(on bool)
- func (h *HoldingDB) Holding() bool
- func (h *HoldingDB) Iterator(start, end []byte) (Iterator, error)
- func (h *HoldingDB) NewBatch() Batch
- func (h *HoldingDB) NewBatchWithSize(size int) Batch
- func (h *HoldingDB) NewSnapshot() (Snapshot, error)
- func (h *HoldingDB) Print() error
- func (h *HoldingDB) ReverseIterator(start, end []byte) (Iterator, error)
- func (h *HoldingDB) Set(key, value []byte) error
- func (h *HoldingDB) SetSync(key, value []byte) error
- func (h *HoldingDB) Stats() map[string]string
- func (h *HoldingDB) WriteHeld() error
- type ImmutableDB
- func (idb *ImmutableDB) Close() error
- func (idb *ImmutableDB) Delete(key []byte) error
- func (idb *ImmutableDB) DeleteSync(key []byte) error
- func (idb *ImmutableDB) Get(key []byte) ([]byte, error)
- func (idb *ImmutableDB) Has(key []byte) (bool, error)
- func (idb *ImmutableDB) Iterator(start, end []byte) (Iterator, error)
- func (idb *ImmutableDB) NewBatch() Batch
- func (idb *ImmutableDB) NewBatchWithSize(_ int) Batch
- func (idb *ImmutableDB) NewSnapshot() (Snapshot, error)
- func (idb *ImmutableDB) Print() error
- func (idb *ImmutableDB) ReverseIterator(start, end []byte) (Iterator, error)
- func (idb *ImmutableDB) Set(key []byte, value []byte) error
- func (idb *ImmutableDB) SetSync(key []byte, value []byte) error
- func (idb *ImmutableDB) Stats() map[string]string
- type Iterator
- type PrefixDB
- func (pdb *PrefixDB) Close() error
- func (pdb *PrefixDB) Delete(key []byte) error
- func (pdb *PrefixDB) DeleteSync(key []byte) error
- func (pdb *PrefixDB) Get(key []byte) ([]byte, error)
- func (pdb *PrefixDB) Has(key []byte) (bool, error)
- func (pdb *PrefixDB) Iterator(start, end []byte) (Iterator, error)
- func (pdb *PrefixDB) Mutex() *sync.Mutex
- func (pdb *PrefixDB) NewBatch() Batch
- func (pdb *PrefixDB) NewBatchWithSize(size int) Batch
- func (*PrefixDB) NewSnapshot() (Snapshot, error)
- func (pdb *PrefixDB) Print() error
- func (pdb *PrefixDB) ReverseIterator(start, end []byte) (Iterator, error)
- func (pdb *PrefixDB) Set(key []byte, value []byte) error
- func (pdb *PrefixDB) SetSync(key []byte, value []byte) error
- func (pdb *PrefixDB) Stats() map[string]string
- type Snapshot
- type SnapshotDB
- func (s *SnapshotDB) Close() error
- func (s *SnapshotDB) Delete([]byte) error
- func (s *SnapshotDB) DeleteSync([]byte) error
- func (s *SnapshotDB) NewBatch() Batch
- func (s *SnapshotDB) NewBatchWithSize(int) Batch
- func (s *SnapshotDB) NewSnapshot() (Snapshot, error)
- func (s *SnapshotDB) Print() error
- func (s *SnapshotDB) Set([]byte, []byte) error
- func (s *SnapshotDB) SetSync([]byte, []byte) error
- func (s *SnapshotDB) Stats() map[string]string
Constants ¶
This section is empty.
Variables ¶
var ErrIteratorOverHeldWrites = errors.New("db: iterator would miss the writes held in memory")
ErrIteratorOverHeldWrites is the panic value of an iterator opened on a CollectingDB while its collector holds writes across blocks: the iterator reads only the underlying database, so it would miss them.
Functions ¶
func InternalRegisterDBCreator ¶
func InternalRegisterDBCreator(backend BackendType, creator dbCreator, force bool)
InternalRegisterDBCreator is used by the init functions of imported databases to register their own dbCreators.
This function is not meant for usage outside of db/.
func IsKeyInDomain ¶
See DB interface documentation for more information.
Types ¶
type BackendType ¶
type BackendType string
const ( // GoLevelDBBackend represents goleveldb (github.com/syndtr/goleveldb - most // popular implementation) // - stable GoLevelDBBackend BackendType = "goleveldb" // PebbleDBBackend represents pebble (github.com/cockroachdb/pebble) // - stable PebbleDBBackend BackendType = "pebbledb" // MemDBBackend represents in-memory key value store, which is mostly used // for testing. MemDBBackend BackendType = "memdb" // BoltDBBackend represents bolt (uses etcd's fork of bolt - // go.etcd.io/bbolt) // - EXPERIMENTAL // - may be faster is some use-cases (random reads - indexer) BoltDBBackend BackendType = "boltdb" )
These are valid backend types.
The backends themselves must be imported to be used (ie. using the blank import, `import _ "github.com/gnolang/gno/tm2/pkg/db/goleveldb"`). To allow for end-user customization at build time, the package "github.com/gnolang/gno/tm2/pkg/db/_tags" can be imported -- this package will import each database depending on whether its build tag is provided.
This can be used in conjunction with specific to provide defaults, for instance:
package main
import (
"github.com/gnolang/gno/tm2/pkg/db"
_ "github.com/gnolang/gno/tm2/pkg/db/_tags" // allow user to customize with build tags
_ "github.com/gnolang/gno/tm2/pkg/db/memdb" // always support memdb
)
func main() {
db.NewDB("mydb", db.BackendType(userProvidedBackend), "./data")
}
func BackendList ¶
func BackendList() []BackendType
BackendList returns a list of available db backends. The list is sorted.
func (BackendType) String ¶
func (b BackendType) String() string
type Batch ¶
type Batch interface {
// Set sets a key/value pair.
// CONTRACT: key, value readonly []byte
Set(key, value []byte) error
// Delete deletes a key/value pair.
// CONTRACT: key readonly []byte
Delete(key []byte) error
// Write writes the batch, possibly without flushing to disk. Only Close() can be called after,
// other methods will error.
Write() error
// WriteSync writes the batch and flushes it to disk. Only Close() can be called after, other
// methods will error.
WriteSync() error
// Close closes the batch, DISCARDING any ops staged since the last Write.
// It is idempotent, but calls to other methods afterwards will error.
Close() error
// GetByteSize that returns the current size of the batch in bytes. Depending on the implementation,
// this may return the size of the underlying LSM batch, including the size of additional metadata
// on top of the expected key and value total byte count.
GetByteSize() (int, error)
}
Batch represents a group of writes. They may or may not be written atomically depending on the backend. Callers must call Close on the batch when done.
Batch Close must be called when the program no longer needs the object.
type BatchCollector ¶
type BatchCollector struct {
// contains filtered or unexported fields
}
BatchCollector accumulates Set/Delete operations in memory without touching disk. It backs CollectingDB and lets rootmulti fuse multiple sub-store write sites (dbadapter flush, IAVL/bptree SaveVersion, rootmulti metadata) into one atomic disk write: direct DB writes enter the collector immediately, batch writes enter when their batch is Written (see batchHandle), and WriteTo writes the accumulated ops as one synced batch, at the end of a commit or, while holding, at a flush point.
pending indexes the last op per key so CollectingDB can serve read-your- writes without touching the underlying DB — required when a caller writes and then reads back before the next drain (e.g. deploying a package in one tx and importing it in the next, before the block-level Commit runs).
Set/Delete/Get are safe for concurrent use. WriteTo, Drain and Reset must not race with writers; they run when no writer is active.
While holding (SetHolding), the collector keeps its ops across commits and CollectingDB's iterators refuse to run (see ErrIteratorOverHeldWrites); a refused iterator is recorded for TakeRefusedIterator.
func NewBatchCollector ¶
func NewBatchCollector() *BatchCollector
NewBatchCollector returns an empty collector.
func (*BatchCollector) Drain ¶
func (c *BatchCollector) Drain(dst Batch) error
Drain replays the collected ops into dst in the order recorded, but for an op a later one on the same key overrides: dst is written as one batch, so only the last op on each key can reach the disk. The caller flushes dst (typically via WriteSync) to persist them atomically, then calls Reset: until then the ops stay readable through CollectingDB, so a concurrent reader finds each record in the collector or on disk. WriteTo does all of it.
func (*BatchCollector) Holding ¶
func (c *BatchCollector) Holding() bool
Holding reports whether the collector holds its ops across commits.
func (*BatchCollector) Len ¶
func (c *BatchCollector) Len() int
Len returns the number of ops currently buffered.
func (*BatchCollector) NewBatch ¶
func (c *BatchCollector) NewBatch() Batch
NewBatch returns a Batch that flushes into this collector on Write. Callers use it to write into the same op-log as CollectingDB-wrapped sub-stores; it lets rootmulti feed its own metadata (commitInfo, latestVersion) into the same atomic drain as IAVL and dbadapter writes.
func (*BatchCollector) Reset ¶
func (c *BatchCollector) Reset()
Reset drops all accumulated ops. Call it once the batch they were drained into is written. While holding it also frees the memory they held; outside it keeps it for the next commit.
func (*BatchCollector) SetHolding ¶
func (c *BatchCollector) SetHolding(on bool)
SetHolding turns holding on or off; see BatchCollector. It panics when turned on or off with ops collected: on, they would be held as part of the commits that follow; off, they would be lost, or written with later ones.
func (*BatchCollector) Size ¶
func (c *BatchCollector) Size() int
Size returns the bytes of keys and values currently buffered, counting every op, overwrites included.
func (*BatchCollector) TakeRefusedIterator ¶
func (c *BatchCollector) TakeRefusedIterator() bool
TakeRefusedIterator reports whether an iterator was refused since the last call, and clears the record.
func (*BatchCollector) WriteTo ¶
func (c *BatchCollector) WriteTo(db DB) error
WriteTo writes the collected ops to db as one synced batch (see Drain), and only then drops them.
type CollectingDB ¶
type CollectingDB struct {
// contains filtered or unexported fields
}
CollectingDB wraps a real DB and routes every write through a shared BatchCollector while iterators pass through untouched. It is installed under rootmulti's sub-stores so that IAVL/bptree, dbadapter, and rootmulti's own metadata writes accumulate in one place during CommitAll and get flushed as a single atomic batch.
Point reads (Get/Has) consult the collector first for read-your-writes over ops already in it — direct Sets/Deletes land there immediately, batch ops once their batch is Written. Iterators read the underlying DB ONLY (see Iterator below); consumers that iterate over keys they stage must not depend on pending ops being visible (bptree's clearFastIndex resumes by key for exactly this reason).
func NewCollectingDB ¶
func NewCollectingDB(db DB, c *BatchCollector) *CollectingDB
NewCollectingDB returns a DB whose writes accumulate in c and whose reads pass through to db. Reads and writes may run concurrently.
func (*CollectingDB) Close ¶
func (c *CollectingDB) Close() error
Close is a no-op — the caller owns the real DB.
func (*CollectingDB) Delete ¶
func (c *CollectingDB) Delete(key []byte) error
func (*CollectingDB) DeleteSync ¶
func (c *CollectingDB) DeleteSync(key []byte) error
func (*CollectingDB) Get ¶
func (c *CollectingDB) Get(key []byte) ([]byte, error)
Get consults the collector first for read-your-writes, then falls through to the real DB. A pending Delete masks any real value for the key.
func (*CollectingDB) Has ¶
func (c *CollectingDB) Has(key []byte) (bool, error)
Has mirrors Get: a pending Set makes the key present, a pending Delete makes it absent regardless of real DB state, and otherwise we defer to the real DB.
func (*CollectingDB) Iterator ¶
func (c *CollectingDB) Iterator(start, end []byte) (Iterator, error)
Iterator and ReverseIterator do NOT merge pending writes with the real DB. Callers that iterate before the next Drain will miss pending Sets and see stale keys through pending Deletes. Current consumers (IAVL SaveVersion, dbadapter cache flush, rootmulti metadata) don't iterate during a commit window, so this is safe today; revisit if that changes. While the collector holds writes across commits they panic instead, with ErrIteratorOverHeldWrites, and record it.
func (*CollectingDB) NewBatch ¶
func (c *CollectingDB) NewBatch() Batch
NewBatch and NewBatchWithSize return a Batch that buffers Set/Delete locally and moves them into the collector on Write/WriteSync (no disk I/O — the collector is drained externally when the commit closes). Close without Write DISCARDS the buffered ops, honoring the standard dbm.Batch contract: callers like bptree's DiscardBatch rely on Close dropping staged-but-uncommitted writes (session rollback, error paths). IAVL's BatchWithFlusher auto-flush (Write, Close, NewBatch cycles) works unchanged.
func (*CollectingDB) NewBatchWithSize ¶
func (c *CollectingDB) NewBatchWithSize(int) Batch
func (*CollectingDB) NewSnapshot ¶
func (c *CollectingDB) NewSnapshot() (Snapshot, error)
func (*CollectingDB) Print ¶
func (c *CollectingDB) Print() error
func (*CollectingDB) ReverseIterator ¶
func (c *CollectingDB) ReverseIterator(start, end []byte) (Iterator, error)
func (*CollectingDB) Set ¶
func (c *CollectingDB) Set(key, value []byte) error
Direct writes route into the collector. SetSync/DeleteSync collapse to Set/ Delete because the collector is drained under an explicit WriteSync at the end of the commit.
func (*CollectingDB) SetSync ¶
func (c *CollectingDB) SetSync(key, value []byte) error
func (*CollectingDB) Stats ¶
func (c *CollectingDB) Stats() map[string]string
type DB ¶
type DB interface {
// Get returns nil iff key doesn't exist.
// A nil key is interpreted as an empty byteslice.
// CONTRACT: key, value readonly []byte
Get([]byte) ([]byte, error)
// Has checks if a key exists.
// A nil key is interpreted as an empty byteslice.
// CONTRACT: key, value readonly []byte
Has(key []byte) (bool, error)
// Set sets the key.
// A nil key is interpreted as an empty byteslice.
// CONTRACT: key, value readonly []byte
Set([]byte, []byte) error
SetSync([]byte, []byte) error
// Delete deletes the key.
// A nil key is interpreted as an empty byteslice.
// CONTRACT: key readonly []byte
Delete([]byte) error
DeleteSync([]byte) error
// Iterate over a domain of keys in ascending order. End is exclusive.
// Start must be less than end, or the Iterator is invalid.
// A nil start is interpreted as an empty byteslice.
// If end is nil, iterates up to the last item (inclusive).
// 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)
// Iterate over a domain of keys in descending order. End is exclusive.
// Start must be less than end, or the Iterator is invalid.
// If start is nil, iterates up to the first/least item (inclusive).
// If end is nil, iterates from the last/greatest item (inclusive).
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
// CONTRACT: start, end readonly []byte
ReverseIterator(start, end []byte) (Iterator, error)
// Closes the connection.
Close() error
// Creates a batch for atomic updates.
NewBatch() Batch
// NewBatchWithSize create a new batch for atomic updates, but with pre-allocated size.
// This will does the same thing as NewBatch if the batch implementation doesn't support pre-allocation.
NewBatchWithSize(int) Batch
// For debugging
Print() error
// Stats returns a map of property values for all keys and the size of the cache.
Stats() map[string]string
// NewSnapshot returns a point-in-time read-only view of the DB.
// The caller must call Close on the snapshot when done.
NewSnapshot() (Snapshot, error)
}
DBs are goroutine safe.
type HoldingDB ¶
type HoldingDB struct {
// contains filtered or unexported fields
}
HoldingDB is a DB that passes every call through to the database it wraps until Hold(true). From then on it keeps every write in memory, where Get and Has see it, until WriteHeld writes all of them as one synced batch; its iterators panic meanwhile (see ErrIteratorOverHeldWrites). Block sync uses it to write the records of many blocks at once (tm2/adr, deferred writes during block sync).
Hold and WriteHeld are called by the one goroutine that writes, between its writes. Reads may run concurrently at any time: a held write stays readable until it is on disk.
func NewHoldingDB ¶
NewHoldingDB returns a HoldingDB over db, passing every call through.
func (*HoldingDB) DeleteSync ¶
func (*HoldingDB) HeldBytes ¶
HeldBytes returns the bytes of keys and values held, every write counted.
func (*HoldingDB) Hold ¶
Hold turns holding on or off. It panics when turned off with writes still held: they would be lost, or written after later ones. (Nothing is collected while not holding, so turning it on finds the collector empty.)
func (*HoldingDB) Iterator ¶
Iterator and ReverseIterator read the database, and panic while writes are held, which they would miss (see CollectingDB).
func (*HoldingDB) NewBatchWithSize ¶
func (*HoldingDB) NewSnapshot ¶
func (*HoldingDB) ReverseIterator ¶
type ImmutableDB ¶
type ImmutableDB struct {
// contains filtered or unexported fields
}
func NewImmutableDB ¶
func NewImmutableDB(db DB) *ImmutableDB
NewImmutableDB wraps a db to make it immutable. ImmutableDB panics on mutation operations.
func (*ImmutableDB) DeleteSync ¶
func (idb *ImmutableDB) DeleteSync(key []byte) error
Implements DB.
func (*ImmutableDB) Iterator ¶
func (idb *ImmutableDB) Iterator(start, end []byte) (Iterator, error)
Implements DB.
func (*ImmutableDB) NewBatch ¶
func (idb *ImmutableDB) NewBatch() Batch
Implements DB. Returns a read-only no-op batch (Set/Delete are discarded, Write/WriteSync panic) rather than nil: consumers construct batches eagerly (bptree's nodeDB, IAVL's BatchWithFlusher) and Close them on read-only paths, which must not nil-deref.
func (*ImmutableDB) NewBatchWithSize ¶
func (idb *ImmutableDB) NewBatchWithSize(_ int) Batch
Implements DB. See NewBatch.
func (*ImmutableDB) NewSnapshot ¶
func (idb *ImmutableDB) NewSnapshot() (Snapshot, error)
Implements DB.
func (*ImmutableDB) ReverseIterator ¶
func (idb *ImmutableDB) ReverseIterator(start, end []byte) (Iterator, error)
Implements DB.
func (*ImmutableDB) Set ¶
func (idb *ImmutableDB) Set(key []byte, value []byte) error
Implements DB.
type Iterator ¶
type Iterator interface {
// Domain returns the start (inclusive) and end (exclusive) limits of the iterator.
Domain() (start, end []byte)
// Valid returns whether the current iterator is valid. Once invalid, the Iterator remains
// invalid forever.
Valid() bool
// Next moves the iterator to the next key in the database, as defined by order of iteration.
// If Valid returns false, this method will panic.
Next()
// Key returns the key at the current position. Panics if the iterator is invalid.
// Note, the key returned should be a copy and thus safe for modification.
Key() []byte
// Value returns the value at the current position. Panics if the iterator is
// invalid.
// Note, the value returned should be a copy and thus safe for modification.
Value() []byte
// Error returns the last error encountered by the iterator, if any.
Error() error
// Close closes the iterator, releasing any allocated resources.
Close() error
}
Usage:
var itr Iterator = ... defer itr.Close()
for ; itr.Valid(); itr.Next() {
k, v := itr.Key(); itr.Value()
// ...
}
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.
func IteratePrefix ¶
IteratePrefix is a convenience function for iterating over a key domain restricted by prefix.
type PrefixDB ¶
type PrefixDB struct {
// contains filtered or unexported fields
}
func NewPrefixDB ¶
NewPrefixDB lets you namespace multiple DBs within a single DB.
func (*PrefixDB) NewBatchWithSize ¶
Implements DB. Panics if the underlying DB is not an atomicSetDeleter.
func (*PrefixDB) NewSnapshot ¶
func (*PrefixDB) ReverseIterator ¶
Implements DB.
type Snapshot ¶
type Snapshot interface {
// Get returns nil iff key doesn't exist.
// A nil key is interpreted as an empty byteslice.
// CONTRACT: key, value readonly []byte
Get([]byte) ([]byte, error)
// Has checks if a key exists.
// A nil key is interpreted as an empty byteslice.
// CONTRACT: key, value readonly []byte
Has(key []byte) (bool, error)
// Iterate over a domain of keys in ascending order. End is exclusive.
// Start must be less than end, or the Iterator is invalid.
// A nil start is interpreted as an empty byteslice.
// If end is nil, iterates up to the last item (inclusive).
// 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)
// Iterate over a domain of keys in descending order. End is exclusive.
// Start must be less than end, or the Iterator is invalid.
// If start is nil, iterates up to the first/least item (inclusive).
// If end is nil, iterates from the last/greatest item (inclusive).
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
// CONTRACT: start, end readonly []byte
ReverseIterator(start, end []byte) (Iterator, error)
// Close releases the resources associated with the snapshot.
Close() error
}
Snapshot is a read-only, point-in-time view of the DB. Callers must call Close when done to release resources.
type SnapshotDB ¶
type SnapshotDB struct {
Snapshot
}
SnapshotDB wraps a Snapshot to implement the DB interface. All read operations delegate to the snapshot. Write operations panic. Close is a no-op — the caller owns the snapshot lifecycle.
func NewSnapshotDB ¶
func NewSnapshotDB(snap Snapshot) *SnapshotDB
NewSnapshotDB returns a read-only DB backed by snap.
func (*SnapshotDB) Close ¶
func (s *SnapshotDB) Close() error
func (*SnapshotDB) Delete ¶
func (s *SnapshotDB) Delete([]byte) error
func (*SnapshotDB) DeleteSync ¶
func (s *SnapshotDB) DeleteSync([]byte) error
func (*SnapshotDB) NewBatch ¶
func (s *SnapshotDB) NewBatch() Batch
NewBatch and NewBatchWithSize return a no-op batch. IAVL creates a BatchWithFlusher eagerly in its constructor even for immutable loads, but never commits it when skipFastStorageUpgrade=true. The no-op batch panics on Write/WriteSync to catch any unexpected write attempts.
func (*SnapshotDB) NewBatchWithSize ¶
func (s *SnapshotDB) NewBatchWithSize(int) Batch
func (*SnapshotDB) NewSnapshot ¶
func (s *SnapshotDB) NewSnapshot() (Snapshot, error)
func (*SnapshotDB) Print ¶
func (s *SnapshotDB) Print() error
func (*SnapshotDB) Stats ¶
func (s *SnapshotDB) Stats() map[string]string