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