github.com/hyperion-hyn/go-ethereum@v2.4.0+incompatible/swarm/storage/ldbstore.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY 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 go-ethereum 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/ethereum/go-ethereum/metrics" 38 "github.com/ethereum/go-ethereum/rlp" 39 "github.com/ethereum/go-ethereum/swarm/log" 40 "github.com/ethereum/go-ethereum/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 (s *LDBStore) updateIndexAccess(index *dpaDBIndex) { 252 index.Access = s.accessCnt 253 } 254 255 func getIndexKey(hash Address) []byte { 256 hashSize := len(hash) 257 key := make([]byte, hashSize+1) 258 key[0] = keyIndex 259 copy(key[1:], hash[:]) 260 return key 261 } 262 263 func getDataKey(idx uint64, po uint8) []byte { 264 key := make([]byte, 10) 265 key[0] = keyData 266 key[1] = po 267 binary.BigEndian.PutUint64(key[2:], idx) 268 269 return key 270 } 271 272 func getGCIdxKey(index *dpaDBIndex) []byte { 273 key := make([]byte, 9) 274 key[0] = keyGCIdx 275 binary.BigEndian.PutUint64(key[1:], index.Access) 276 return key 277 } 278 279 func getGCIdxValue(index *dpaDBIndex, po uint8, addr Address) []byte { 280 val := make([]byte, 41) // po = 1, index.Index = 8, Address = 32 281 val[0] = po 282 binary.BigEndian.PutUint64(val[1:], index.Idx) 283 copy(val[9:], addr) 284 return val 285 } 286 287 func parseGCIdxKey(key []byte) (byte, []byte) { 288 return key[0], key[1:] 289 } 290 291 func parseGCIdxEntry(accessCnt []byte, val []byte) (index *dpaDBIndex, po uint8, addr Address) { 292 index = &dpaDBIndex{ 293 Idx: binary.BigEndian.Uint64(val[1:]), 294 Access: binary.BigEndian.Uint64(accessCnt), 295 } 296 po = val[0] 297 addr = val[9:] 298 return 299 } 300 301 func encodeIndex(index *dpaDBIndex) []byte { 302 data, _ := rlp.EncodeToBytes(index) 303 return data 304 } 305 306 func encodeData(chunk Chunk) []byte { 307 // Always create a new underlying array for the returned byte slice. 308 // The chunk.Address array may be used in the returned slice which 309 // may be changed later in the code or by the LevelDB, resulting 310 // that the Address is changed as well. 311 return append(append([]byte{}, chunk.Address()[:]...), chunk.Data()...) 312 } 313 314 func decodeIndex(data []byte, index *dpaDBIndex) error { 315 dec := rlp.NewStream(bytes.NewReader(data), 0) 316 return dec.Decode(index) 317 } 318 319 func decodeData(addr Address, data []byte) (*chunk, error) { 320 return NewChunk(addr, data[32:]), nil 321 } 322 323 func (s *LDBStore) collectGarbage() error { 324 325 // prevent duplicate gc from starting when one is already running 326 select { 327 case <-s.gc.runC: 328 default: 329 return nil 330 } 331 332 s.lock.Lock() 333 entryCnt := s.entryCnt 334 s.lock.Unlock() 335 336 metrics.GetOrRegisterCounter("ldbstore.collectgarbage", nil).Inc(1) 337 338 // calculate the amount of chunks to collect and reset counter 339 s.startGC(int(entryCnt)) 340 log.Debug("collectGarbage", "target", s.gc.target, "entryCnt", entryCnt) 341 342 var totalDeleted int 343 for s.gc.count < s.gc.target { 344 it := s.db.NewIterator() 345 ok := it.Seek([]byte{keyGCIdx}) 346 var singleIterationCount int 347 348 // every batch needs a lock so we avoid entries changing accessidx in the meantime 349 s.lock.Lock() 350 for ; ok && (singleIterationCount < s.gc.maxBatch); ok = it.Next() { 351 352 // quit if no more access index keys 353 itkey := it.Key() 354 if (itkey == nil) || (itkey[0] != keyGCIdx) { 355 break 356 } 357 358 // get chunk data entry from access index 359 val := it.Value() 360 index, po, hash := parseGCIdxEntry(itkey[1:], val) 361 keyIdx := make([]byte, 33) 362 keyIdx[0] = keyIndex 363 copy(keyIdx[1:], hash) 364 365 // add delete operation to batch 366 s.delete(s.gc.batch.Batch, index, keyIdx, po) 367 singleIterationCount++ 368 s.gc.count++ 369 log.Trace("garbage collect enqueued chunk for deletion", "key", hash) 370 371 // break if target is not on max garbage batch boundary 372 if s.gc.count >= s.gc.target { 373 break 374 } 375 } 376 377 s.writeBatch(s.gc.batch, wEntryCnt) 378 s.lock.Unlock() 379 it.Release() 380 log.Trace("garbage collect batch done", "batch", singleIterationCount, "total", s.gc.count) 381 } 382 383 s.gc.runC <- struct{}{} 384 log.Debug("garbage collect done", "c", s.gc.count) 385 386 metrics.GetOrRegisterCounter("ldbstore.collectgarbage.delete", nil).Inc(int64(totalDeleted)) 387 return nil 388 } 389 390 // Export writes all chunks from the store to a tar archive, returning the 391 // number of chunks written. 392 func (s *LDBStore) Export(out io.Writer) (int64, error) { 393 tw := tar.NewWriter(out) 394 defer tw.Close() 395 396 it := s.db.NewIterator() 397 defer it.Release() 398 var count int64 399 for ok := it.Seek([]byte{keyIndex}); ok; ok = it.Next() { 400 key := it.Key() 401 if (key == nil) || (key[0] != keyIndex) { 402 break 403 } 404 405 var index dpaDBIndex 406 407 hash := key[1:] 408 decodeIndex(it.Value(), &index) 409 po := s.po(hash) 410 datakey := getDataKey(index.Idx, po) 411 log.Trace("store.export", "dkey", fmt.Sprintf("%x", datakey), "dataidx", index.Idx, "po", po) 412 data, err := s.db.Get(datakey) 413 if err != nil { 414 log.Warn(fmt.Sprintf("Chunk %x found but could not be accessed: %v", key, err)) 415 continue 416 } 417 418 hdr := &tar.Header{ 419 Name: hex.EncodeToString(hash), 420 Mode: 0644, 421 Size: int64(len(data)), 422 } 423 if err := tw.WriteHeader(hdr); err != nil { 424 return count, err 425 } 426 if _, err := tw.Write(data); err != nil { 427 return count, err 428 } 429 count++ 430 } 431 432 return count, nil 433 } 434 435 // of chunks read. 436 func (s *LDBStore) Import(in io.Reader) (int64, error) { 437 tr := tar.NewReader(in) 438 439 ctx, cancel := context.WithCancel(context.Background()) 440 defer cancel() 441 442 countC := make(chan int64) 443 errC := make(chan error) 444 var count int64 445 go func() { 446 for { 447 hdr, err := tr.Next() 448 if err == io.EOF { 449 break 450 } else if err != nil { 451 select { 452 case errC <- err: 453 case <-ctx.Done(): 454 } 455 } 456 457 if len(hdr.Name) != 64 { 458 log.Warn("ignoring non-chunk file", "name", hdr.Name) 459 continue 460 } 461 462 keybytes, err := hex.DecodeString(hdr.Name) 463 if err != nil { 464 log.Warn("ignoring invalid chunk file", "name", hdr.Name, "err", err) 465 continue 466 } 467 468 data, err := ioutil.ReadAll(tr) 469 if err != nil { 470 select { 471 case errC <- err: 472 case <-ctx.Done(): 473 } 474 } 475 key := Address(keybytes) 476 chunk := NewChunk(key, data[32:]) 477 478 go func() { 479 select { 480 case errC <- s.Put(ctx, chunk): 481 case <-ctx.Done(): 482 } 483 }() 484 485 count++ 486 } 487 countC <- count 488 }() 489 490 // wait for all chunks to be stored 491 i := int64(0) 492 var total int64 493 for { 494 select { 495 case err := <-errC: 496 if err != nil { 497 return count, err 498 } 499 i++ 500 case total = <-countC: 501 case <-ctx.Done(): 502 return i, ctx.Err() 503 } 504 if total > 0 && i == total { 505 return total, nil 506 } 507 } 508 } 509 510 // Cleanup iterates over the database and deletes chunks if they pass the `f` condition 511 func (s *LDBStore) Cleanup(f func(*chunk) bool) { 512 var errorsFound, removed, total int 513 514 it := s.db.NewIterator() 515 defer it.Release() 516 for ok := it.Seek([]byte{keyIndex}); ok; ok = it.Next() { 517 key := it.Key() 518 if (key == nil) || (key[0] != keyIndex) { 519 break 520 } 521 total++ 522 var index dpaDBIndex 523 err := decodeIndex(it.Value(), &index) 524 if err != nil { 525 log.Warn("Cannot decode") 526 errorsFound++ 527 continue 528 } 529 hash := key[1:] 530 po := s.po(hash) 531 datakey := getDataKey(index.Idx, po) 532 data, err := s.db.Get(datakey) 533 if err != nil { 534 found := false 535 536 // highest possible proximity is 255 537 for po = 1; po <= 255; po++ { 538 datakey = getDataKey(index.Idx, po) 539 data, err = s.db.Get(datakey) 540 if err == nil { 541 found = true 542 break 543 } 544 } 545 546 if !found { 547 log.Warn(fmt.Sprintf("Chunk %x found but count not be accessed with any po", key)) 548 errorsFound++ 549 continue 550 } 551 } 552 553 ck := data[:32] 554 c, err := decodeData(ck, data) 555 if err != nil { 556 log.Error("decodeData error", "err", err) 557 continue 558 } 559 560 cs := int64(binary.LittleEndian.Uint64(c.sdata[:8])) 561 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) 562 563 // if chunk is to be removed 564 if f(c) { 565 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) 566 s.deleteNow(&index, getIndexKey(key[1:]), po) 567 removed++ 568 errorsFound++ 569 } 570 } 571 572 log.Warn(fmt.Sprintf("Found %v errors out of %v entries. Removed %v chunks.", errorsFound, total, removed)) 573 } 574 575 // CleanGCIndex rebuilds the garbage collector index from scratch, while 576 // removing inconsistent elements, e.g., indices with missing data chunks. 577 // WARN: it's a pretty heavy, long running function. 578 func (s *LDBStore) CleanGCIndex() error { 579 s.lock.Lock() 580 defer s.lock.Unlock() 581 582 batch := leveldb.Batch{} 583 584 var okEntryCount uint64 585 var totalEntryCount uint64 586 587 // throw out all gc indices, we will rebuild from cleaned index 588 it := s.db.NewIterator() 589 it.Seek([]byte{keyGCIdx}) 590 var gcDeletes int 591 for it.Valid() { 592 rowType, _ := parseGCIdxKey(it.Key()) 593 if rowType != keyGCIdx { 594 break 595 } 596 batch.Delete(it.Key()) 597 gcDeletes++ 598 it.Next() 599 } 600 log.Debug("gc", "deletes", gcDeletes) 601 if err := s.db.Write(&batch); err != nil { 602 return err 603 } 604 605 it.Seek([]byte{keyIndex}) 606 var idx dpaDBIndex 607 var poPtrs [256]uint64 608 for it.Valid() { 609 rowType, chunkHash := parseGCIdxKey(it.Key()) 610 if rowType != keyIndex { 611 break 612 } 613 err := decodeIndex(it.Value(), &idx) 614 if err != nil { 615 return fmt.Errorf("corrupt index: %v", err) 616 } 617 po := s.po(chunkHash) 618 619 // if we don't find the data key, remove the entry 620 dataKey := getDataKey(idx.Idx, po) 621 _, err = s.db.Get(dataKey) 622 if err != nil { 623 log.Warn("deleting inconsistent index (missing data)", "key", chunkHash) 624 batch.Delete(it.Key()) 625 } else { 626 gcIdxKey := getGCIdxKey(&idx) 627 gcIdxData := getGCIdxValue(&idx, po, chunkHash) 628 batch.Put(gcIdxKey, gcIdxData) 629 log.Trace("clean ok", "key", chunkHash, "gcKey", gcIdxKey, "gcData", gcIdxData) 630 okEntryCount++ 631 if idx.Idx > poPtrs[po] { 632 poPtrs[po] = idx.Idx 633 } 634 } 635 totalEntryCount++ 636 it.Next() 637 } 638 639 it.Release() 640 log.Debug("gc cleanup entries", "ok", okEntryCount, "total", totalEntryCount, "batchlen", batch.Len()) 641 642 var entryCount [8]byte 643 binary.BigEndian.PutUint64(entryCount[:], okEntryCount) 644 batch.Put(keyEntryCnt, entryCount[:]) 645 var poKey [2]byte 646 poKey[0] = keyDistanceCnt 647 for i, poPtr := range poPtrs { 648 poKey[1] = uint8(i) 649 if poPtr == 0 { 650 batch.Delete(poKey[:]) 651 } else { 652 var idxCount [8]byte 653 binary.BigEndian.PutUint64(idxCount[:], poPtr) 654 batch.Put(poKey[:], idxCount[:]) 655 } 656 } 657 658 return s.db.Write(&batch) 659 } 660 661 // Delete is removes a chunk and updates indices. 662 // Is thread safe 663 func (s *LDBStore) Delete(addr Address) error { 664 s.lock.Lock() 665 defer s.lock.Unlock() 666 667 ikey := getIndexKey(addr) 668 669 idata, err := s.db.Get(ikey) 670 if err != nil { 671 return err 672 } 673 674 var idx dpaDBIndex 675 decodeIndex(idata, &idx) 676 proximity := s.po(addr) 677 return s.deleteNow(&idx, ikey, proximity) 678 } 679 680 // executes one delete operation immediately 681 // see *LDBStore.delete 682 func (s *LDBStore) deleteNow(idx *dpaDBIndex, idxKey []byte, po uint8) error { 683 batch := new(leveldb.Batch) 684 s.delete(batch, idx, idxKey, po) 685 return s.db.Write(batch) 686 } 687 688 // adds a delete chunk operation to the provided batch 689 // if called directly, decrements entrycount regardless if the chunk exists upon deletion. Risk of wrap to max uint64 690 func (s *LDBStore) delete(batch *leveldb.Batch, idx *dpaDBIndex, idxKey []byte, po uint8) { 691 metrics.GetOrRegisterCounter("ldbstore.delete", nil).Inc(1) 692 693 gcIdxKey := getGCIdxKey(idx) 694 batch.Delete(gcIdxKey) 695 dataKey := getDataKey(idx.Idx, po) 696 batch.Delete(dataKey) 697 batch.Delete(idxKey) 698 s.entryCnt-- 699 dbEntryCount.Dec(1) 700 cntKey := make([]byte, 2) 701 cntKey[0] = keyDistanceCnt 702 cntKey[1] = po 703 batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) 704 batch.Put(cntKey, U64ToBytes(s.bucketCnt[po])) 705 } 706 707 func (s *LDBStore) BinIndex(po uint8) uint64 { 708 s.lock.RLock() 709 defer s.lock.RUnlock() 710 return s.bucketCnt[po] 711 } 712 713 func (s *LDBStore) Size() uint64 { 714 s.lock.RLock() 715 defer s.lock.RUnlock() 716 return s.entryCnt 717 } 718 719 func (s *LDBStore) CurrentStorageIndex() uint64 { 720 s.lock.RLock() 721 defer s.lock.RUnlock() 722 return s.dataIdx 723 } 724 725 // Put adds a chunk to the database, adding indices and incrementing global counters. 726 // If it already exists, it merely increments the access count of the existing entry. 727 // Is thread safe 728 func (s *LDBStore) Put(ctx context.Context, chunk Chunk) error { 729 metrics.GetOrRegisterCounter("ldbstore.put", nil).Inc(1) 730 log.Trace("ldbstore.put", "key", chunk.Address()) 731 732 ikey := getIndexKey(chunk.Address()) 733 var index dpaDBIndex 734 735 po := s.po(chunk.Address()) 736 737 s.lock.Lock() 738 739 if s.closed { 740 s.lock.Unlock() 741 return ErrDBClosed 742 } 743 batch := s.batch 744 745 log.Trace("ldbstore.put: s.db.Get", "key", chunk.Address(), "ikey", fmt.Sprintf("%x", ikey)) 746 idata, err := s.db.Get(ikey) 747 if err != nil { 748 s.doPut(chunk, &index, po) 749 } 750 idata = encodeIndex(&index) 751 s.batch.Put(ikey, idata) 752 753 // add the access-chunkindex index for garbage collection 754 gcIdxKey := getGCIdxKey(&index) 755 gcIdxData := getGCIdxValue(&index, po, chunk.Address()) 756 s.batch.Put(gcIdxKey, gcIdxData) 757 s.lock.Unlock() 758 759 select { 760 case s.batchesC <- struct{}{}: 761 default: 762 } 763 764 select { 765 case <-batch.c: 766 return batch.err 767 case <-ctx.Done(): 768 return ctx.Err() 769 } 770 } 771 772 // force putting into db, does not check or update necessary indices 773 func (s *LDBStore) doPut(chunk Chunk, index *dpaDBIndex, po uint8) { 774 data := s.encodeDataFunc(chunk) 775 dkey := getDataKey(s.dataIdx, po) 776 s.batch.Put(dkey, data) 777 index.Idx = s.dataIdx 778 s.bucketCnt[po] = s.dataIdx 779 s.entryCnt++ 780 dbEntryCount.Inc(1) 781 s.dataIdx++ 782 index.Access = s.accessCnt 783 s.accessCnt++ 784 cntKey := make([]byte, 2) 785 cntKey[0] = keyDistanceCnt 786 cntKey[1] = po 787 s.batch.Put(cntKey, U64ToBytes(s.bucketCnt[po])) 788 } 789 790 func (s *LDBStore) writeBatches() { 791 for { 792 select { 793 case <-s.quit: 794 log.Debug("DbStore: quit batch write loop") 795 return 796 case <-s.batchesC: 797 err := s.writeCurrentBatch() 798 if err != nil { 799 log.Debug("DbStore: quit batch write loop", "err", err.Error()) 800 return 801 } 802 } 803 } 804 805 } 806 807 func (s *LDBStore) writeCurrentBatch() error { 808 s.lock.Lock() 809 defer s.lock.Unlock() 810 b := s.batch 811 l := b.Len() 812 if l == 0 { 813 return nil 814 } 815 s.batch = newBatch() 816 b.err = s.writeBatch(b, wEntryCnt|wAccessCnt|wIndexCnt) 817 close(b.c) 818 if s.entryCnt >= s.capacity { 819 go s.collectGarbage() 820 } 821 return nil 822 } 823 824 // must be called non concurrently 825 func (s *LDBStore) writeBatch(b *dbBatch, wFlag uint8) error { 826 if wFlag&wEntryCnt > 0 { 827 b.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) 828 } 829 if wFlag&wIndexCnt > 0 { 830 b.Put(keyDataIdx, U64ToBytes(s.dataIdx)) 831 } 832 if wFlag&wAccessCnt > 0 { 833 b.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) 834 } 835 l := b.Len() 836 if err := s.db.Write(b.Batch); err != nil { 837 return fmt.Errorf("unable to write batch: %v", err) 838 } 839 log.Trace(fmt.Sprintf("batch write (%d entries)", l)) 840 return nil 841 } 842 843 // newMockEncodeDataFunc returns a function that stores the chunk data 844 // to a mock store to bypass the default functionality encodeData. 845 // The constructed function always returns the nil data, as DbStore does 846 // not need to store the data, but still need to create the index. 847 func newMockEncodeDataFunc(mockStore *mock.NodeStore) func(chunk Chunk) []byte { 848 return func(chunk Chunk) []byte { 849 if err := mockStore.Put(chunk.Address(), encodeData(chunk)); err != nil { 850 log.Error(fmt.Sprintf("%T: Chunk %v put: %v", mockStore, chunk.Address().Log(), err)) 851 } 852 return chunk.Address()[:] 853 } 854 } 855 856 // tryAccessIdx tries to find index entry. If found then increments the access 857 // count for garbage collection and returns the index entry and true for found, 858 // otherwise returns nil and false. 859 func (s *LDBStore) tryAccessIdx(addr Address, po uint8) (*dpaDBIndex, bool) { 860 ikey := getIndexKey(addr) 861 idata, err := s.db.Get(ikey) 862 if err != nil { 863 return nil, false 864 } 865 866 index := new(dpaDBIndex) 867 decodeIndex(idata, index) 868 oldGCIdxKey := getGCIdxKey(index) 869 s.batch.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) 870 index.Access = s.accessCnt 871 idata = encodeIndex(index) 872 s.accessCnt++ 873 s.batch.Put(ikey, idata) 874 newGCIdxKey := getGCIdxKey(index) 875 newGCIdxData := getGCIdxValue(index, po, ikey[1:]) 876 s.batch.Delete(oldGCIdxKey) 877 s.batch.Put(newGCIdxKey, newGCIdxData) 878 select { 879 case s.batchesC <- struct{}{}: 880 default: 881 } 882 return index, true 883 } 884 885 // GetSchema is returning the current named schema of the datastore as read from LevelDB 886 func (s *LDBStore) GetSchema() (string, error) { 887 s.lock.Lock() 888 defer s.lock.Unlock() 889 890 data, err := s.db.Get(keySchema) 891 if err != nil { 892 if err == leveldb.ErrNotFound { 893 return DbSchemaNone, nil 894 } 895 return "", err 896 } 897 898 return string(data), nil 899 } 900 901 // PutSchema is saving a named schema to the LevelDB datastore 902 func (s *LDBStore) PutSchema(schema string) error { 903 s.lock.Lock() 904 defer s.lock.Unlock() 905 906 return s.db.Put(keySchema, []byte(schema)) 907 } 908 909 // Get retrieves the chunk matching the provided key from the database. 910 // If the chunk entry does not exist, it returns an error 911 // Updates access count and is thread safe 912 func (s *LDBStore) Get(_ context.Context, addr Address) (chunk Chunk, err error) { 913 metrics.GetOrRegisterCounter("ldbstore.get", nil).Inc(1) 914 log.Trace("ldbstore.get", "key", addr) 915 916 s.lock.Lock() 917 defer s.lock.Unlock() 918 return s.get(addr) 919 } 920 921 // TODO: To conform with other private methods of this object indices should not be updated 922 func (s *LDBStore) get(addr Address) (chunk *chunk, err error) { 923 if s.closed { 924 return nil, ErrDBClosed 925 } 926 proximity := s.po(addr) 927 index, found := s.tryAccessIdx(addr, proximity) 928 if found { 929 var data []byte 930 if s.getDataFunc != nil { 931 // if getDataFunc is defined, use it to retrieve the chunk data 932 log.Trace("ldbstore.get retrieve with getDataFunc", "key", addr) 933 data, err = s.getDataFunc(addr) 934 if err != nil { 935 return 936 } 937 } else { 938 // default DbStore functionality to retrieve chunk data 939 datakey := getDataKey(index.Idx, proximity) 940 data, err = s.db.Get(datakey) 941 log.Trace("ldbstore.get retrieve", "key", addr, "indexkey", index.Idx, "datakey", fmt.Sprintf("%x", datakey), "proximity", proximity) 942 if err != nil { 943 log.Trace("ldbstore.get chunk found but could not be accessed", "key", addr, "err", err) 944 s.deleteNow(index, getIndexKey(addr), s.po(addr)) 945 return 946 } 947 } 948 949 return decodeData(addr, data) 950 } else { 951 err = ErrChunkNotFound 952 } 953 954 return 955 } 956 957 // newMockGetFunc returns a function that reads chunk data from 958 // the mock database, which is used as the value for DbStore.getFunc 959 // to bypass the default functionality of DbStore with a mock store. 960 func newMockGetDataFunc(mockStore *mock.NodeStore) func(addr Address) (data []byte, err error) { 961 return func(addr Address) (data []byte, err error) { 962 data, err = mockStore.Get(addr) 963 if err == mock.ErrNotFound { 964 // preserve ErrChunkNotFound error 965 err = ErrChunkNotFound 966 } 967 return data, err 968 } 969 } 970 971 func (s *LDBStore) setCapacity(c uint64) { 972 s.lock.Lock() 973 defer s.lock.Unlock() 974 975 s.capacity = c 976 977 for s.entryCnt > c { 978 s.collectGarbage() 979 } 980 } 981 982 func (s *LDBStore) Close() { 983 close(s.quit) 984 s.lock.Lock() 985 s.closed = true 986 s.lock.Unlock() 987 // force writing out current batch 988 s.writeCurrentBatch() 989 close(s.batchesC) 990 s.db.Close() 991 } 992 993 // SyncIterator(start, stop, po, f) calls f on each hash of a bin po from start to stop 994 func (s *LDBStore) SyncIterator(since uint64, until uint64, po uint8, f func(Address, uint64) bool) error { 995 metrics.GetOrRegisterCounter("ldbstore.synciterator", nil).Inc(1) 996 997 sincekey := getDataKey(since, po) 998 untilkey := getDataKey(until, po) 999 it := s.db.NewIterator() 1000 defer it.Release() 1001 1002 for ok := it.Seek(sincekey); ok; ok = it.Next() { 1003 metrics.GetOrRegisterCounter("ldbstore.synciterator.seek", nil).Inc(1) 1004 1005 dbkey := it.Key() 1006 if dbkey[0] != keyData || dbkey[1] != po || bytes.Compare(untilkey, dbkey) < 0 { 1007 break 1008 } 1009 key := make([]byte, 32) 1010 val := it.Value() 1011 copy(key, val[:32]) 1012 if !f(Address(key), binary.BigEndian.Uint64(dbkey[2:])) { 1013 break 1014 } 1015 } 1016 return it.Error() 1017 }