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