github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/storage/ldbstore.go (about) 1 // Copyleft 2016 The susy-graviton Authors 2 // This file is part of the susy-graviton library. 3 // 4 // The susy-graviton library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The susy-graviton library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MSRCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the susy-graviton library. If not, see <http://www.gnu.org/licenses/>. 16 17 // disk storage layer for the package bzz 18 // DbStore implements the ChunkStore interface and is used by the FileStore as 19 // persistent storage of chunks 20 // it implements purging based on access count allowing for external control of 21 // max capacity 22 23 package storage 24 25 import ( 26 "archive/tar" 27 "bytes" 28 "context" 29 "encoding/binary" 30 "encoding/hex" 31 "errors" 32 "fmt" 33 "io" 34 "io/ioutil" 35 "sync" 36 37 "github.com/susy-go/susy-graviton/metrics" 38 "github.com/susy-go/susy-graviton/srlp" 39 "github.com/susy-go/susy-graviton/swarm/log" 40 "github.com/susy-go/susy-graviton/swarm/storage/mock" 41 "github.com/syndtr/goleveldb/leveldb" 42 ) 43 44 const ( 45 defaultGCRatio = 10 46 defaultMaxGCRound = 10000 47 defaultMaxGCBatch = 5000 48 49 wEntryCnt = 1 << 0 50 wIndexCnt = 1 << 1 51 wAccessCnt = 1 << 2 52 ) 53 54 var ( 55 dbEntryCount = metrics.NewRegisteredCounter("ldbstore.entryCnt", nil) 56 ) 57 58 var ( 59 keyIndex = byte(0) 60 keyAccessCnt = []byte{2} 61 keyEntryCnt = []byte{3} 62 keyDataIdx = []byte{4} 63 keyData = byte(6) 64 keyDistanceCnt = byte(7) 65 keySchema = []byte{8} 66 keyGCIdx = byte(9) // access to chunk data index, used by garbage collection in ascending order from first entry 67 ) 68 69 var ( 70 ErrDBClosed = errors.New("LDBStore closed") 71 ) 72 73 type LDBStoreParams struct { 74 *StoreParams 75 Path string 76 Po func(Address) uint8 77 } 78 79 // NewLDBStoreParams constructs LDBStoreParams with the specified values. 80 func NewLDBStoreParams(storeparams *StoreParams, path string) *LDBStoreParams { 81 return &LDBStoreParams{ 82 StoreParams: storeparams, 83 Path: path, 84 Po: func(k Address) (ret uint8) { return uint8(Proximity(storeparams.BaseKey, k[:])) }, 85 } 86 } 87 88 type garbage struct { 89 maxRound int // maximum number of chunks to delete in one garbage collection round 90 maxBatch int // maximum number of chunks to delete in one db request batch 91 ratio int // 1/x ratio to calculate the number of chunks to gc on a low capacity db 92 count int // number of chunks deleted in running round 93 target int // number of chunks to delete in running round 94 batch *dbBatch // the delete batch 95 runC chan struct{} // struct in chan means gc is NOT running 96 } 97 98 type LDBStore struct { 99 db *LDBDatabase 100 101 // this should be stored in db, accessed transactionally 102 entryCnt uint64 // number of items in the LevelDB 103 accessCnt uint64 // ever-accumulating number increased every time we read/access an entry 104 dataIdx uint64 // similar to entryCnt, but we only increment it 105 capacity uint64 106 bucketCnt []uint64 107 108 hashfunc SwarmHasher 109 po func(Address) uint8 110 111 batchesC chan struct{} 112 closed bool 113 batch *dbBatch 114 lock sync.RWMutex 115 quit chan struct{} 116 gc *garbage 117 118 // Functions encodeDataFunc is used to bypass 119 // the default functionality of DbStore with 120 // mock.NodeStore for testing purposes. 121 encodeDataFunc func(chunk Chunk) []byte 122 // If getDataFunc is defined, it will be used for 123 // retrieving the chunk data instead from the local 124 // LevelDB database. 125 getDataFunc func(key Address) (data []byte, err error) 126 } 127 128 type dbBatch struct { 129 *leveldb.Batch 130 err error 131 c chan struct{} 132 } 133 134 func newBatch() *dbBatch { 135 return &dbBatch{Batch: new(leveldb.Batch), c: make(chan struct{})} 136 } 137 138 // TODO: Instead of passing the distance function, just pass the address from which distances are calculated 139 // to avoid the appearance of a pluggable distance metric and opportunities of bugs associated with providing 140 // a function different from the one that is actually used. 141 func NewLDBStore(params *LDBStoreParams) (s *LDBStore, err error) { 142 s = new(LDBStore) 143 s.hashfunc = params.Hash 144 s.quit = make(chan struct{}) 145 146 s.batchesC = make(chan struct{}, 1) 147 go s.writeBatches() 148 s.batch = newBatch() 149 // associate encodeData with default functionality 150 s.encodeDataFunc = encodeData 151 152 s.db, err = NewLDBDatabase(params.Path) 153 if err != nil { 154 return nil, err 155 } 156 157 s.po = params.Po 158 s.setCapacity(params.DbCapacity) 159 160 s.bucketCnt = make([]uint64, 0x100) 161 for i := 0; i < 0x100; i++ { 162 k := make([]byte, 2) 163 k[0] = keyDistanceCnt 164 k[1] = uint8(i) 165 cnt, _ := s.db.Get(k) 166 s.bucketCnt[i] = BytesToU64(cnt) 167 } 168 data, _ := s.db.Get(keyEntryCnt) 169 s.entryCnt = BytesToU64(data) 170 data, _ = s.db.Get(keyAccessCnt) 171 s.accessCnt = BytesToU64(data) 172 data, _ = s.db.Get(keyDataIdx) 173 s.dataIdx = BytesToU64(data) 174 175 // set up garbage collection 176 s.gc = &garbage{ 177 maxBatch: defaultMaxGCBatch, 178 maxRound: defaultMaxGCRound, 179 ratio: defaultGCRatio, 180 } 181 182 s.gc.runC = make(chan struct{}, 1) 183 s.gc.runC <- struct{}{} 184 185 return s, nil 186 } 187 188 // MarkAccessed increments the access counter as a best effort for a chunk, so 189 // the chunk won't get garbage collected. 190 func (s *LDBStore) MarkAccessed(addr Address) { 191 s.lock.Lock() 192 defer s.lock.Unlock() 193 194 if s.closed { 195 return 196 } 197 198 proximity := s.po(addr) 199 s.tryAccessIdx(addr, proximity) 200 } 201 202 // initialize and set values for processing of gc round 203 func (s *LDBStore) startGC(c int) { 204 205 s.gc.count = 0 206 // calculate the target number of deletions 207 if c >= s.gc.maxRound { 208 s.gc.target = s.gc.maxRound 209 } else { 210 s.gc.target = c / s.gc.ratio 211 } 212 s.gc.batch = newBatch() 213 log.Debug("startgc", "requested", c, "target", s.gc.target) 214 } 215 216 // NewMockDbStore creates a new instance of DbStore with 217 // mockStore set to a provided value. If mockStore argument is nil, 218 // this function behaves exactly as NewDbStore. 219 func NewMockDbStore(params *LDBStoreParams, mockStore *mock.NodeStore) (s *LDBStore, err error) { 220 s, err = NewLDBStore(params) 221 if err != nil { 222 return nil, err 223 } 224 225 // replace put and get with mock store functionality 226 if mockStore != nil { 227 s.encodeDataFunc = newMockEncodeDataFunc(mockStore) 228 s.getDataFunc = newMockGetDataFunc(mockStore) 229 } 230 return 231 } 232 233 type dpaDBIndex struct { 234 Idx uint64 235 Access uint64 236 } 237 238 func BytesToU64(data []byte) uint64 { 239 if len(data) < 8 { 240 return 0 241 } 242 return binary.BigEndian.Uint64(data) 243 } 244 245 func U64ToBytes(val uint64) []byte { 246 data := make([]byte, 8) 247 binary.BigEndian.PutUint64(data, val) 248 return data 249 } 250 251 func getIndexKey(hash Address) []byte { 252 hashSize := len(hash) 253 key := make([]byte, hashSize+1) 254 key[0] = keyIndex 255 copy(key[1:], hash[:]) 256 return key 257 } 258 259 func getDataKey(idx uint64, po uint8) []byte { 260 key := make([]byte, 10) 261 key[0] = keyData 262 key[1] = po 263 binary.BigEndian.PutUint64(key[2:], idx) 264 265 return key 266 } 267 268 func getGCIdxKey(index *dpaDBIndex) []byte { 269 key := make([]byte, 9) 270 key[0] = keyGCIdx 271 binary.BigEndian.PutUint64(key[1:], index.Access) 272 return key 273 } 274 275 func getGCIdxValue(index *dpaDBIndex, po uint8, addr Address) []byte { 276 val := make([]byte, 41) // po = 1, index.Index = 8, Address = 32 277 val[0] = po 278 binary.BigEndian.PutUint64(val[1:], index.Idx) 279 copy(val[9:], addr) 280 return val 281 } 282 283 func parseIdxKey(key []byte) (byte, []byte) { 284 return key[0], key[1:] 285 } 286 287 func parseGCIdxEntry(accessCnt []byte, val []byte) (index *dpaDBIndex, po uint8, addr Address) { 288 index = &dpaDBIndex{ 289 Idx: binary.BigEndian.Uint64(val[1:]), 290 Access: binary.BigEndian.Uint64(accessCnt), 291 } 292 po = val[0] 293 addr = val[9:] 294 return 295 } 296 297 func encodeIndex(index *dpaDBIndex) []byte { 298 data, _ := srlp.EncodeToBytes(index) 299 return data 300 } 301 302 func encodeData(chunk Chunk) []byte { 303 // Always create a new underlying array for the returned byte slice. 304 // The chunk.Address array may be used in the returned slice which 305 // may be changed later in the code or by the LevelDB, resulting 306 // that the Address is changed as well. 307 return append(append([]byte{}, chunk.Address()[:]...), chunk.Data()...) 308 } 309 310 func decodeIndex(data []byte, index *dpaDBIndex) error { 311 dec := srlp.NewStream(bytes.NewReader(data), 0) 312 return dec.Decode(index) 313 } 314 315 func decodeData(addr Address, data []byte) (*chunk, error) { 316 return NewChunk(addr, data[32:]), nil 317 } 318 319 func (s *LDBStore) collectGarbage() error { 320 // prevent duplicate gc from starting when one is already running 321 select { 322 case <-s.gc.runC: 323 default: 324 return nil 325 } 326 327 s.lock.Lock() 328 entryCnt := s.entryCnt 329 s.lock.Unlock() 330 331 metrics.GetOrRegisterCounter("ldbstore.collectgarbage", nil).Inc(1) 332 333 // calculate the amount of chunks to collect and reset counter 334 s.startGC(int(entryCnt)) 335 log.Debug("collectGarbage", "target", s.gc.target, "entryCnt", entryCnt) 336 337 for s.gc.count < s.gc.target { 338 it := s.db.NewIterator() 339 ok := it.Seek([]byte{keyGCIdx}) 340 var singleIterationCount int 341 342 // every batch needs a lock so we avoid entries changing accessidx in the meantime 343 s.lock.Lock() 344 for ; ok && (singleIterationCount < s.gc.maxBatch); ok = it.Next() { 345 346 // quit if no more access index keys 347 itkey := it.Key() 348 if (itkey == nil) || (itkey[0] != keyGCIdx) { 349 break 350 } 351 352 // get chunk data entry from access index 353 val := it.Value() 354 index, po, hash := parseGCIdxEntry(itkey[1:], val) 355 keyIdx := make([]byte, 33) 356 keyIdx[0] = keyIndex 357 copy(keyIdx[1:], hash) 358 359 // add delete operation to batch 360 s.delete(s.gc.batch.Batch, index, keyIdx, po) 361 singleIterationCount++ 362 s.gc.count++ 363 log.Trace("garbage collect enqueued chunk for deletion", "key", hash) 364 365 // break if target is not on max garbage batch boundary 366 if s.gc.count >= s.gc.target { 367 break 368 } 369 } 370 371 s.writeBatch(s.gc.batch, wEntryCnt) 372 log.Trace("garbage collect batch done", "batch", singleIterationCount, "total", s.gc.count) 373 s.lock.Unlock() 374 it.Release() 375 } 376 377 metrics.GetOrRegisterCounter("ldbstore.collectgarbage.delete", nil).Inc(int64(s.gc.count)) 378 log.Debug("garbage collect done", "c", s.gc.count) 379 s.gc.runC <- struct{}{} 380 381 return nil 382 } 383 384 // Export writes all chunks from the store to a tar archive, returning the 385 // number of chunks written. 386 func (s *LDBStore) Export(out io.Writer) (int64, error) { 387 tw := tar.NewWriter(out) 388 defer tw.Close() 389 390 it := s.db.NewIterator() 391 defer it.Release() 392 var count int64 393 for ok := it.Seek([]byte{keyIndex}); ok; ok = it.Next() { 394 key := it.Key() 395 if (key == nil) || (key[0] != keyIndex) { 396 break 397 } 398 399 var index dpaDBIndex 400 401 hash := key[1:] 402 decodeIndex(it.Value(), &index) 403 po := s.po(hash) 404 datakey := getDataKey(index.Idx, po) 405 log.Trace("store.export", "dkey", fmt.Sprintf("%x", datakey), "dataidx", index.Idx, "po", po) 406 data, err := s.db.Get(datakey) 407 if err != nil { 408 log.Warn(fmt.Sprintf("Chunk %x found but could not be accessed: %v", key, err)) 409 continue 410 } 411 412 hdr := &tar.Header{ 413 Name: hex.EncodeToString(hash), 414 Mode: 0644, 415 Size: int64(len(data)), 416 } 417 if err := tw.WriteHeader(hdr); err != nil { 418 return count, err 419 } 420 if _, err := tw.Write(data); err != nil { 421 return count, err 422 } 423 count++ 424 } 425 426 return count, nil 427 } 428 429 // of chunks read. 430 func (s *LDBStore) Import(in io.Reader) (int64, error) { 431 tr := tar.NewReader(in) 432 433 ctx, cancel := context.WithCancel(context.Background()) 434 defer cancel() 435 436 countC := make(chan int64) 437 errC := make(chan error) 438 var count int64 439 go func() { 440 for { 441 hdr, err := tr.Next() 442 if err == io.EOF { 443 break 444 } else if err != nil { 445 select { 446 case errC <- err: 447 case <-ctx.Done(): 448 } 449 } 450 451 if len(hdr.Name) != 64 { 452 log.Warn("ignoring non-chunk file", "name", hdr.Name) 453 continue 454 } 455 456 keybytes, err := hex.DecodeString(hdr.Name) 457 if err != nil { 458 log.Warn("ignoring invalid chunk file", "name", hdr.Name, "err", err) 459 continue 460 } 461 462 data, err := ioutil.ReadAll(tr) 463 if err != nil { 464 select { 465 case errC <- err: 466 case <-ctx.Done(): 467 } 468 } 469 key := Address(keybytes) 470 chunk := NewChunk(key, data[32:]) 471 472 go func() { 473 select { 474 case errC <- s.Put(ctx, chunk): 475 case <-ctx.Done(): 476 } 477 }() 478 479 count++ 480 } 481 countC <- count 482 }() 483 484 // wait for all chunks to be stored 485 i := int64(0) 486 var total int64 487 for { 488 select { 489 case err := <-errC: 490 if err != nil { 491 return count, err 492 } 493 i++ 494 case total = <-countC: 495 case <-ctx.Done(): 496 return i, ctx.Err() 497 } 498 if total > 0 && i == total { 499 return total, nil 500 } 501 } 502 } 503 504 // Cleanup iterates over the database and deletes chunks if they pass the `f` condition 505 func (s *LDBStore) Cleanup(f func(*chunk) bool) { 506 var errorsFound, removed, total int 507 508 it := s.db.NewIterator() 509 defer it.Release() 510 for ok := it.Seek([]byte{keyIndex}); ok; ok = it.Next() { 511 key := it.Key() 512 if (key == nil) || (key[0] != keyIndex) { 513 break 514 } 515 total++ 516 var index dpaDBIndex 517 err := decodeIndex(it.Value(), &index) 518 if err != nil { 519 log.Warn("Cannot decode") 520 errorsFound++ 521 continue 522 } 523 hash := key[1:] 524 po := s.po(hash) 525 datakey := getDataKey(index.Idx, po) 526 data, err := s.db.Get(datakey) 527 if err != nil { 528 found := false 529 530 // highest possible proximity is 255 531 for po = 1; po <= 255; po++ { 532 datakey = getDataKey(index.Idx, po) 533 data, err = s.db.Get(datakey) 534 if err == nil { 535 found = true 536 break 537 } 538 } 539 540 if !found { 541 log.Warn(fmt.Sprintf("Chunk %x found but count not be accessed with any po", key)) 542 errorsFound++ 543 continue 544 } 545 } 546 547 ck := data[:32] 548 c, err := decodeData(ck, data) 549 if err != nil { 550 log.Error("decodeData error", "err", err) 551 continue 552 } 553 554 cs := int64(binary.LittleEndian.Uint64(c.sdata[:8])) 555 log.Trace("chunk", "key", fmt.Sprintf("%x", key), "ck", fmt.Sprintf("%x", ck), "dkey", fmt.Sprintf("%x", datakey), "dataidx", index.Idx, "po", po, "len data", len(data), "len sdata", len(c.sdata), "size", cs) 556 557 // if chunk is to be removed 558 if f(c) { 559 log.Warn("chunk for cleanup", "key", fmt.Sprintf("%x", key), "ck", fmt.Sprintf("%x", ck), "dkey", fmt.Sprintf("%x", datakey), "dataidx", index.Idx, "po", po, "len data", len(data), "len sdata", len(c.sdata), "size", cs) 560 s.deleteNow(&index, getIndexKey(key[1:]), po) 561 removed++ 562 errorsFound++ 563 } 564 } 565 566 log.Warn(fmt.Sprintf("Found %v errors out of %v entries. Removed %v chunks.", errorsFound, total, removed)) 567 } 568 569 // CleanGCIndex rebuilds the garbage collector index from scratch, while 570 // removing inconsistent elements, e.g., indices with missing data chunks. 571 // WARN: it's a pretty heavy, long running function. 572 func (s *LDBStore) CleanGCIndex() error { 573 s.lock.Lock() 574 defer s.lock.Unlock() 575 576 batch := leveldb.Batch{} 577 578 var okEntryCount uint64 579 var totalEntryCount uint64 580 581 // throw out all gc indices, we will rebuild from cleaned index 582 it := s.db.NewIterator() 583 it.Seek([]byte{keyGCIdx}) 584 var gcDeletes int 585 for it.Valid() { 586 rowType, _ := parseIdxKey(it.Key()) 587 if rowType != keyGCIdx { 588 break 589 } 590 batch.Delete(it.Key()) 591 gcDeletes++ 592 it.Next() 593 } 594 log.Debug("gc", "deletes", gcDeletes) 595 if err := s.db.Write(&batch); err != nil { 596 return err 597 } 598 batch.Reset() 599 600 it.Release() 601 602 // corrected po index pointer values 603 var poPtrs [256]uint64 604 605 // set to true if chunk count not on 4096 iteration boundary 606 var doneIterating bool 607 608 // last key index in previous iteration 609 lastIdxKey := []byte{keyIndex} 610 611 // counter for debug output 612 var cleanBatchCount int 613 614 // go through all key index entries 615 for !doneIterating { 616 cleanBatchCount++ 617 var idxs []dpaDBIndex 618 var chunkHashes [][]byte 619 var pos []uint8 620 it := s.db.NewIterator() 621 622 it.Seek(lastIdxKey) 623 624 // 4096 is just a nice number, don't look for any hidden meaning here... 625 var i int 626 for i = 0; i < 4096; i++ { 627 628 // this really shouldn't happen unless database is empty 629 // but let's keep it to be safe 630 if !it.Valid() { 631 doneIterating = true 632 break 633 } 634 635 // if it's not keyindex anymore we're done iterating 636 rowType, chunkHash := parseIdxKey(it.Key()) 637 if rowType != keyIndex { 638 doneIterating = true 639 break 640 } 641 642 // decode the retrieved index 643 var idx dpaDBIndex 644 err := decodeIndex(it.Value(), &idx) 645 if err != nil { 646 return fmt.Errorf("corrupt index: %v", err) 647 } 648 po := s.po(chunkHash) 649 lastIdxKey = it.Key() 650 651 // if we don't find the data key, remove the entry 652 // if we find it, add to the array of new gc indices to create 653 dataKey := getDataKey(idx.Idx, po) 654 _, err = s.db.Get(dataKey) 655 if err != nil { 656 log.Warn("deleting inconsistent index (missing data)", "key", chunkHash) 657 batch.Delete(it.Key()) 658 } else { 659 idxs = append(idxs, idx) 660 chunkHashes = append(chunkHashes, chunkHash) 661 pos = append(pos, po) 662 okEntryCount++ 663 if idx.Idx > poPtrs[po] { 664 poPtrs[po] = idx.Idx 665 } 666 } 667 totalEntryCount++ 668 it.Next() 669 } 670 it.Release() 671 672 // flush the key index corrections 673 err := s.db.Write(&batch) 674 if err != nil { 675 return err 676 } 677 batch.Reset() 678 679 // add correct gc indices 680 for i, okIdx := range idxs { 681 gcIdxKey := getGCIdxKey(&okIdx) 682 gcIdxData := getGCIdxValue(&okIdx, pos[i], chunkHashes[i]) 683 batch.Put(gcIdxKey, gcIdxData) 684 log.Trace("clean ok", "key", chunkHashes[i], "gcKey", gcIdxKey, "gcData", gcIdxData) 685 } 686 687 // flush them 688 err = s.db.Write(&batch) 689 if err != nil { 690 return err 691 } 692 batch.Reset() 693 694 log.Debug("clean gc index pass", "batch", cleanBatchCount, "checked", i, "kept", len(idxs)) 695 } 696 697 log.Debug("gc cleanup entries", "ok", okEntryCount, "total", totalEntryCount, "batchlen", batch.Len()) 698 699 // lastly add updated entry count 700 var entryCount [8]byte 701 binary.BigEndian.PutUint64(entryCount[:], okEntryCount) 702 batch.Put(keyEntryCnt, entryCount[:]) 703 704 // and add the new po index pointers 705 var poKey [2]byte 706 poKey[0] = keyDistanceCnt 707 for i, poPtr := range poPtrs { 708 poKey[1] = uint8(i) 709 if poPtr == 0 { 710 batch.Delete(poKey[:]) 711 } else { 712 var idxCount [8]byte 713 binary.BigEndian.PutUint64(idxCount[:], poPtr) 714 batch.Put(poKey[:], idxCount[:]) 715 } 716 } 717 718 // if you made it this far your harddisk has survived. Congratulations 719 return s.db.Write(&batch) 720 } 721 722 // Delete is removes a chunk and updates indices. 723 // Is thread safe 724 func (s *LDBStore) Delete(addr Address) error { 725 s.lock.Lock() 726 defer s.lock.Unlock() 727 728 ikey := getIndexKey(addr) 729 730 idata, err := s.db.Get(ikey) 731 if err != nil { 732 return err 733 } 734 735 var idx dpaDBIndex 736 decodeIndex(idata, &idx) 737 proximity := s.po(addr) 738 return s.deleteNow(&idx, ikey, proximity) 739 } 740 741 // executes one delete operation immediately 742 // see *LDBStore.delete 743 func (s *LDBStore) deleteNow(idx *dpaDBIndex, idxKey []byte, po uint8) error { 744 batch := new(leveldb.Batch) 745 s.delete(batch, idx, idxKey, po) 746 return s.db.Write(batch) 747 } 748 749 // adds a delete chunk operation to the provided batch 750 // if called directly, decrements entrycount regardless if the chunk exists upon deletion. Risk of wrap to max uint64 751 func (s *LDBStore) delete(batch *leveldb.Batch, idx *dpaDBIndex, idxKey []byte, po uint8) { 752 metrics.GetOrRegisterCounter("ldbstore.delete", nil).Inc(1) 753 754 gcIdxKey := getGCIdxKey(idx) 755 batch.Delete(gcIdxKey) 756 dataKey := getDataKey(idx.Idx, po) 757 batch.Delete(dataKey) 758 batch.Delete(idxKey) 759 s.entryCnt-- 760 dbEntryCount.Dec(1) 761 cntKey := make([]byte, 2) 762 cntKey[0] = keyDistanceCnt 763 cntKey[1] = po 764 batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) 765 batch.Put(cntKey, U64ToBytes(s.bucketCnt[po])) 766 } 767 768 func (s *LDBStore) BinIndex(po uint8) uint64 { 769 s.lock.RLock() 770 defer s.lock.RUnlock() 771 return s.bucketCnt[po] 772 } 773 774 // Put adds a chunk to the database, adding indices and incrementing global counters. 775 // If it already exists, it merely increments the access count of the existing entry. 776 // Is thread safe 777 func (s *LDBStore) Put(ctx context.Context, chunk Chunk) error { 778 metrics.GetOrRegisterCounter("ldbstore.put", nil).Inc(1) 779 log.Trace("ldbstore.put", "key", chunk.Address()) 780 781 ikey := getIndexKey(chunk.Address()) 782 var index dpaDBIndex 783 784 po := s.po(chunk.Address()) 785 786 s.lock.Lock() 787 788 if s.closed { 789 s.lock.Unlock() 790 return ErrDBClosed 791 } 792 batch := s.batch 793 794 log.Trace("ldbstore.put: s.db.Get", "key", chunk.Address(), "ikey", fmt.Sprintf("%x", ikey)) 795 _, err := s.db.Get(ikey) 796 if err != nil { 797 s.doPut(chunk, &index, po) 798 } 799 idata := encodeIndex(&index) 800 s.batch.Put(ikey, idata) 801 802 // add the access-chunkindex index for garbage collection 803 gcIdxKey := getGCIdxKey(&index) 804 gcIdxData := getGCIdxValue(&index, po, chunk.Address()) 805 s.batch.Put(gcIdxKey, gcIdxData) 806 s.lock.Unlock() 807 808 select { 809 case s.batchesC <- struct{}{}: 810 default: 811 } 812 813 select { 814 case <-batch.c: 815 return batch.err 816 case <-ctx.Done(): 817 return ctx.Err() 818 } 819 } 820 821 // force putting into db, does not check or update necessary indices 822 func (s *LDBStore) doPut(chunk Chunk, index *dpaDBIndex, po uint8) { 823 data := s.encodeDataFunc(chunk) 824 dkey := getDataKey(s.dataIdx, po) 825 s.batch.Put(dkey, data) 826 index.Idx = s.dataIdx 827 s.bucketCnt[po] = s.dataIdx 828 s.entryCnt++ 829 dbEntryCount.Inc(1) 830 s.dataIdx++ 831 index.Access = s.accessCnt 832 s.accessCnt++ 833 cntKey := make([]byte, 2) 834 cntKey[0] = keyDistanceCnt 835 cntKey[1] = po 836 s.batch.Put(cntKey, U64ToBytes(s.bucketCnt[po])) 837 } 838 839 func (s *LDBStore) writeBatches() { 840 for { 841 select { 842 case <-s.quit: 843 log.Debug("DbStore: quit batch write loop") 844 return 845 case <-s.batchesC: 846 err := s.writeCurrentBatch() 847 if err != nil { 848 log.Debug("DbStore: quit batch write loop", "err", err.Error()) 849 return 850 } 851 } 852 } 853 854 } 855 856 func (s *LDBStore) writeCurrentBatch() error { 857 s.lock.Lock() 858 defer s.lock.Unlock() 859 b := s.batch 860 l := b.Len() 861 if l == 0 { 862 return nil 863 } 864 s.batch = newBatch() 865 b.err = s.writeBatch(b, wEntryCnt|wAccessCnt|wIndexCnt) 866 close(b.c) 867 if s.entryCnt >= s.capacity { 868 go s.collectGarbage() 869 } 870 return nil 871 } 872 873 // must be called non concurrently 874 func (s *LDBStore) writeBatch(b *dbBatch, wFlag uint8) error { 875 if wFlag&wEntryCnt > 0 { 876 b.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) 877 } 878 if wFlag&wIndexCnt > 0 { 879 b.Put(keyDataIdx, U64ToBytes(s.dataIdx)) 880 } 881 if wFlag&wAccessCnt > 0 { 882 b.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) 883 } 884 l := b.Len() 885 if err := s.db.Write(b.Batch); err != nil { 886 return fmt.Errorf("unable to write batch: %v", err) 887 } 888 log.Trace(fmt.Sprintf("batch write (%d entries)", l)) 889 return nil 890 } 891 892 // newMockEncodeDataFunc returns a function that stores the chunk data 893 // to a mock store to bypass the default functionality encodeData. 894 // The constructed function always returns the nil data, as DbStore does 895 // not need to store the data, but still need to create the index. 896 func newMockEncodeDataFunc(mockStore *mock.NodeStore) func(chunk Chunk) []byte { 897 return func(chunk Chunk) []byte { 898 if err := mockStore.Put(chunk.Address(), encodeData(chunk)); err != nil { 899 log.Error(fmt.Sprintf("%T: Chunk %v put: %v", mockStore, chunk.Address().Log(), err)) 900 } 901 return chunk.Address()[:] 902 } 903 } 904 905 // tryAccessIdx tries to find index entry. If found then increments the access 906 // count for garbage collection and returns the index entry and true for found, 907 // otherwise returns nil and false. 908 func (s *LDBStore) tryAccessIdx(addr Address, po uint8) (*dpaDBIndex, bool) { 909 ikey := getIndexKey(addr) 910 idata, err := s.db.Get(ikey) 911 if err != nil { 912 return nil, false 913 } 914 915 index := new(dpaDBIndex) 916 decodeIndex(idata, index) 917 oldGCIdxKey := getGCIdxKey(index) 918 s.batch.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) 919 index.Access = s.accessCnt 920 idata = encodeIndex(index) 921 s.accessCnt++ 922 s.batch.Put(ikey, idata) 923 newGCIdxKey := getGCIdxKey(index) 924 newGCIdxData := getGCIdxValue(index, po, ikey[1:]) 925 s.batch.Delete(oldGCIdxKey) 926 s.batch.Put(newGCIdxKey, newGCIdxData) 927 select { 928 case s.batchesC <- struct{}{}: 929 default: 930 } 931 return index, true 932 } 933 934 // GetSchema is returning the current named schema of the datastore as read from LevelDB 935 func (s *LDBStore) GetSchema() (string, error) { 936 s.lock.Lock() 937 defer s.lock.Unlock() 938 939 data, err := s.db.Get(keySchema) 940 if err != nil { 941 if err == leveldb.ErrNotFound { 942 return DbSchemaNone, nil 943 } 944 return "", err 945 } 946 947 return string(data), nil 948 } 949 950 // PutSchema is saving a named schema to the LevelDB datastore 951 func (s *LDBStore) PutSchema(schema string) error { 952 s.lock.Lock() 953 defer s.lock.Unlock() 954 955 return s.db.Put(keySchema, []byte(schema)) 956 } 957 958 // Get retrieves the chunk matching the provided key from the database. 959 // If the chunk entry does not exist, it returns an error 960 // Updates access count and is thread safe 961 func (s *LDBStore) Get(_ context.Context, addr Address) (chunk Chunk, err error) { 962 metrics.GetOrRegisterCounter("ldbstore.get", nil).Inc(1) 963 log.Trace("ldbstore.get", "key", addr) 964 965 s.lock.Lock() 966 defer s.lock.Unlock() 967 return s.get(addr) 968 } 969 970 // Has queries the underlying DB if a chunk with the given address is stored 971 // Returns true if the chunk is found, false if not 972 func (s *LDBStore) Has(_ context.Context, addr Address) bool { 973 s.lock.RLock() 974 defer s.lock.RUnlock() 975 976 ikey := getIndexKey(addr) 977 _, err := s.db.Get(ikey) 978 979 return err == nil 980 } 981 982 // TODO: To conform with other private methods of this object indices should not be updated 983 func (s *LDBStore) get(addr Address) (chunk *chunk, err error) { 984 if s.closed { 985 return nil, ErrDBClosed 986 } 987 proximity := s.po(addr) 988 index, found := s.tryAccessIdx(addr, proximity) 989 if found { 990 var data []byte 991 if s.getDataFunc != nil { 992 // if getDataFunc is defined, use it to retrieve the chunk data 993 log.Trace("ldbstore.get retrieve with getDataFunc", "key", addr) 994 data, err = s.getDataFunc(addr) 995 if err != nil { 996 return 997 } 998 } else { 999 // default DbStore functionality to retrieve chunk data 1000 datakey := getDataKey(index.Idx, proximity) 1001 data, err = s.db.Get(datakey) 1002 log.Trace("ldbstore.get retrieve", "key", addr, "indexkey", index.Idx, "datakey", fmt.Sprintf("%x", datakey), "proximity", proximity) 1003 if err != nil { 1004 log.Trace("ldbstore.get chunk found but could not be accessed", "key", addr, "err", err) 1005 s.deleteNow(index, getIndexKey(addr), s.po(addr)) 1006 return 1007 } 1008 } 1009 1010 return decodeData(addr, data) 1011 } else { 1012 err = ErrChunkNotFound 1013 } 1014 1015 return 1016 } 1017 1018 // newMockGetFunc returns a function that reads chunk data from 1019 // the mock database, which is used as the value for DbStore.getFunc 1020 // to bypass the default functionality of DbStore with a mock store. 1021 func newMockGetDataFunc(mockStore *mock.NodeStore) func(addr Address) (data []byte, err error) { 1022 return func(addr Address) (data []byte, err error) { 1023 data, err = mockStore.Get(addr) 1024 if err == mock.ErrNotFound { 1025 // preserve ErrChunkNotFound error 1026 err = ErrChunkNotFound 1027 } 1028 return data, err 1029 } 1030 } 1031 1032 func (s *LDBStore) setCapacity(c uint64) { 1033 s.lock.Lock() 1034 defer s.lock.Unlock() 1035 1036 s.capacity = c 1037 1038 for s.entryCnt > c { 1039 s.collectGarbage() 1040 } 1041 } 1042 1043 func (s *LDBStore) Close() { 1044 close(s.quit) 1045 s.lock.Lock() 1046 s.closed = true 1047 s.lock.Unlock() 1048 // force writing out current batch 1049 s.writeCurrentBatch() 1050 s.db.Close() 1051 } 1052 1053 // SyncIterator(start, stop, po, f) calls f on each hash of a bin po from start to stop 1054 func (s *LDBStore) SyncIterator(since uint64, until uint64, po uint8, f func(Address, uint64) bool) error { 1055 metrics.GetOrRegisterCounter("ldbstore.synciterator", nil).Inc(1) 1056 1057 sincekey := getDataKey(since, po) 1058 untilkey := getDataKey(until, po) 1059 it := s.db.NewIterator() 1060 defer it.Release() 1061 1062 for ok := it.Seek(sincekey); ok; ok = it.Next() { 1063 metrics.GetOrRegisterCounter("ldbstore.synciterator.seek", nil).Inc(1) 1064 1065 dbkey := it.Key() 1066 if dbkey[0] != keyData || dbkey[1] != po || bytes.Compare(untilkey, dbkey) < 0 { 1067 break 1068 } 1069 key := make([]byte, 32) 1070 val := it.Value() 1071 copy(key, val[:32]) 1072 if !f(Address(key), binary.BigEndian.Uint64(dbkey[2:])) { 1073 break 1074 } 1075 } 1076 return it.Error() 1077 }