github.com/status-im/status-go@v1.1.0/db/db.go (about) 1 package db 2 3 import ( 4 "path/filepath" 5 6 "github.com/syndtr/goleveldb/leveldb" 7 "github.com/syndtr/goleveldb/leveldb/errors" 8 "github.com/syndtr/goleveldb/leveldb/iterator" 9 "github.com/syndtr/goleveldb/leveldb/opt" 10 "github.com/syndtr/goleveldb/leveldb/storage" 11 "github.com/syndtr/goleveldb/leveldb/util" 12 13 "github.com/ethereum/go-ethereum/log" 14 ) 15 16 type storagePrefix byte 17 18 const ( 19 // PeersCache is used for the db entries used for peers DB 20 PeersCache storagePrefix = iota 21 // DeduplicatorCache is used for the db entries used for messages 22 // deduplication cache 23 DeduplicatorCache 24 // MailserversCache is a list of mail servers provided by users. 25 MailserversCache 26 // TopicHistoryBucket isolated bucket for storing history metadata. 27 TopicHistoryBucket 28 // HistoryRequestBucket isolated bucket for storing list of pending requests. 29 HistoryRequestBucket 30 ) 31 32 // NewMemoryDB returns leveldb with memory backend prefixed with a bucket. 33 func NewMemoryDB() (*leveldb.DB, error) { 34 return leveldb.Open(storage.NewMemStorage(), nil) 35 } 36 37 // NewDBNamespace returns instance that ensures isolated operations. 38 func NewDBNamespace(db Storage, prefix storagePrefix) LevelDBNamespace { 39 return LevelDBNamespace{ 40 db: db, 41 prefix: prefix, 42 } 43 } 44 45 // NewMemoryDBNamespace wraps in memory leveldb with provided bucket. 46 // Mostly used for tests. Including tests in other packages. 47 func NewMemoryDBNamespace(prefix storagePrefix) (pdb LevelDBNamespace, err error) { 48 db, err := NewMemoryDB() 49 if err != nil { 50 return pdb, err 51 } 52 return NewDBNamespace(LevelDBStorage{db: db}, prefix), nil 53 } 54 55 // Key creates a DB key for a specified service with specified data 56 func Key(prefix storagePrefix, data ...[]byte) []byte { 57 keyLength := 1 58 for _, d := range data { 59 keyLength += len(d) 60 } 61 key := make([]byte, keyLength) 62 key[0] = byte(prefix) 63 startPos := 1 64 for _, d := range data { 65 copy(key[startPos:], d[:]) 66 startPos += len(d) 67 } 68 69 return key 70 } 71 72 // Create returns status pointer to leveldb.DB. 73 func Create(path, dbName string) (*leveldb.DB, error) { 74 // Create euphemeral storage if the node config path isn't provided 75 if path == "" { 76 return leveldb.Open(storage.NewMemStorage(), nil) 77 } 78 79 path = filepath.Join(path, dbName) 80 return Open(path, &opt.Options{OpenFilesCacheCapacity: 5}) 81 } 82 83 // Open opens an existing leveldb database 84 func Open(path string, opts *opt.Options) (db *leveldb.DB, err error) { 85 db, err = leveldb.OpenFile(path, opts) 86 if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted { 87 log.Info("database is corrupted trying to recover", "path", path) 88 db, err = leveldb.RecoverFile(path, nil) 89 } 90 return 91 } 92 93 // LevelDBNamespace database where all operations will be prefixed with a certain bucket. 94 type LevelDBNamespace struct { 95 db Storage 96 prefix storagePrefix 97 } 98 99 func (db LevelDBNamespace) prefixedKey(key []byte) []byte { 100 endkey := make([]byte, len(key)+1) 101 endkey[0] = byte(db.prefix) 102 copy(endkey[1:], key) 103 return endkey 104 } 105 106 func (db LevelDBNamespace) Put(key, value []byte) error { 107 return db.db.Put(db.prefixedKey(key), value) 108 } 109 110 func (db LevelDBNamespace) Get(key []byte) ([]byte, error) { 111 return db.db.Get(db.prefixedKey(key)) 112 } 113 114 // Range returns leveldb util.Range prefixed with a single byte. 115 // If prefix is nil range will iterate over all records in a given bucket. 116 func (db LevelDBNamespace) Range(prefix, limit []byte) *util.Range { 117 if limit == nil { 118 return util.BytesPrefix(db.prefixedKey(prefix)) 119 } 120 return &util.Range{Start: db.prefixedKey(prefix), Limit: db.prefixedKey(limit)} 121 } 122 123 // Delete removes key from database. 124 func (db LevelDBNamespace) Delete(key []byte) error { 125 return db.db.Delete(db.prefixedKey(key)) 126 } 127 128 // NewIterator returns iterator for a given slice. 129 func (db LevelDBNamespace) NewIterator(slice *util.Range) NamespaceIterator { 130 return NamespaceIterator{db.db.NewIterator(slice)} 131 } 132 133 // NamespaceIterator wraps leveldb iterator, works mostly the same way. 134 // The only difference is that first byte of the key is dropped. 135 type NamespaceIterator struct { 136 iter iterator.Iterator 137 } 138 139 // Key returns key of the current item. 140 func (iter NamespaceIterator) Key() []byte { 141 return iter.iter.Key()[1:] 142 } 143 144 // Value returns actual value of the current item. 145 func (iter NamespaceIterator) Value() []byte { 146 return iter.iter.Value() 147 } 148 149 // Error returns accumulated error. 150 func (iter NamespaceIterator) Error() error { 151 return iter.iter.Error() 152 } 153 154 // Prev moves cursor backward. 155 func (iter NamespaceIterator) Prev() bool { 156 return iter.iter.Prev() 157 } 158 159 // Next moves cursor forward. 160 func (iter NamespaceIterator) Next() bool { 161 return iter.iter.Next() 162 }