github.com/braveheart12/just@v0.8.7/ledger/storage/db.go (about) 1 /* 2 * Copyright 2019 Insolar Technologies 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package storage 18 19 import ( 20 "context" 21 "path/filepath" 22 "sync" 23 24 "github.com/dgraph-io/badger" 25 "github.com/insolar/insolar/configuration" 26 "github.com/insolar/insolar/core" 27 "github.com/insolar/insolar/ledger/storage/jet" 28 "github.com/insolar/insolar/ledger/storage/record" 29 "github.com/pkg/errors" 30 ) 31 32 const ( 33 scopeIDLifeline byte = 1 34 scopeIDRecord byte = 2 35 scopeIDJetDrop byte = 3 36 scopeIDPulse byte = 4 37 scopeIDSystem byte = 5 38 scopeIDMessage byte = 6 39 scopeIDBlob byte = 7 40 41 sysGenesis byte = 1 42 sysLatestPulse byte = 2 43 sysHeavyClientState byte = 3 44 sysLastSyncedPulseOnHeavy byte = 4 45 sysJetList byte = 5 46 sysDropSizeHistory byte = 6 47 ) 48 49 // DBContext provides base db methods 50 //go:generate minimock -i github.com/insolar/insolar/ledger/storage.DBContext -o ./ -s _mock.go 51 type DBContext interface { 52 BeginTransaction(update bool) (*TransactionManager, error) 53 View(ctx context.Context, fn func(*TransactionManager) error) error 54 Update(ctx context.Context, fn func(*TransactionManager) error) error 55 56 IterateRecordsOnPulse( 57 ctx context.Context, 58 jetID core.RecordID, 59 pulse core.PulseNumber, 60 handler func(id core.RecordID, rec record.Record) error, 61 ) error 62 63 StoreKeyValues(ctx context.Context, kvs []core.KV) error 64 65 GetBadgerDB() *badger.DB 66 67 Close() error 68 69 set(ctx context.Context, key, value []byte) error 70 get(ctx context.Context, key []byte) ([]byte, error) 71 72 // TODO i.markin 28.01.19: Delete after switching to conveyor architecture. 73 waitingFlight() 74 75 iterate(ctx context.Context, 76 prefix []byte, 77 handler func(k, v []byte) error, 78 ) error 79 } 80 81 // DB represents BadgerDB storage implementation. 82 type DB struct { 83 PlatformCryptographyScheme core.PlatformCryptographyScheme `inject:""` 84 85 db *badger.DB 86 87 // dropLock protects dropWG from concurrent calls to Add and Wait 88 dropLock sync.Mutex 89 // dropWG guards inflight updates before jet drop calculated. 90 dropWG sync.WaitGroup 91 92 // for BadgerDB it is normal to have transaction conflicts 93 // and these conflicts we should resolve by ourself 94 // so txretiries is our knob to tune up retry logic. 95 txretiries int 96 97 idlocker *IDLocker 98 jetHeavyClientLocker *IDLocker 99 100 closeLock sync.RWMutex 101 isClosed bool 102 } 103 104 func setOptions(o *badger.Options) *badger.Options { 105 newo := &badger.Options{} 106 if o != nil { 107 *newo = *o 108 } else { 109 *newo = badger.DefaultOptions 110 } 111 return newo 112 } 113 114 // NewDB returns storage.DB with BadgerDB instance initialized by opts. 115 // Creates database in provided dir or in current directory if dir parameter is empty. 116 func NewDB(conf configuration.Ledger, opts *badger.Options) (DBContext, error) { 117 opts = setOptions(opts) 118 dir, err := filepath.Abs(conf.Storage.DataDirectory) 119 if err != nil { 120 return nil, err 121 } 122 123 opts.Dir = dir 124 opts.ValueDir = dir 125 126 bdb, err := badger.Open(*opts) 127 if err != nil { 128 return nil, errors.Wrap(err, "local database open failed") 129 } 130 131 db := &DB{ 132 db: bdb, 133 txretiries: conf.Storage.TxRetriesOnConflict, 134 idlocker: NewIDLocker(), 135 jetHeavyClientLocker: NewIDLocker(), 136 } 137 return db, nil 138 } 139 140 // Close wraps BadgerDB Close method. 141 // 142 // From https://godoc.org/github.com/dgraph-io/badger#DB.Close: 143 // «It's crucial to call it to ensure all the pending updates make their way to disk. 144 // Calling DB.Close() multiple times is not safe and wouldcause panic.» 145 func (db *DB) Close() error { 146 db.closeLock.Lock() 147 defer db.closeLock.Unlock() 148 if db.isClosed { 149 return ErrClosed 150 } 151 db.isClosed = true 152 153 return db.db.Close() 154 } 155 156 // Stop stops DB component. 157 func (db *DB) Stop(ctx context.Context) error { 158 return db.Close() 159 } 160 161 // BeginTransaction opens a new transaction. 162 // All methods called on returned transaction manager will persist changes 163 // only after success on "Commit" call. 164 func (db *DB) BeginTransaction(update bool) (*TransactionManager, error) { 165 db.closeLock.RLock() 166 defer db.closeLock.RUnlock() 167 if db.isClosed { 168 return nil, ErrClosed 169 } 170 171 if update { 172 db.dropLock.Lock() 173 db.dropWG.Add(1) 174 db.dropLock.Unlock() 175 } 176 return &TransactionManager{ 177 db: db, 178 update: update, 179 txupdates: make(map[string]keyval), 180 }, nil 181 } 182 183 // View accepts transaction function. All calls to received transaction manager will be consistent. 184 func (db *DB) View(ctx context.Context, fn func(*TransactionManager) error) error { 185 tx, err := db.BeginTransaction(false) 186 if err != nil { 187 return err 188 } 189 defer tx.Discard() 190 return fn(tx) 191 } 192 193 // Update accepts transaction function and commits changes. All calls to received transaction manager will be 194 // consistent and written tp disk or an error will be returned. 195 func (db *DB) Update(ctx context.Context, fn func(*TransactionManager) error) error { 196 tries := db.txretiries 197 var tx *TransactionManager 198 var err error 199 for { 200 tx, err = db.BeginTransaction(true) 201 if err != nil { 202 return err 203 } 204 err = fn(tx) 205 if err != nil { 206 break 207 } 208 err = tx.Commit() 209 if err == nil { 210 break 211 } 212 if err != badger.ErrConflict { 213 break 214 } 215 if tries < 1 { 216 if db.txretiries > 0 { 217 err = ErrConflictRetriesOver 218 } else { 219 err = ErrConflict 220 } 221 break 222 } 223 tries-- 224 tx.Discard() 225 } 226 tx.Discard() 227 228 return err 229 } 230 231 // GetBadgerDB return badger.DB instance (for internal usage, like tests) 232 func (db *DB) GetBadgerDB() *badger.DB { 233 return db.db 234 } 235 236 // IterateRecordsOnPulse iterates over records on provided Jet ID and Pulse. 237 func (db *DB) IterateRecordsOnPulse( 238 ctx context.Context, 239 jetID core.RecordID, 240 pulse core.PulseNumber, 241 handler func(id core.RecordID, rec record.Record) error, 242 ) error { 243 _, jetPrefix := jet.Jet(jetID) 244 prefix := prefixkey(scopeIDRecord, jetPrefix, pulse.Bytes()) 245 246 return db.iterate(ctx, prefix, func(k, v []byte) error { 247 id := core.NewRecordID(pulse, k) 248 rec := record.DeserializeRecord(v) 249 err := handler(*id, rec) 250 if err != nil { 251 return err 252 } 253 return nil 254 }) 255 } 256 257 // StoreKeyValues stores provided key/value pairs. 258 func (db *DB) StoreKeyValues(ctx context.Context, kvs []core.KV) error { 259 return db.Update(ctx, func(tx *TransactionManager) error { 260 for _, rec := range kvs { 261 err := tx.set(ctx, rec.K, rec.V) 262 if err != nil { 263 return err 264 } 265 } 266 return nil 267 }) 268 } 269 270 func (db *DB) GetPlatformCryptographyScheme() core.PlatformCryptographyScheme { 271 return db.PlatformCryptographyScheme 272 } 273 274 // get wraps matching transaction manager method. 275 func (db *DB) get(ctx context.Context, key []byte) ([]byte, error) { 276 tx, err := db.BeginTransaction(false) 277 if err != nil { 278 return nil, err 279 } 280 defer tx.Discard() 281 return tx.get(ctx, key) 282 } 283 284 // set wraps matching transaction manager method. 285 func (db *DB) set(ctx context.Context, key, value []byte) error { 286 return db.Update(ctx, func(tx *TransactionManager) error { 287 return tx.set(ctx, key, value) 288 }) 289 } 290 291 func (db *DB) waitingFlight() { 292 db.dropLock.Lock() 293 db.dropWG.Wait() 294 db.dropLock.Unlock() 295 } 296 297 func (db *DB) iterate( 298 ctx context.Context, 299 prefix []byte, 300 handler func(k, v []byte) error, 301 ) error { 302 db.closeLock.RLock() 303 defer db.closeLock.RUnlock() 304 if db.isClosed { 305 return ErrClosed 306 } 307 308 return db.db.View(func(txn *badger.Txn) error { 309 it := txn.NewIterator(badger.DefaultIteratorOptions) 310 defer it.Close() 311 312 for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { 313 key := it.Item().KeyCopy(nil)[len(prefix):] 314 value, err := it.Item().ValueCopy(nil) 315 if err != nil { 316 return err 317 } 318 err = handler(key, value) 319 if err != nil { 320 return err 321 } 322 } 323 return nil 324 }) 325 }