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