Skip to content

Commit

Permalink
Merge pull request hashicorp#90 from hashicorp/docs
Browse files Browse the repository at this point in the history
Docs update, most importantly to clarify values must not be mutated
  • Loading branch information
mitchellh authored Feb 2, 2021
2 parents 1cc0bb5 + 9488e8e commit 335f02e
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 10 deletions.
23 changes: 17 additions & 6 deletions memdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@ import (
"github.com/hashicorp/go-immutable-radix"
)

// MemDB is an in-memory database.
// MemDB is an in-memory database providing Atomicity, Consistency, and
// Isolation from ACID. MemDB doesn't provide Durability since it is an
// in-memory database.
//
// MemDB provides a table abstraction to store objects (rows) with multiple
// indexes based on inserted values. The database makes use of immutable radix
// trees to provide transactions and MVCC.
//
// Objects inserted into MemDB are not copied. It is **extremely important**
// that objects are not modified in-place after they are inserted since they
// are stored directly in MemDB. It remains unsafe to modify inserted objects
// even after they've been deleted from MemDB since there may still be older
// snapshots of the DB being read from other goroutines.
type MemDB struct {
schema *DBSchema
root unsafe.Pointer // *iradix.Tree underneath
Expand All @@ -24,7 +32,7 @@ type MemDB struct {
writer sync.Mutex
}

// NewMemDB creates a new MemDB with the given schema
// NewMemDB creates a new MemDB with the given schema.
func NewMemDB(schema *DBSchema) (*MemDB, error) {
// Validate the schema
if err := schema.Validate(); err != nil {
Expand All @@ -50,7 +58,7 @@ func (db *MemDB) getRoot() *iradix.Tree {
return root
}

// Txn is used to start a new transaction, in either read or write mode.
// Txn is used to start a new transaction in either read or write mode.
// There can only be a single concurrent writer, but any number of readers.
func (db *MemDB) Txn(write bool) *Txn {
if write {
Expand All @@ -64,9 +72,12 @@ func (db *MemDB) Txn(write bool) *Txn {
return txn
}

// Snapshot is used to capture a point-in-time snapshot
// of the database that will not be affected by any write
// operations to the existing DB.
// Snapshot is used to capture a point-in-time snapshot of the database that
// will not be affected by any write operations to the existing DB.
//
// If MemDB is storing reference-based values (pointers, maps, slices, etc.),
// the Snapshot will not deep copy those values. Therefore, it is still unsafe
// to modify any inserted values in either DB.
func (db *MemDB) Snapshot() *MemDB {
clone := &MemDB{
schema: db.schema,
Expand Down
20 changes: 16 additions & 4 deletions txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,11 @@ func (txn *Txn) Commit() {
}
}

// Insert is used to add or update an object into the given table
// Insert is used to add or update an object into the given table.
//
// When updating an object, the obj provided should be a copy rather
// than a value updated in-place. Modifying values in-place that are already
// inserted into MemDB is not supported behavior.
func (txn *Txn) Insert(table string, obj interface{}) error {
if !txn.write {
return fmt.Errorf("cannot insert in read-only transaction")
Expand Down Expand Up @@ -293,8 +297,8 @@ func (txn *Txn) Insert(table string, obj interface{}) error {
return nil
}

// Delete is used to delete a single object from the given table
// This object must already exist in the table
// Delete is used to delete a single object from the given table.
// This object must already exist in the table.
func (txn *Txn) Delete(table string, obj interface{}) error {
if !txn.write {
return fmt.Errorf("cannot delete in read-only transaction")
Expand Down Expand Up @@ -688,7 +692,14 @@ type ResultIterator interface {
}

// Get is used to construct a ResultIterator over all the rows that match the
// given constraints of an index.
// given constraints of an index. The index values must match exactly (this
// is not a range-based or prefix-based lookup) by default.
//
// Prefix lookups: if the named index implements PrefixIndexer, you may perform
// prefix-based lookups by appending "_prefix" to the index name. In this
// scenario, the index values given in args are treated as prefix lookups. For
// example, a StringFieldIndex will match any string with the given value
// as a prefix: "mem" matches "memdb".
//
// See the documentation for ResultIterator to understand the behaviour of the
// returned ResultIterator.
Expand All @@ -713,6 +724,7 @@ func (txn *Txn) Get(table, index string, args ...interface{}) (ResultIterator, e
// rows that match the given constraints of an index.
// The returned ResultIterator's Next() will return the next Previous value.
//
// See the documentation on Get for details on arguments.
// See the documentation for ResultIterator to understand the behaviour of the
// returned ResultIterator.
func (txn *Txn) GetReverse(table, index string, args ...interface{}) (ResultIterator, error) {
Expand Down

0 comments on commit 335f02e

Please sign in to comment.