github.com/lbryio/lbcd@v0.22.119/database/ffldb/dbcache.go (about) 1 // Copyright (c) 2015-2016 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package ffldb 6 7 import ( 8 "bytes" 9 "fmt" 10 "sync" 11 "time" 12 13 "github.com/lbryio/lbcd/database/internal/treap" 14 "github.com/syndtr/goleveldb/leveldb" 15 "github.com/syndtr/goleveldb/leveldb/iterator" 16 "github.com/syndtr/goleveldb/leveldb/util" 17 ) 18 19 const ( 20 // defaultCacheSize is the default size for the database cache. 21 defaultCacheSize = 100 * 1024 * 1024 // 100 MB 22 23 // defaultFlushSecs is the default number of seconds to use as a 24 // threshold in between database cache flushes when the cache size has 25 // not been exceeded. 26 defaultFlushSecs = 120 // 2 minutes 27 28 // ldbBatchHeaderSize is the size of a leveldb batch header which 29 // includes the sequence header and record counter. 30 // 31 // ldbRecordIKeySize is the size of the ikey used internally by leveldb 32 // when appending a record to a batch. 33 // 34 // These are used to help preallocate space needed for a batch in one 35 // allocation instead of letting leveldb itself constantly grow it. 36 // This results in far less pressure on the GC and consequently helps 37 // prevent the GC from allocating a lot of extra unneeded space. 38 ldbBatchHeaderSize = 12 39 ldbRecordIKeySize = 8 40 ) 41 42 // ldbCacheIter wraps a treap iterator to provide the additional functionality 43 // needed to satisfy the leveldb iterator.Iterator interface. 44 type ldbCacheIter struct { 45 *treap.Iterator 46 } 47 48 // Enforce ldbCacheIterator implements the leveldb iterator.Iterator interface. 49 var _ iterator.Iterator = (*ldbCacheIter)(nil) 50 51 // Error is only provided to satisfy the iterator interface as there are no 52 // errors for this memory-only structure. 53 // 54 // This is part of the leveldb iterator.Iterator interface implementation. 55 func (iter *ldbCacheIter) Error() error { 56 return nil 57 } 58 59 // SetReleaser is only provided to satisfy the iterator interface as there is no 60 // need to override it. 61 // 62 // This is part of the leveldb iterator.Iterator interface implementation. 63 func (iter *ldbCacheIter) SetReleaser(releaser util.Releaser) { 64 } 65 66 // Release is only provided to satisfy the iterator interface. 67 // 68 // This is part of the leveldb iterator.Iterator interface implementation. 69 func (iter *ldbCacheIter) Release() { 70 } 71 72 // newLdbCacheIter creates a new treap iterator for the given slice against the 73 // pending keys for the passed cache snapshot and returns it wrapped in an 74 // ldbCacheIter so it can be used as a leveldb iterator. 75 func newLdbCacheIter(snap *dbCacheSnapshot, slice *util.Range) *ldbCacheIter { 76 iter := snap.pendingKeys.Iterator(slice.Start, slice.Limit) 77 return &ldbCacheIter{Iterator: iter} 78 } 79 80 // dbCacheIterator defines an iterator over the key/value pairs in the database 81 // cache and underlying database. 82 type dbCacheIterator struct { 83 cacheSnapshot *dbCacheSnapshot 84 dbIter iterator.Iterator 85 cacheIter iterator.Iterator 86 currentIter iterator.Iterator 87 released bool 88 } 89 90 // Enforce dbCacheIterator implements the leveldb iterator.Iterator interface. 91 var _ iterator.Iterator = (*dbCacheIterator)(nil) 92 93 // skipPendingUpdates skips any keys at the current database iterator position 94 // that are being updated by the cache. The forwards flag indicates the 95 // direction the iterator is moving. 96 func (iter *dbCacheIterator) skipPendingUpdates(forwards bool) { 97 for iter.dbIter.Valid() { 98 var skip bool 99 key := iter.dbIter.Key() 100 if iter.cacheSnapshot.pendingRemove.Has(key) { 101 skip = true 102 } else if iter.cacheSnapshot.pendingKeys.Has(key) { 103 skip = true 104 } 105 if !skip { 106 break 107 } 108 109 if forwards { 110 iter.dbIter.Next() 111 } else { 112 iter.dbIter.Prev() 113 } 114 } 115 } 116 117 // chooseIterator first skips any entries in the database iterator that are 118 // being updated by the cache and sets the current iterator to the appropriate 119 // iterator depending on their validity and the order they compare in while taking 120 // into account the direction flag. When the iterator is being moved forwards 121 // and both iterators are valid, the iterator with the smaller key is chosen and 122 // vice versa when the iterator is being moved backwards. 123 func (iter *dbCacheIterator) chooseIterator(forwards bool) bool { 124 // Skip any keys at the current database iterator position that are 125 // being updated by the cache. 126 iter.skipPendingUpdates(forwards) 127 128 // When both iterators are exhausted, the iterator is exhausted too. 129 if !iter.dbIter.Valid() && !iter.cacheIter.Valid() { 130 iter.currentIter = nil 131 return false 132 } 133 134 // Choose the database iterator when the cache iterator is exhausted. 135 if !iter.cacheIter.Valid() { 136 iter.currentIter = iter.dbIter 137 return true 138 } 139 140 // Choose the cache iterator when the database iterator is exhausted. 141 if !iter.dbIter.Valid() { 142 iter.currentIter = iter.cacheIter 143 return true 144 } 145 146 // Both iterators are valid, so choose the iterator with either the 147 // smaller or larger key depending on the forwards flag. 148 compare := bytes.Compare(iter.dbIter.Key(), iter.cacheIter.Key()) 149 if (forwards && compare > 0) || (!forwards && compare < 0) { 150 iter.currentIter = iter.cacheIter 151 } else { 152 iter.currentIter = iter.dbIter 153 } 154 return true 155 } 156 157 // First positions the iterator at the first key/value pair and returns whether 158 // or not the pair exists. 159 // 160 // This is part of the leveldb iterator.Iterator interface implementation. 161 func (iter *dbCacheIterator) First() bool { 162 // Seek to the first key in both the database and cache iterators and 163 // choose the iterator that is both valid and has the smaller key. 164 iter.dbIter.First() 165 iter.cacheIter.First() 166 return iter.chooseIterator(true) 167 } 168 169 // Last positions the iterator at the last key/value pair and returns whether or 170 // not the pair exists. 171 // 172 // This is part of the leveldb iterator.Iterator interface implementation. 173 func (iter *dbCacheIterator) Last() bool { 174 // Seek to the last key in both the database and cache iterators and 175 // choose the iterator that is both valid and has the larger key. 176 iter.dbIter.Last() 177 iter.cacheIter.Last() 178 return iter.chooseIterator(false) 179 } 180 181 // Next moves the iterator one key/value pair forward and returns whether or not 182 // the pair exists. 183 // 184 // This is part of the leveldb iterator.Iterator interface implementation. 185 func (iter *dbCacheIterator) Next() bool { 186 // Nothing to return if cursor is exhausted. 187 if iter.currentIter == nil { 188 return false 189 } 190 191 // Move the current iterator to the next entry and choose the iterator 192 // that is both valid and has the smaller key. 193 iter.currentIter.Next() 194 return iter.chooseIterator(true) 195 } 196 197 // Prev moves the iterator one key/value pair backward and returns whether or 198 // not the pair exists. 199 // 200 // This is part of the leveldb iterator.Iterator interface implementation. 201 func (iter *dbCacheIterator) Prev() bool { 202 // Nothing to return if cursor is exhausted. 203 if iter.currentIter == nil { 204 return false 205 } 206 207 // Move the current iterator to the previous entry and choose the 208 // iterator that is both valid and has the larger key. 209 iter.currentIter.Prev() 210 return iter.chooseIterator(false) 211 } 212 213 // Seek positions the iterator at the first key/value pair that is greater than 214 // or equal to the passed seek key. Returns false if no suitable key was found. 215 // 216 // This is part of the leveldb iterator.Iterator interface implementation. 217 func (iter *dbCacheIterator) Seek(key []byte) bool { 218 // Seek to the provided key in both the database and cache iterators 219 // then choose the iterator that is both valid and has the larger key. 220 iter.dbIter.Seek(key) 221 iter.cacheIter.Seek(key) 222 return iter.chooseIterator(true) 223 } 224 225 // Valid indicates whether the iterator is positioned at a valid key/value pair. 226 // It will be considered invalid when the iterator is newly created or exhausted. 227 // 228 // This is part of the leveldb iterator.Iterator interface implementation. 229 func (iter *dbCacheIterator) Valid() bool { 230 return iter.currentIter != nil 231 } 232 233 // Key returns the current key the iterator is pointing to. 234 // 235 // This is part of the leveldb iterator.Iterator interface implementation. 236 func (iter *dbCacheIterator) Key() []byte { 237 // Nothing to return if iterator is exhausted. 238 if iter.currentIter == nil { 239 return nil 240 } 241 242 return iter.currentIter.Key() 243 } 244 245 // Value returns the current value the iterator is pointing to. 246 // 247 // This is part of the leveldb iterator.Iterator interface implementation. 248 func (iter *dbCacheIterator) Value() []byte { 249 // Nothing to return if iterator is exhausted. 250 if iter.currentIter == nil { 251 return nil 252 } 253 254 return iter.currentIter.Value() 255 } 256 257 // SetReleaser is only provided to satisfy the iterator interface as there is no 258 // need to override it. 259 // 260 // This is part of the leveldb iterator.Iterator interface implementation. 261 func (iter *dbCacheIterator) SetReleaser(releaser util.Releaser) { 262 } 263 264 // Release releases the iterator by removing the underlying treap iterator from 265 // the list of active iterators against the pending keys treap. 266 // 267 // This is part of the leveldb iterator.Iterator interface implementation. 268 func (iter *dbCacheIterator) Release() { 269 if !iter.released { 270 iter.dbIter.Release() 271 iter.cacheIter.Release() 272 iter.currentIter = nil 273 iter.released = true 274 } 275 } 276 277 // Error is only provided to satisfy the iterator interface as there are no 278 // errors for this memory-only structure. 279 // 280 // This is part of the leveldb iterator.Iterator interface implementation. 281 func (iter *dbCacheIterator) Error() error { 282 return nil 283 } 284 285 // dbCacheSnapshot defines a snapshot of the database cache and underlying 286 // database at a particular point in time. 287 type dbCacheSnapshot struct { 288 dbSnapshot *leveldb.Snapshot 289 pendingKeys *treap.Immutable 290 pendingRemove *treap.Immutable 291 } 292 293 // Has returns whether or not the passed key exists. 294 func (snap *dbCacheSnapshot) Has(key []byte) bool { 295 // Check the cached entries first. 296 if snap.pendingRemove.Has(key) { 297 return false 298 } 299 if snap.pendingKeys.Has(key) { 300 return true 301 } 302 303 // Consult the database. 304 hasKey, _ := snap.dbSnapshot.Has(key, nil) 305 return hasKey 306 } 307 308 // Get returns the value for the passed key. The function will return nil when 309 // the key does not exist. 310 func (snap *dbCacheSnapshot) Get(key []byte) []byte { 311 // Check the cached entries first. 312 if snap.pendingRemove.Has(key) { 313 return nil 314 } 315 if value := snap.pendingKeys.Get(key); value != nil { 316 return value 317 } 318 319 // Consult the database. 320 value, err := snap.dbSnapshot.Get(key, nil) 321 if err != nil { 322 return nil 323 } 324 return value 325 } 326 327 // Release releases the snapshot. 328 func (snap *dbCacheSnapshot) Release() { 329 snap.dbSnapshot.Release() 330 snap.pendingKeys = nil 331 snap.pendingRemove = nil 332 } 333 334 // NewIterator returns a new iterator for the snapshot. The newly returned 335 // iterator is not pointing to a valid item until a call to one of the methods 336 // to position it is made. 337 // 338 // The slice parameter allows the iterator to be limited to a range of keys. 339 // The start key is inclusive and the limit key is exclusive. Either or both 340 // can be nil if the functionality is not desired. 341 func (snap *dbCacheSnapshot) NewIterator(slice *util.Range) *dbCacheIterator { 342 return &dbCacheIterator{ 343 dbIter: snap.dbSnapshot.NewIterator(slice, nil), 344 cacheIter: newLdbCacheIter(snap, slice), 345 cacheSnapshot: snap, 346 } 347 } 348 349 // dbCache provides a database cache layer backed by an underlying database. It 350 // allows a maximum cache size and flush interval to be specified such that the 351 // cache is flushed to the database when the cache size exceeds the maximum 352 // configured value or it has been longer than the configured interval since the 353 // last flush. This effectively provides transaction batching so that callers 354 // can commit transactions at will without incurring large performance hits due 355 // to frequent disk syncs. 356 type dbCache struct { 357 // ldb is the underlying leveldb DB for metadata. 358 ldb *leveldb.DB 359 360 // store is used to sync blocks to flat files. 361 store *blockStore 362 363 // The following fields are related to flushing the cache to persistent 364 // storage. Note that all flushing is performed in an opportunistic 365 // fashion. This means that it is only flushed during a transaction or 366 // when the database cache is closed. 367 // 368 // maxSize is the maximum size threshold the cache can grow to before 369 // it is flushed. 370 // 371 // flushInterval is the threshold interval of time that is allowed to 372 // pass before the cache is flushed. 373 // 374 // lastFlush is the time the cache was last flushed. It is used in 375 // conjunction with the current time and the flush interval. 376 // 377 // NOTE: These flush related fields are protected by the database write 378 // lock. 379 maxSize uint64 380 flushInterval time.Duration 381 lastFlush time.Time 382 383 // The following fields hold the keys that need to be stored or deleted 384 // from the underlying database once the cache is full, enough time has 385 // passed, or when the database is shutting down. Note that these are 386 // stored using immutable treaps to support O(1) MVCC snapshots against 387 // the cached data. The cacheLock is used to protect concurrent access 388 // for cache updates and snapshots. 389 cacheLock sync.RWMutex 390 cachedKeys *treap.Immutable 391 cachedRemove *treap.Immutable 392 } 393 394 // Snapshot returns a snapshot of the database cache and underlying database at 395 // a particular point in time. 396 // 397 // The snapshot must be released after use by calling Release. 398 func (c *dbCache) Snapshot() (*dbCacheSnapshot, error) { 399 dbSnapshot, err := c.ldb.GetSnapshot() 400 if err != nil { 401 str := "failed to open transaction" 402 return nil, convertErr(str, err) 403 } 404 405 // Since the cached keys to be added and removed use an immutable treap, 406 // a snapshot is simply obtaining the root of the tree under the lock 407 // which is used to atomically swap the root. 408 c.cacheLock.RLock() 409 cacheSnapshot := &dbCacheSnapshot{ 410 dbSnapshot: dbSnapshot, 411 pendingKeys: c.cachedKeys, 412 pendingRemove: c.cachedRemove, 413 } 414 c.cacheLock.RUnlock() 415 return cacheSnapshot, nil 416 } 417 418 // updateDB invokes the passed function in the context of a managed leveldb 419 // transaction. Any errors returned from the user-supplied function will cause 420 // the transaction to be rolled back and are returned from this function. 421 // Otherwise, the transaction is committed when the user-supplied function 422 // returns a nil error. 423 func (c *dbCache) updateDB(fn func(ldbTx *leveldb.Transaction) error) error { 424 // Start a leveldb transaction. 425 ldbTx, err := c.ldb.OpenTransaction() 426 if err != nil { 427 return convertErr("failed to open ldb transaction", err) 428 } 429 430 if err := fn(ldbTx); err != nil { 431 ldbTx.Discard() 432 return err 433 } 434 435 // Commit the leveldb transaction and convert any errors as needed. 436 if err := ldbTx.Commit(); err != nil { 437 return convertErr("failed to commit leveldb transaction", err) 438 } 439 return nil 440 } 441 442 // TreapForEacher is an interface which allows iteration of a treap in ascending 443 // order using a user-supplied callback for each key/value pair. It mainly 444 // exists so both mutable and immutable treaps can be atomically committed to 445 // the database with the same function. 446 type TreapForEacher interface { 447 ForEach(func(k, v []byte) bool) 448 } 449 450 // commitTreaps atomically commits all of the passed pending add/update/remove 451 // updates to the underlying database. 452 func (c *dbCache) commitTreaps(pendingKeys, pendingRemove TreapForEacher) error { 453 // Perform all leveldb updates using an atomic transaction. 454 return c.updateDB(func(ldbTx *leveldb.Transaction) error { 455 var innerErr error 456 pendingKeys.ForEach(func(k, v []byte) bool { 457 if dbErr := ldbTx.Put(k, v, nil); dbErr != nil { 458 str := fmt.Sprintf("failed to put key %q to "+ 459 "ldb transaction", k) 460 innerErr = convertErr(str, dbErr) 461 return false 462 } 463 return true 464 }) 465 if innerErr != nil { 466 return innerErr 467 } 468 469 pendingRemove.ForEach(func(k, v []byte) bool { 470 if dbErr := ldbTx.Delete(k, nil); dbErr != nil { 471 str := fmt.Sprintf("failed to delete "+ 472 "key %q from ldb transaction", 473 k) 474 innerErr = convertErr(str, dbErr) 475 return false 476 } 477 return true 478 }) 479 return innerErr 480 }) 481 } 482 483 // flush flushes the database cache to persistent storage. This involes syncing 484 // the block store and replaying all transactions that have been applied to the 485 // cache to the underlying database. 486 // 487 // This function MUST be called with the database write lock held. 488 func (c *dbCache) flush() error { 489 c.lastFlush = time.Now() 490 491 // Sync the current write file associated with the block store. This is 492 // necessary before writing the metadata to prevent the case where the 493 // metadata contains information about a block which actually hasn't 494 // been written yet in unexpected shutdown scenarios. 495 if err := c.store.syncBlocks(); err != nil { 496 return err 497 } 498 499 // Since the cached keys to be added and removed use an immutable treap, 500 // a snapshot is simply obtaining the root of the tree under the lock 501 // which is used to atomically swap the root. 502 c.cacheLock.Lock() 503 cachedKeys := c.cachedKeys 504 cachedRemove := c.cachedRemove 505 c.cachedKeys = treap.NewImmutable() 506 c.cachedRemove = treap.NewImmutable() 507 c.cacheLock.Unlock() 508 509 // Nothing to do if there is no data to flush. 510 if cachedKeys.Len() == 0 && cachedRemove.Len() == 0 { 511 return nil 512 } 513 514 // Perform all leveldb updates using an atomic transaction. 515 if err := c.commitTreaps(cachedKeys, cachedRemove); err != nil { 516 return err 517 } 518 519 return nil 520 } 521 522 // needsFlush returns whether or not the database cache needs to be flushed to 523 // persistent storage based on its current size, whether or not adding all of 524 // the entries in the passed database transaction would cause it to exceed the 525 // configured limit, and how much time has elapsed since the last time the cache 526 // was flushed. 527 // 528 // This function MUST be called with the database write lock held. 529 func (c *dbCache) needsFlush(tx *transaction) bool { 530 // A flush is needed when more time has elapsed than the configured 531 // flush interval. 532 if time.Since(c.lastFlush) > c.flushInterval { 533 return true 534 } 535 536 // A flush is needed when the size of the database cache exceeds the 537 // specified max cache size. The total calculated size is multiplied by 538 // 1.5 here to account for additional memory consumption that will be 539 // needed during the flush as well as old nodes in the cache that are 540 // referenced by the snapshot used by the transaction. 541 snap := tx.snapshot 542 totalSize := snap.pendingKeys.Size() + snap.pendingRemove.Size() 543 totalSize = uint64(float64(totalSize) * 1.5) 544 return totalSize > c.maxSize 545 } 546 547 // commitTx atomically adds all of the pending keys to add and remove into the 548 // database cache. When adding the pending keys would cause the size of the 549 // cache to exceed the max cache size, or the time since the last flush exceeds 550 // the configured flush interval, the cache will be flushed to the underlying 551 // persistent database. 552 // 553 // This is an atomic operation with respect to the cache in that either all of 554 // the pending keys to add and remove in the transaction will be applied or none 555 // of them will. 556 // 557 // The database cache itself might be flushed to the underlying persistent 558 // database even if the transaction fails to apply, but it will only be the 559 // state of the cache without the transaction applied. 560 // 561 // This function MUST be called during a database write transaction which in 562 // turn implies the database write lock will be held. 563 func (c *dbCache) commitTx(tx *transaction) error { 564 // Flush the cache and write the current transaction directly to the 565 // database if a flush is needed. 566 if c.needsFlush(tx) { 567 if err := c.flush(); err != nil { 568 return err 569 } 570 571 // Perform all leveldb updates using an atomic transaction. 572 err := c.commitTreaps(tx.pendingKeys, tx.pendingRemove) 573 if err != nil { 574 return err 575 } 576 577 // Clear the transaction entries since they have been committed. 578 tx.pendingKeys = nil 579 tx.pendingRemove = nil 580 return nil 581 } 582 583 // At this point a database flush is not needed, so atomically commit 584 // the transaction to the cache. 585 586 // Since the cached keys to be added and removed use an immutable treap, 587 // a snapshot is simply obtaining the root of the tree under the lock 588 // which is used to atomically swap the root. 589 c.cacheLock.RLock() 590 newCachedKeys := c.cachedKeys 591 newCachedRemove := c.cachedRemove 592 c.cacheLock.RUnlock() 593 594 // Apply every key to add in the database transaction to the cache. 595 tx.pendingKeys.ForEach(func(k, v []byte) bool { 596 newCachedRemove = newCachedRemove.Delete(k) 597 newCachedKeys = newCachedKeys.Put(k, v) 598 return true 599 }) 600 tx.pendingKeys = nil 601 602 // Apply every key to remove in the database transaction to the cache. 603 tx.pendingRemove.ForEach(func(k, v []byte) bool { 604 newCachedKeys = newCachedKeys.Delete(k) 605 newCachedRemove = newCachedRemove.Put(k, nil) 606 return true 607 }) 608 tx.pendingRemove = nil 609 610 // Atomically replace the immutable treaps which hold the cached keys to 611 // add and delete. 612 c.cacheLock.Lock() 613 c.cachedKeys = newCachedKeys 614 c.cachedRemove = newCachedRemove 615 c.cacheLock.Unlock() 616 return nil 617 } 618 619 // Close cleanly shuts down the database cache by syncing all data and closing 620 // the underlying leveldb database. 621 // 622 // This function MUST be called with the database write lock held. 623 func (c *dbCache) Close() error { 624 // Flush any outstanding cached entries to disk. 625 if err := c.flush(); err != nil { 626 // Even if there is an error while flushing, attempt to close 627 // the underlying database. The error is ignored since it would 628 // mask the flush error. 629 _ = c.ldb.Close() 630 return err 631 } 632 633 // Close the underlying leveldb database. 634 if err := c.ldb.Close(); err != nil { 635 str := "failed to close underlying leveldb database" 636 return convertErr(str, err) 637 } 638 639 return nil 640 } 641 642 // newDbCache returns a new database cache instance backed by the provided 643 // leveldb instance. The cache will be flushed to leveldb when the max size 644 // exceeds the provided value or it has been longer than the provided interval 645 // since the last flush. 646 func newDbCache(ldb *leveldb.DB, store *blockStore, maxSize uint64, flushIntervalSecs uint32) *dbCache { 647 return &dbCache{ 648 ldb: ldb, 649 store: store, 650 maxSize: maxSize, 651 flushInterval: time.Second * time.Duration(flushIntervalSecs), 652 lastFlush: time.Now(), 653 cachedKeys: treap.NewImmutable(), 654 cachedRemove: treap.NewImmutable(), 655 } 656 }