github.com/MetalBlockchain/metalgo@v1.11.9/database/versiondb/db.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package versiondb 5 6 import ( 7 "context" 8 "slices" 9 "strings" 10 "sync" 11 12 "github.com/MetalBlockchain/metalgo/database" 13 "github.com/MetalBlockchain/metalgo/database/memdb" 14 ) 15 16 var ( 17 _ database.Database = (*Database)(nil) 18 _ Commitable = (*Database)(nil) 19 _ database.Batch = (*batch)(nil) 20 _ database.Iterator = (*iterator)(nil) 21 ) 22 23 // Commitable defines the interface that specifies that something may be 24 // committed. 25 type Commitable interface { 26 // Commit writes all the queued operations to the underlying data structure. 27 Commit() error 28 } 29 30 // Database implements the Database interface by living on top of another 31 // database, writing changes to the underlying database only when commit is 32 // called. 33 type Database struct { 34 lock sync.RWMutex 35 mem map[string]valueDelete 36 db database.Database 37 batch database.Batch 38 } 39 40 type valueDelete struct { 41 value []byte 42 delete bool 43 } 44 45 // New returns a new versioned database 46 func New(db database.Database) *Database { 47 return &Database{ 48 mem: make(map[string]valueDelete, memdb.DefaultSize), 49 db: db, 50 batch: db.NewBatch(), 51 } 52 } 53 54 func (db *Database) Has(key []byte) (bool, error) { 55 db.lock.RLock() 56 defer db.lock.RUnlock() 57 58 if db.mem == nil { 59 return false, database.ErrClosed 60 } 61 if val, has := db.mem[string(key)]; has { 62 return !val.delete, nil 63 } 64 return db.db.Has(key) 65 } 66 67 func (db *Database) Get(key []byte) ([]byte, error) { 68 db.lock.RLock() 69 defer db.lock.RUnlock() 70 71 if db.mem == nil { 72 return nil, database.ErrClosed 73 } 74 if val, has := db.mem[string(key)]; has { 75 if val.delete { 76 return nil, database.ErrNotFound 77 } 78 return slices.Clone(val.value), nil 79 } 80 return db.db.Get(key) 81 } 82 83 func (db *Database) Put(key, value []byte) error { 84 db.lock.Lock() 85 defer db.lock.Unlock() 86 87 if db.mem == nil { 88 return database.ErrClosed 89 } 90 db.mem[string(key)] = valueDelete{value: slices.Clone(value)} 91 return nil 92 } 93 94 func (db *Database) Delete(key []byte) error { 95 db.lock.Lock() 96 defer db.lock.Unlock() 97 98 if db.mem == nil { 99 return database.ErrClosed 100 } 101 db.mem[string(key)] = valueDelete{delete: true} 102 return nil 103 } 104 105 func (db *Database) NewBatch() database.Batch { 106 return &batch{db: db} 107 } 108 109 func (db *Database) NewIterator() database.Iterator { 110 return db.NewIteratorWithStartAndPrefix(nil, nil) 111 } 112 113 func (db *Database) NewIteratorWithStart(start []byte) database.Iterator { 114 return db.NewIteratorWithStartAndPrefix(start, nil) 115 } 116 117 func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator { 118 return db.NewIteratorWithStartAndPrefix(nil, prefix) 119 } 120 121 func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator { 122 db.lock.RLock() 123 defer db.lock.RUnlock() 124 125 if db.mem == nil { 126 return &database.IteratorError{ 127 Err: database.ErrClosed, 128 } 129 } 130 131 startString := string(start) 132 prefixString := string(prefix) 133 keys := make([]string, 0, len(db.mem)) 134 for key := range db.mem { 135 if strings.HasPrefix(key, prefixString) && key >= startString { 136 keys = append(keys, key) 137 } 138 } 139 slices.Sort(keys) // Keys need to be in sorted order 140 values := make([]valueDelete, len(keys)) 141 for i, key := range keys { 142 values[i] = db.mem[key] 143 } 144 145 return &iterator{ 146 db: db, 147 Iterator: db.db.NewIteratorWithStartAndPrefix(start, prefix), 148 keys: keys, 149 values: values, 150 } 151 } 152 153 func (db *Database) Compact(start, limit []byte) error { 154 db.lock.Lock() 155 defer db.lock.Unlock() 156 157 if db.mem == nil { 158 return database.ErrClosed 159 } 160 return db.db.Compact(start, limit) 161 } 162 163 // SetDatabase changes the underlying database to the specified database 164 func (db *Database) SetDatabase(newDB database.Database) error { 165 db.lock.Lock() 166 defer db.lock.Unlock() 167 168 if db.mem == nil { 169 return database.ErrClosed 170 } 171 172 db.db = newDB 173 db.batch = newDB.NewBatch() 174 return nil 175 } 176 177 // GetDatabase returns the underlying database 178 func (db *Database) GetDatabase() database.Database { 179 db.lock.RLock() 180 defer db.lock.RUnlock() 181 182 return db.db 183 } 184 185 // Commit writes all the operations of this database to the underlying database 186 func (db *Database) Commit() error { 187 db.lock.Lock() 188 defer db.lock.Unlock() 189 190 batch, err := db.commitBatch() 191 if err != nil { 192 return err 193 } 194 if err := batch.Write(); err != nil { 195 return err 196 } 197 batch.Reset() 198 db.abort() 199 return nil 200 } 201 202 // Abort all changes to the underlying database 203 func (db *Database) Abort() { 204 db.lock.Lock() 205 defer db.lock.Unlock() 206 207 db.abort() 208 } 209 210 func (db *Database) abort() { 211 clear(db.mem) 212 } 213 214 // CommitBatch returns a batch that contains all uncommitted puts/deletes. 215 // Calling Write() on the returned batch causes the puts/deletes to be 216 // written to the underlying database. The returned batch should be written before 217 // future calls to this DB unless the batch will never be written. 218 func (db *Database) CommitBatch() (database.Batch, error) { 219 db.lock.Lock() 220 defer db.lock.Unlock() 221 222 return db.commitBatch() 223 } 224 225 // Put all of the puts/deletes in memory into db.batch 226 // and return the batch 227 func (db *Database) commitBatch() (database.Batch, error) { 228 if db.mem == nil { 229 return nil, database.ErrClosed 230 } 231 232 db.batch.Reset() 233 for key, value := range db.mem { 234 if value.delete { 235 if err := db.batch.Delete([]byte(key)); err != nil { 236 return nil, err 237 } 238 } else if err := db.batch.Put([]byte(key), value.value); err != nil { 239 return nil, err 240 } 241 } 242 243 return db.batch, nil 244 } 245 246 func (db *Database) Close() error { 247 db.lock.Lock() 248 defer db.lock.Unlock() 249 250 if db.mem == nil { 251 return database.ErrClosed 252 } 253 db.batch = nil 254 db.mem = nil 255 db.db = nil 256 return nil 257 } 258 259 func (db *Database) isClosed() bool { 260 db.lock.RLock() 261 defer db.lock.RUnlock() 262 263 return db.db == nil 264 } 265 266 func (db *Database) HealthCheck(ctx context.Context) (interface{}, error) { 267 db.lock.RLock() 268 defer db.lock.RUnlock() 269 270 if db.mem == nil { 271 return nil, database.ErrClosed 272 } 273 return db.db.HealthCheck(ctx) 274 } 275 276 type batch struct { 277 database.BatchOps 278 279 db *Database 280 } 281 282 func (b *batch) Write() error { 283 b.db.lock.Lock() 284 defer b.db.lock.Unlock() 285 286 if b.db.mem == nil { 287 return database.ErrClosed 288 } 289 290 for _, op := range b.Ops { 291 b.db.mem[string(op.Key)] = valueDelete{ 292 value: op.Value, 293 delete: op.Delete, 294 } 295 } 296 return nil 297 } 298 299 func (b *batch) Inner() database.Batch { 300 return b 301 } 302 303 // iterator walks over both the in memory database and the underlying database 304 // at the same time. 305 type iterator struct { 306 db *Database 307 database.Iterator 308 309 key, value []byte 310 err error 311 312 keys []string 313 values []valueDelete 314 315 initialized, exhausted bool 316 } 317 318 // Next moves the iterator to the next key/value pair. It returns whether the 319 // iterator is exhausted. We must pay careful attention to set the proper values 320 // based on if the in memory db or the underlying db should be read next 321 func (it *iterator) Next() bool { 322 // Short-circuit and set an error if the underlying database has been closed. 323 if it.db.isClosed() { 324 it.key = nil 325 it.value = nil 326 it.err = database.ErrClosed 327 return false 328 } 329 330 if !it.initialized { 331 it.exhausted = !it.Iterator.Next() 332 it.initialized = true 333 } 334 335 for { 336 switch { 337 case it.exhausted && len(it.keys) == 0: 338 it.key = nil 339 it.value = nil 340 return false 341 case it.exhausted: 342 nextKey := it.keys[0] 343 nextValue := it.values[0] 344 345 it.keys[0] = "" 346 it.keys = it.keys[1:] 347 it.values[0].value = nil 348 it.values = it.values[1:] 349 350 if !nextValue.delete { 351 it.key = []byte(nextKey) 352 it.value = nextValue.value 353 return true 354 } 355 case len(it.keys) == 0: 356 it.key = it.Iterator.Key() 357 it.value = it.Iterator.Value() 358 it.exhausted = !it.Iterator.Next() 359 return true 360 default: 361 memKey := it.keys[0] 362 memValue := it.values[0] 363 364 dbKey := it.Iterator.Key() 365 366 dbStringKey := string(dbKey) 367 switch { 368 case memKey < dbStringKey: 369 it.keys[0] = "" 370 it.keys = it.keys[1:] 371 it.values[0].value = nil 372 it.values = it.values[1:] 373 374 if !memValue.delete { 375 it.key = []byte(memKey) 376 it.value = memValue.value 377 return true 378 } 379 case dbStringKey < memKey: 380 it.key = dbKey 381 it.value = it.Iterator.Value() 382 it.exhausted = !it.Iterator.Next() 383 return true 384 default: 385 it.keys[0] = "" 386 it.keys = it.keys[1:] 387 it.values[0].value = nil 388 it.values = it.values[1:] 389 it.exhausted = !it.Iterator.Next() 390 391 if !memValue.delete { 392 it.key = []byte(memKey) 393 it.value = memValue.value 394 return true 395 } 396 } 397 } 398 } 399 } 400 401 func (it *iterator) Error() error { 402 if it.err != nil { 403 return it.err 404 } 405 return it.Iterator.Error() 406 } 407 408 func (it *iterator) Key() []byte { 409 return it.key 410 } 411 412 func (it *iterator) Value() []byte { 413 return it.value 414 } 415 416 func (it *iterator) Release() { 417 it.key = nil 418 it.value = nil 419 it.keys = nil 420 it.values = nil 421 it.Iterator.Release() 422 }