package database import ( "fmt" "os" "sync" log "github.com/corgi-kx/logcustom" "github.com/pkg/errors" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/opt" goleveldbutil "github.com/syndtr/goleveldb/leveldb/util" ) type dbState int32 const ( closed dbState = iota opened ) // DB 用于存储区块链账本的levelDB数据库 type DB struct { dbPath string db *leveldb.DB dbState dbState mutex sync.RWMutex readOpts *opt.ReadOptions writeOptsNoSync *opt.WriteOptions writeOptsSync *opt.WriteOptions } // UpdateBatch encloses the details of multiple `updates` type UpdateBatch struct { leveldbBatch *leveldb.Batch dbName string size int } type Snapshot struct { dbName string snapshot *leveldb.Snapshot readOpts *opt.ReadOptions } // CreateDB constructs a `DB` func CreateDB(dp string) *DB { readOpts := &opt.ReadOptions{} writeOptsNoSync := &opt.WriteOptions{} writeOptsSync := &opt.WriteOptions{} writeOptsSync.Sync = true return &DB{ dbPath: dp, dbState: closed, readOpts: readOpts, writeOptsNoSync: writeOptsNoSync, writeOptsSync: writeOptsSync, } } // 打开基础数据库 func (dbInst *DB) Open() { dbInst.mutex.Lock() defer dbInst.mutex.Unlock() if dbInst.dbState == opened { return } dbPath := dbInst.dbPath var err error if err = os.MkdirAll(dbPath, 0755); err != nil { panic(fmt.Sprintf("Error creating dir if missing: %s", err)) } if dbInst.db, err = leveldb.OpenFile(dbPath, nil); err != nil { panic(fmt.Sprintf("Error opening leveldb: %s", err)) } dbInst.dbState = opened } // IsEmpty returns whether a database is empty func (dbInst *DB) IsEmpty() (bool, error) { dbInst.mutex.RLock() defer dbInst.mutex.RUnlock() itr := dbInst.db.NewIterator(&goleveldbutil.Range{}, dbInst.readOpts) defer itr.Release() hasItems := itr.Next() return !hasItems, errors.Wrapf(itr.Error(), "error while trying to see if the leveldb at path [%s] is empty", dbInst.dbPath) } // Close closes the underlying db func (dbInst *DB) Close() { dbInst.mutex.Lock() defer dbInst.mutex.Unlock() if dbInst.dbState == closed { return } if err := dbInst.db.Close(); err != nil { log.Errorf("Error closing leveldb: %s", err) } dbInst.dbState = closed } // Get returns the value for the given key func (dbInst *DB) Get(key []byte) ([]byte, error) { dbInst.mutex.RLock() defer dbInst.mutex.RUnlock() value, err := dbInst.db.Get(key, dbInst.readOpts) if err == leveldb.ErrNotFound { value = nil err = nil } if err != nil { log.Errorf("Error retrieving leveldb key [%#v]: %s", key, err) return nil, errors.Wrapf(err, "error retrieving leveldb key [%#v]", key) } return value, nil } // Put saves the key/value func (dbInst *DB) Put(key []byte, value []byte, sync bool) error { dbInst.mutex.RLock() defer dbInst.mutex.RUnlock() wo := dbInst.writeOptsNoSync if sync { wo = dbInst.writeOptsSync } err := dbInst.db.Put(key, value, wo) if err != nil { log.Errorf("Error writing leveldb key [%#v]", key) return errors.Wrapf(err, "error writing leveldb key [%#v]", key) } return nil } // Delete deletes the given key func (dbInst *DB) Delete(key []byte, sync bool) error { dbInst.mutex.RLock() defer dbInst.mutex.RUnlock() wo := dbInst.writeOptsNoSync if sync { wo = dbInst.writeOptsSync } err := dbInst.db.Delete(key, wo) if err != nil { log.Errorf("Error deleting leveldb key [%#v]", key) return errors.Wrapf(err, "error deleting leveldb key [%#v]", key) } return nil } // GetIterator returns an iterator over key-value store. The iterator should be released after the use. // The resultSet contains all the keys that are present in the db between the startKey (inclusive) and the endKey (exclusive). // A nil startKey represents the first available key and a nil endKey represent a logical key after the last available key func (dbInst *DB) GetIterator(startKey []byte, endKey []byte) iterator.Iterator { dbInst.mutex.RLock() defer dbInst.mutex.RUnlock() return dbInst.db.NewIterator(&goleveldbutil.Range{Start: startKey, Limit: endKey}, dbInst.readOpts) } func (dbInst *DB) PrefixQuery(Key []byte) iterator.Iterator { dbInst.mutex.RLock() defer dbInst.mutex.RUnlock() //BytesPrefix返回满足给定前缀的键范围。这仅适用于标准的“字节比较器” return dbInst.db.NewIterator(goleveldbutil.BytesPrefix(Key), dbInst.readOpts) } // WriteBatch 批量写入 func (dbInst *DB) WriteBatch(batch *leveldb.Batch, sync bool) error { dbInst.mutex.RLock() defer dbInst.mutex.RUnlock() wo := dbInst.writeOptsNoSync if sync { wo = dbInst.writeOptsSync } if err := dbInst.db.Write(batch, wo); err != nil { return errors.Wrap(err, "error writing batch to leveldb") } return nil } /*type Batch struct { data []byte index []batchIndex // internalLen is sums of key/value pair length plus 8-bytes internal key. internalLen int } func (*leveldb.Batch).Delete(key []byte) func (*leveldb.Batch).Dump() []byte func (*leveldb.Batch).Len() int func (*leveldb.Batch).Load(data []byte) error func (*leveldb.Batch).Put(key []byte, value []byte) func (*leveldb.Batch).Replay(r leveldb.BatchReplay) error func (*leveldb.Batch).Reset() */