34871f89be
* changes necessary to support oss licesning shims revert nomad fmt changes update test to work with enterprise changes update tests to work with new ent enforcements make check update cas test to use scheduler algorithm back out preemption changes add comments * remove unused method
98 lines
2.3 KiB
Go
98 lines
2.3 KiB
Go
// Package memdb provides an in-memory database that supports transactions
|
|
// and MVCC.
|
|
package memdb
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
"unsafe"
|
|
|
|
"github.com/hashicorp/go-immutable-radix"
|
|
)
|
|
|
|
// MemDB 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.
|
|
type MemDB struct {
|
|
schema *DBSchema
|
|
root unsafe.Pointer // *iradix.Tree underneath
|
|
primary bool
|
|
|
|
// There can only be a single writer at once
|
|
writer sync.Mutex
|
|
}
|
|
|
|
// NewMemDB creates a new MemDB with the given schema
|
|
func NewMemDB(schema *DBSchema) (*MemDB, error) {
|
|
// Validate the schema
|
|
if err := schema.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Create the MemDB
|
|
db := &MemDB{
|
|
schema: schema,
|
|
root: unsafe.Pointer(iradix.New()),
|
|
primary: true,
|
|
}
|
|
if err := db.initialize(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return db, nil
|
|
}
|
|
|
|
// getRoot is used to do an atomic load of the root pointer
|
|
func (db *MemDB) getRoot() *iradix.Tree {
|
|
root := (*iradix.Tree)(atomic.LoadPointer(&db.root))
|
|
return root
|
|
}
|
|
|
|
// 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 {
|
|
db.writer.Lock()
|
|
}
|
|
txn := &Txn{
|
|
db: db,
|
|
write: write,
|
|
rootTxn: db.getRoot().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.
|
|
func (db *MemDB) Snapshot() *MemDB {
|
|
clone := &MemDB{
|
|
schema: db.schema,
|
|
root: unsafe.Pointer(db.getRoot()),
|
|
primary: false,
|
|
}
|
|
return clone
|
|
}
|
|
|
|
// initialize is used to setup the DB for use after creation. This should
|
|
// be called only once after allocating a MemDB.
|
|
func (db *MemDB) initialize() error {
|
|
root := db.getRoot()
|
|
for tName, tableSchema := range db.schema.Tables {
|
|
for iName := range tableSchema.Indexes {
|
|
index := iradix.New()
|
|
path := indexPath(tName, iName)
|
|
root, _, _ = root.Insert(path, index)
|
|
}
|
|
}
|
|
db.root = unsafe.Pointer(root)
|
|
return nil
|
|
}
|
|
|
|
// indexPath returns the path from the root to the given table index
|
|
func indexPath(table, index string) []byte {
|
|
return []byte(table + "." + index)
|
|
}
|