github.com/gobitfly/go-ethereum@v1.8.12/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 "encoding/binary" 29 "encoding/hex" 30 "fmt" 31 "io" 32 "io/ioutil" 33 "sort" 34 "sync" 35 36 "github.com/ethereum/go-ethereum/metrics" 37 "github.com/ethereum/go-ethereum/rlp" 38 "github.com/ethereum/go-ethereum/swarm/log" 39 "github.com/ethereum/go-ethereum/swarm/storage/mock" 40 "github.com/syndtr/goleveldb/leveldb" 41 "github.com/syndtr/goleveldb/leveldb/opt" 42 ) 43 44 const ( 45 gcArrayFreeRatio = 0.1 46 maxGCitems = 5000 // max number of items to be gc'd per call to collectGarbage() 47 ) 48 49 var ( 50 keyIndex = byte(0) 51 keyOldData = byte(1) 52 keyAccessCnt = []byte{2} 53 keyEntryCnt = []byte{3} 54 keyDataIdx = []byte{4} 55 keyData = byte(6) 56 keyDistanceCnt = byte(7) 57 ) 58 59 type gcItem struct { 60 idx uint64 61 value uint64 62 idxKey []byte 63 po uint8 64 } 65 66 type LDBStoreParams struct { 67 *StoreParams 68 Path string 69 Po func(Address) uint8 70 } 71 72 // NewLDBStoreParams constructs LDBStoreParams with the specified values. 73 func NewLDBStoreParams(storeparams *StoreParams, path string) *LDBStoreParams { 74 return &LDBStoreParams{ 75 StoreParams: storeparams, 76 Path: path, 77 Po: func(k Address) (ret uint8) { return uint8(Proximity(storeparams.BaseKey[:], k[:])) }, 78 } 79 } 80 81 type LDBStore struct { 82 db *LDBDatabase 83 84 // this should be stored in db, accessed transactionally 85 entryCnt uint64 // number of items in the LevelDB 86 accessCnt uint64 // ever-accumulating number increased every time we read/access an entry 87 dataIdx uint64 // similar to entryCnt, but we only increment it 88 capacity uint64 89 bucketCnt []uint64 90 91 hashfunc SwarmHasher 92 po func(Address) uint8 93 94 batchC chan bool 95 batchesC chan struct{} 96 batch *leveldb.Batch 97 lock sync.RWMutex 98 quit chan struct{} 99 100 // Functions encodeDataFunc is used to bypass 101 // the default functionality of DbStore with 102 // mock.NodeStore for testing purposes. 103 encodeDataFunc func(chunk *Chunk) []byte 104 // If getDataFunc is defined, it will be used for 105 // retrieving the chunk data instead from the local 106 // LevelDB database. 107 getDataFunc func(addr Address) (data []byte, err error) 108 } 109 110 // TODO: Instead of passing the distance function, just pass the address from which distances are calculated 111 // to avoid the appearance of a pluggable distance metric and opportunities of bugs associated with providing 112 // a function different from the one that is actually used. 113 func NewLDBStore(params *LDBStoreParams) (s *LDBStore, err error) { 114 s = new(LDBStore) 115 s.hashfunc = params.Hash 116 s.quit = make(chan struct{}) 117 118 s.batchC = make(chan bool) 119 s.batchesC = make(chan struct{}, 1) 120 go s.writeBatches() 121 s.batch = new(leveldb.Batch) 122 // associate encodeData with default functionality 123 s.encodeDataFunc = encodeData 124 125 s.db, err = NewLDBDatabase(params.Path) 126 if err != nil { 127 return nil, err 128 } 129 130 s.po = params.Po 131 s.setCapacity(params.DbCapacity) 132 133 s.bucketCnt = make([]uint64, 0x100) 134 for i := 0; i < 0x100; i++ { 135 k := make([]byte, 2) 136 k[0] = keyDistanceCnt 137 k[1] = uint8(i) 138 cnt, _ := s.db.Get(k) 139 s.bucketCnt[i] = BytesToU64(cnt) 140 s.bucketCnt[i]++ 141 } 142 data, _ := s.db.Get(keyEntryCnt) 143 s.entryCnt = BytesToU64(data) 144 s.entryCnt++ 145 data, _ = s.db.Get(keyAccessCnt) 146 s.accessCnt = BytesToU64(data) 147 s.accessCnt++ 148 data, _ = s.db.Get(keyDataIdx) 149 s.dataIdx = BytesToU64(data) 150 s.dataIdx++ 151 152 return s, nil 153 } 154 155 // NewMockDbStore creates a new instance of DbStore with 156 // mockStore set to a provided value. If mockStore argument is nil, 157 // this function behaves exactly as NewDbStore. 158 func NewMockDbStore(params *LDBStoreParams, mockStore *mock.NodeStore) (s *LDBStore, err error) { 159 s, err = NewLDBStore(params) 160 if err != nil { 161 return nil, err 162 } 163 164 // replace put and get with mock store functionality 165 if mockStore != nil { 166 s.encodeDataFunc = newMockEncodeDataFunc(mockStore) 167 s.getDataFunc = newMockGetDataFunc(mockStore) 168 } 169 return 170 } 171 172 type dpaDBIndex struct { 173 Idx uint64 174 Access uint64 175 } 176 177 func BytesToU64(data []byte) uint64 { 178 if len(data) < 8 { 179 return 0 180 } 181 return binary.BigEndian.Uint64(data) 182 } 183 184 func U64ToBytes(val uint64) []byte { 185 data := make([]byte, 8) 186 binary.BigEndian.PutUint64(data, val) 187 return data 188 } 189 190 func (s *LDBStore) updateIndexAccess(index *dpaDBIndex) { 191 index.Access = s.accessCnt 192 } 193 194 func getIndexKey(hash Address) []byte { 195 hashSize := len(hash) 196 key := make([]byte, hashSize+1) 197 key[0] = keyIndex 198 copy(key[1:], hash[:]) 199 return key 200 } 201 202 func getOldDataKey(idx uint64) []byte { 203 key := make([]byte, 9) 204 key[0] = keyOldData 205 binary.BigEndian.PutUint64(key[1:9], idx) 206 207 return key 208 } 209 210 func getDataKey(idx uint64, po uint8) []byte { 211 key := make([]byte, 10) 212 key[0] = keyData 213 key[1] = po 214 binary.BigEndian.PutUint64(key[2:], idx) 215 216 return key 217 } 218 219 func encodeIndex(index *dpaDBIndex) []byte { 220 data, _ := rlp.EncodeToBytes(index) 221 return data 222 } 223 224 func encodeData(chunk *Chunk) []byte { 225 // Always create a new underlying array for the returned byte slice. 226 // The chunk.Key array may be used in the returned slice which 227 // may be changed later in the code or by the LevelDB, resulting 228 // that the Key is changed as well. 229 return append(append([]byte{}, chunk.Addr[:]...), chunk.SData...) 230 } 231 232 func decodeIndex(data []byte, index *dpaDBIndex) error { 233 dec := rlp.NewStream(bytes.NewReader(data), 0) 234 return dec.Decode(index) 235 } 236 237 func decodeData(data []byte, chunk *Chunk) { 238 chunk.SData = data[32:] 239 chunk.Size = int64(binary.BigEndian.Uint64(data[32:40])) 240 } 241 242 func decodeOldData(data []byte, chunk *Chunk) { 243 chunk.SData = data 244 chunk.Size = int64(binary.BigEndian.Uint64(data[0:8])) 245 } 246 247 func (s *LDBStore) collectGarbage(ratio float32) { 248 metrics.GetOrRegisterCounter("ldbstore.collectgarbage", nil).Inc(1) 249 250 it := s.db.NewIterator() 251 defer it.Release() 252 253 garbage := []*gcItem{} 254 gcnt := 0 255 256 for ok := it.Seek([]byte{keyIndex}); ok && (gcnt < maxGCitems) && (uint64(gcnt) < s.entryCnt); ok = it.Next() { 257 itkey := it.Key() 258 259 if (itkey == nil) || (itkey[0] != keyIndex) { 260 break 261 } 262 263 // it.Key() contents change on next call to it.Next(), so we must copy it 264 key := make([]byte, len(it.Key())) 265 copy(key, it.Key()) 266 267 val := it.Value() 268 269 var index dpaDBIndex 270 271 hash := key[1:] 272 decodeIndex(val, &index) 273 po := s.po(hash) 274 275 gci := &gcItem{ 276 idxKey: key, 277 idx: index.Idx, 278 value: index.Access, // the smaller, the more likely to be gc'd. see sort comparator below. 279 po: po, 280 } 281 282 garbage = append(garbage, gci) 283 gcnt++ 284 } 285 286 sort.Slice(garbage[:gcnt], func(i, j int) bool { return garbage[i].value < garbage[j].value }) 287 288 cutoff := int(float32(gcnt) * ratio) 289 metrics.GetOrRegisterCounter("ldbstore.collectgarbage.delete", nil).Inc(int64(cutoff)) 290 291 for i := 0; i < cutoff; i++ { 292 s.delete(garbage[i].idx, garbage[i].idxKey, garbage[i].po) 293 } 294 } 295 296 // Export writes all chunks from the store to a tar archive, returning the 297 // number of chunks written. 298 func (s *LDBStore) Export(out io.Writer) (int64, error) { 299 tw := tar.NewWriter(out) 300 defer tw.Close() 301 302 it := s.db.NewIterator() 303 defer it.Release() 304 var count int64 305 for ok := it.Seek([]byte{keyIndex}); ok; ok = it.Next() { 306 key := it.Key() 307 if (key == nil) || (key[0] != keyIndex) { 308 break 309 } 310 311 var index dpaDBIndex 312 313 hash := key[1:] 314 decodeIndex(it.Value(), &index) 315 po := s.po(hash) 316 datakey := getDataKey(index.Idx, po) 317 log.Trace("store.export", "dkey", fmt.Sprintf("%x", datakey), "dataidx", index.Idx, "po", po) 318 data, err := s.db.Get(datakey) 319 if err != nil { 320 log.Warn(fmt.Sprintf("Chunk %x found but could not be accessed: %v", key[:], err)) 321 continue 322 } 323 324 hdr := &tar.Header{ 325 Name: hex.EncodeToString(hash), 326 Mode: 0644, 327 Size: int64(len(data)), 328 } 329 if err := tw.WriteHeader(hdr); err != nil { 330 return count, err 331 } 332 if _, err := tw.Write(data); err != nil { 333 return count, err 334 } 335 count++ 336 } 337 338 return count, nil 339 } 340 341 // of chunks read. 342 func (s *LDBStore) Import(in io.Reader) (int64, error) { 343 tr := tar.NewReader(in) 344 345 var count int64 346 var wg sync.WaitGroup 347 for { 348 hdr, err := tr.Next() 349 if err == io.EOF { 350 break 351 } else if err != nil { 352 return count, err 353 } 354 355 if len(hdr.Name) != 64 { 356 log.Warn("ignoring non-chunk file", "name", hdr.Name) 357 continue 358 } 359 360 keybytes, err := hex.DecodeString(hdr.Name) 361 if err != nil { 362 log.Warn("ignoring invalid chunk file", "name", hdr.Name, "err", err) 363 continue 364 } 365 366 data, err := ioutil.ReadAll(tr) 367 if err != nil { 368 return count, err 369 } 370 key := Address(keybytes) 371 chunk := NewChunk(key, nil) 372 chunk.SData = data[32:] 373 s.Put(chunk) 374 wg.Add(1) 375 go func() { 376 defer wg.Done() 377 <-chunk.dbStoredC 378 }() 379 count++ 380 } 381 wg.Wait() 382 return count, nil 383 } 384 385 func (s *LDBStore) Cleanup() { 386 //Iterates over the database and checks that there are no faulty chunks 387 it := s.db.NewIterator() 388 startPosition := []byte{keyIndex} 389 it.Seek(startPosition) 390 var key []byte 391 var errorsFound, total int 392 for it.Valid() { 393 key = it.Key() 394 if (key == nil) || (key[0] != keyIndex) { 395 break 396 } 397 total++ 398 var index dpaDBIndex 399 err := decodeIndex(it.Value(), &index) 400 if err != nil { 401 it.Next() 402 continue 403 } 404 data, err := s.db.Get(getDataKey(index.Idx, s.po(Address(key[1:])))) 405 if err != nil { 406 log.Warn(fmt.Sprintf("Chunk %x found but could not be accessed: %v", key[:], err)) 407 s.delete(index.Idx, getIndexKey(key[1:]), s.po(Address(key[1:]))) 408 errorsFound++ 409 } else { 410 hasher := s.hashfunc() 411 hasher.Write(data[32:]) 412 hash := hasher.Sum(nil) 413 if !bytes.Equal(hash, key[1:]) { 414 log.Warn(fmt.Sprintf("Found invalid chunk. Hash mismatch. hash=%x, key=%x", hash, key[:])) 415 s.delete(index.Idx, getIndexKey(key[1:]), s.po(Address(key[1:]))) 416 } 417 } 418 it.Next() 419 } 420 it.Release() 421 log.Warn(fmt.Sprintf("Found %v errors out of %v entries", errorsFound, total)) 422 } 423 424 func (s *LDBStore) ReIndex() { 425 //Iterates over the database and checks that there are no faulty chunks 426 it := s.db.NewIterator() 427 startPosition := []byte{keyOldData} 428 it.Seek(startPosition) 429 var key []byte 430 var errorsFound, total int 431 for it.Valid() { 432 key = it.Key() 433 if (key == nil) || (key[0] != keyOldData) { 434 break 435 } 436 data := it.Value() 437 hasher := s.hashfunc() 438 hasher.Write(data) 439 hash := hasher.Sum(nil) 440 441 newKey := make([]byte, 10) 442 oldCntKey := make([]byte, 2) 443 newCntKey := make([]byte, 2) 444 oldCntKey[0] = keyDistanceCnt 445 newCntKey[0] = keyDistanceCnt 446 key[0] = keyData 447 key[1] = s.po(Address(key[1:])) 448 oldCntKey[1] = key[1] 449 newCntKey[1] = s.po(Address(newKey[1:])) 450 copy(newKey[2:], key[1:]) 451 newValue := append(hash, data...) 452 453 batch := new(leveldb.Batch) 454 batch.Delete(key) 455 s.bucketCnt[oldCntKey[1]]-- 456 batch.Put(oldCntKey, U64ToBytes(s.bucketCnt[oldCntKey[1]])) 457 batch.Put(newKey, newValue) 458 s.bucketCnt[newCntKey[1]]++ 459 batch.Put(newCntKey, U64ToBytes(s.bucketCnt[newCntKey[1]])) 460 s.db.Write(batch) 461 it.Next() 462 } 463 it.Release() 464 log.Warn(fmt.Sprintf("Found %v errors out of %v entries", errorsFound, total)) 465 } 466 467 func (s *LDBStore) delete(idx uint64, idxKey []byte, po uint8) { 468 metrics.GetOrRegisterCounter("ldbstore.delete", nil).Inc(1) 469 470 batch := new(leveldb.Batch) 471 batch.Delete(idxKey) 472 batch.Delete(getDataKey(idx, po)) 473 s.entryCnt-- 474 s.bucketCnt[po]-- 475 cntKey := make([]byte, 2) 476 cntKey[0] = keyDistanceCnt 477 cntKey[1] = po 478 batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt)) 479 batch.Put(cntKey, U64ToBytes(s.bucketCnt[po])) 480 s.db.Write(batch) 481 } 482 483 func (s *LDBStore) CurrentBucketStorageIndex(po uint8) uint64 { 484 s.lock.RLock() 485 defer s.lock.RUnlock() 486 487 return s.bucketCnt[po] 488 } 489 490 func (s *LDBStore) Size() uint64 { 491 s.lock.Lock() 492 defer s.lock.Unlock() 493 return s.entryCnt 494 } 495 496 func (s *LDBStore) CurrentStorageIndex() uint64 { 497 s.lock.RLock() 498 defer s.lock.RUnlock() 499 return s.dataIdx 500 } 501 502 func (s *LDBStore) Put(chunk *Chunk) { 503 metrics.GetOrRegisterCounter("ldbstore.put", nil).Inc(1) 504 log.Trace("ldbstore.put", "key", chunk.Addr) 505 506 ikey := getIndexKey(chunk.Addr) 507 var index dpaDBIndex 508 509 po := s.po(chunk.Addr) 510 s.lock.Lock() 511 defer s.lock.Unlock() 512 513 log.Trace("ldbstore.put: s.db.Get", "key", chunk.Addr, "ikey", fmt.Sprintf("%x", ikey)) 514 idata, err := s.db.Get(ikey) 515 if err != nil { 516 s.doPut(chunk, &index, po) 517 batchC := s.batchC 518 go func() { 519 <-batchC 520 chunk.markAsStored() 521 }() 522 } else { 523 log.Trace("ldbstore.put: chunk already exists, only update access", "key", chunk.Addr) 524 decodeIndex(idata, &index) 525 chunk.markAsStored() 526 } 527 index.Access = s.accessCnt 528 s.accessCnt++ 529 idata = encodeIndex(&index) 530 s.batch.Put(ikey, idata) 531 select { 532 case s.batchesC <- struct{}{}: 533 default: 534 } 535 } 536 537 // force putting into db, does not check access index 538 func (s *LDBStore) doPut(chunk *Chunk, index *dpaDBIndex, po uint8) { 539 data := s.encodeDataFunc(chunk) 540 dkey := getDataKey(s.dataIdx, po) 541 s.batch.Put(dkey, data) 542 index.Idx = s.dataIdx 543 s.bucketCnt[po] = s.dataIdx 544 s.entryCnt++ 545 s.dataIdx++ 546 547 cntKey := make([]byte, 2) 548 cntKey[0] = keyDistanceCnt 549 cntKey[1] = po 550 s.batch.Put(cntKey, U64ToBytes(s.bucketCnt[po])) 551 } 552 553 func (s *LDBStore) writeBatches() { 554 mainLoop: 555 for { 556 select { 557 case <-s.quit: 558 break mainLoop 559 case <-s.batchesC: 560 s.lock.Lock() 561 b := s.batch 562 e := s.entryCnt 563 d := s.dataIdx 564 a := s.accessCnt 565 c := s.batchC 566 s.batchC = make(chan bool) 567 s.batch = new(leveldb.Batch) 568 err := s.writeBatch(b, e, d, a) 569 // TODO: set this error on the batch, then tell the chunk 570 if err != nil { 571 log.Error(fmt.Sprintf("spawn batch write (%d entries): %v", b.Len(), err)) 572 } 573 close(c) 574 for e > s.capacity { 575 // Collect garbage in a separate goroutine 576 // to be able to interrupt this loop by s.quit. 577 done := make(chan struct{}) 578 go func() { 579 s.collectGarbage(gcArrayFreeRatio) 580 close(done) 581 }() 582 583 e = s.entryCnt 584 select { 585 case <-s.quit: 586 s.lock.Unlock() 587 break mainLoop 588 case <-done: 589 } 590 } 591 s.lock.Unlock() 592 } 593 } 594 log.Trace(fmt.Sprintf("DbStore: quit batch write loop")) 595 } 596 597 // must be called non concurrently 598 func (s *LDBStore) writeBatch(b *leveldb.Batch, entryCnt, dataIdx, accessCnt uint64) error { 599 b.Put(keyEntryCnt, U64ToBytes(entryCnt)) 600 b.Put(keyDataIdx, U64ToBytes(dataIdx)) 601 b.Put(keyAccessCnt, U64ToBytes(accessCnt)) 602 l := b.Len() 603 if err := s.db.Write(b); err != nil { 604 return fmt.Errorf("unable to write batch: %v", err) 605 } 606 log.Trace(fmt.Sprintf("batch write (%d entries)", l)) 607 return nil 608 } 609 610 // newMockEncodeDataFunc returns a function that stores the chunk data 611 // to a mock store to bypass the default functionality encodeData. 612 // The constructed function always returns the nil data, as DbStore does 613 // not need to store the data, but still need to create the index. 614 func newMockEncodeDataFunc(mockStore *mock.NodeStore) func(chunk *Chunk) []byte { 615 return func(chunk *Chunk) []byte { 616 if err := mockStore.Put(chunk.Addr, encodeData(chunk)); err != nil { 617 log.Error(fmt.Sprintf("%T: Chunk %v put: %v", mockStore, chunk.Addr.Log(), err)) 618 } 619 return chunk.Addr[:] 620 } 621 } 622 623 // try to find index; if found, update access cnt and return true 624 func (s *LDBStore) tryAccessIdx(ikey []byte, index *dpaDBIndex) bool { 625 idata, err := s.db.Get(ikey) 626 if err != nil { 627 return false 628 } 629 decodeIndex(idata, index) 630 s.batch.Put(keyAccessCnt, U64ToBytes(s.accessCnt)) 631 s.accessCnt++ 632 index.Access = s.accessCnt 633 idata = encodeIndex(index) 634 s.batch.Put(ikey, idata) 635 select { 636 case s.batchesC <- struct{}{}: 637 default: 638 } 639 return true 640 } 641 642 func (s *LDBStore) Get(addr Address) (chunk *Chunk, err error) { 643 metrics.GetOrRegisterCounter("ldbstore.get", nil).Inc(1) 644 log.Trace("ldbstore.get", "key", addr) 645 646 s.lock.Lock() 647 defer s.lock.Unlock() 648 return s.get(addr) 649 } 650 651 func (s *LDBStore) get(addr Address) (chunk *Chunk, err error) { 652 var indx dpaDBIndex 653 654 if s.tryAccessIdx(getIndexKey(addr), &indx) { 655 var data []byte 656 if s.getDataFunc != nil { 657 // if getDataFunc is defined, use it to retrieve the chunk data 658 log.Trace("ldbstore.get retrieve with getDataFunc", "key", addr) 659 data, err = s.getDataFunc(addr) 660 if err != nil { 661 return 662 } 663 } else { 664 // default DbStore functionality to retrieve chunk data 665 proximity := s.po(addr) 666 datakey := getDataKey(indx.Idx, proximity) 667 data, err = s.db.Get(datakey) 668 log.Trace("ldbstore.get retrieve", "key", addr, "indexkey", indx.Idx, "datakey", fmt.Sprintf("%x", datakey), "proximity", proximity) 669 if err != nil { 670 log.Trace("ldbstore.get chunk found but could not be accessed", "key", addr, "err", err) 671 s.delete(indx.Idx, getIndexKey(addr), s.po(addr)) 672 return 673 } 674 } 675 676 chunk = NewChunk(addr, nil) 677 chunk.markAsStored() 678 decodeData(data, chunk) 679 } else { 680 err = ErrChunkNotFound 681 } 682 683 return 684 } 685 686 // newMockGetFunc returns a function that reads chunk data from 687 // the mock database, which is used as the value for DbStore.getFunc 688 // to bypass the default functionality of DbStore with a mock store. 689 func newMockGetDataFunc(mockStore *mock.NodeStore) func(addr Address) (data []byte, err error) { 690 return func(addr Address) (data []byte, err error) { 691 data, err = mockStore.Get(addr) 692 if err == mock.ErrNotFound { 693 // preserve ErrChunkNotFound error 694 err = ErrChunkNotFound 695 } 696 return data, err 697 } 698 } 699 700 func (s *LDBStore) updateAccessCnt(addr Address) { 701 702 s.lock.Lock() 703 defer s.lock.Unlock() 704 705 var index dpaDBIndex 706 s.tryAccessIdx(getIndexKey(addr), &index) // result_chn == nil, only update access cnt 707 708 } 709 710 func (s *LDBStore) setCapacity(c uint64) { 711 s.lock.Lock() 712 defer s.lock.Unlock() 713 714 s.capacity = c 715 716 if s.entryCnt > c { 717 ratio := float32(1.01) - float32(c)/float32(s.entryCnt) 718 if ratio < gcArrayFreeRatio { 719 ratio = gcArrayFreeRatio 720 } 721 if ratio > 1 { 722 ratio = 1 723 } 724 for s.entryCnt > c { 725 s.collectGarbage(ratio) 726 } 727 } 728 } 729 730 func (s *LDBStore) Close() { 731 close(s.quit) 732 s.db.Close() 733 } 734 735 // SyncIterator(start, stop, po, f) calls f on each hash of a bin po from start to stop 736 func (s *LDBStore) SyncIterator(since uint64, until uint64, po uint8, f func(Address, uint64) bool) error { 737 metrics.GetOrRegisterCounter("ldbstore.synciterator", nil).Inc(1) 738 739 sincekey := getDataKey(since, po) 740 untilkey := getDataKey(until, po) 741 it := s.db.NewIterator() 742 defer it.Release() 743 744 for ok := it.Seek(sincekey); ok; ok = it.Next() { 745 metrics.GetOrRegisterCounter("ldbstore.synciterator.seek", nil).Inc(1) 746 747 dbkey := it.Key() 748 if dbkey[0] != keyData || dbkey[1] != po || bytes.Compare(untilkey, dbkey) < 0 { 749 break 750 } 751 key := make([]byte, 32) 752 val := it.Value() 753 copy(key, val[:32]) 754 if !f(Address(key), binary.BigEndian.Uint64(dbkey[2:])) { 755 break 756 } 757 } 758 return it.Error() 759 } 760 761 func databaseExists(path string) bool { 762 o := &opt.Options{ 763 ErrorIfMissing: true, 764 } 765 tdb, err := leveldb.OpenFile(path, o) 766 if err != nil { 767 return false 768 } 769 defer tdb.Close() 770 return true 771 }