1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 |
- package memdb
- import (
- "sync"
- "sync/atomic"
- "unsafe"
- "github.com/hashicorp/go-immutable-radix"
- )
- // MemDB is an in-memory database. It provides a table abstraction,
- // which is used to store objects (rows) with multiple indexes based
- // on 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
- // There can only be a single writter 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()),
- }
- 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()),
- }
- return clone
- }
- // initialize is used to setup the DB for use after creation
- 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)
- }
|