github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/snapshot.go (about) 1 // Copyright 2012 The LevelDB-Go and Pebble and Bitalostored Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package bitalostable 6 7 import ( 8 "io" 9 "math" 10 ) 11 12 // Snapshot provides a read-only point-in-time view of the DB state. 13 type Snapshot struct { 14 // The db the snapshot was created from. 15 db *DB 16 seqNum uint64 17 18 // The list the snapshot is linked into. 19 list *snapshotList 20 21 // The next/prev link for the snapshotList doubly-linked list of snapshots. 22 prev, next *Snapshot 23 } 24 25 var _ Reader = (*Snapshot)(nil) 26 27 // Get gets the value for the given key. It returns ErrNotFound if the Snapshot 28 // does not contain the key. 29 // 30 // The caller should not modify the contents of the returned slice, but it is 31 // safe to modify the contents of the argument after Get returns. The returned 32 // slice will remain valid until the returned Closer is closed. On success, the 33 // caller MUST call closer.Close() or a memory leak will occur. 34 func (s *Snapshot) Get(key []byte) ([]byte, io.Closer, error) { 35 if s.db == nil { 36 panic(ErrClosed) 37 } 38 return s.db.getInternal(key, nil /* batch */, s) 39 } 40 41 // NewIter returns an iterator that is unpositioned (Iterator.Valid() will 42 // return false). The iterator can be positioned via a call to SeekGE, 43 // SeekLT, First or Last. 44 func (s *Snapshot) NewIter(o *IterOptions) *Iterator { 45 if s.db == nil { 46 panic(ErrClosed) 47 } 48 return s.db.newIterInternal(nil /* batch */, s, o) 49 } 50 51 // Close closes the snapshot, releasing its resources. Close must be called. 52 // Failure to do so will result in a tiny memory leak and a large leak of 53 // resources on disk due to the entries the snapshot is preventing from being 54 // deleted. 55 func (s *Snapshot) Close() error { 56 if s.db == nil { 57 panic(ErrClosed) 58 } 59 s.db.mu.Lock() 60 s.db.mu.snapshots.remove(s) 61 62 // If s was the previous earliest snapshot, we might be able to reclaim 63 // disk space by dropping obsolete records that were pinned by s. 64 if e := s.db.mu.snapshots.earliest(); e > s.seqNum { 65 s.db.maybeScheduleCompactionPicker(pickElisionOnly) 66 } 67 s.db.mu.Unlock() 68 s.db = nil 69 return nil 70 } 71 72 type snapshotList struct { 73 root Snapshot 74 } 75 76 func (l *snapshotList) init() { 77 l.root.next = &l.root 78 l.root.prev = &l.root 79 } 80 81 func (l *snapshotList) empty() bool { 82 return l.root.next == &l.root 83 } 84 85 func (l *snapshotList) count() int { 86 if l.empty() { 87 return 0 88 } 89 var count int 90 for i := l.root.next; i != &l.root; i = i.next { 91 count++ 92 } 93 return count 94 } 95 96 func (l *snapshotList) earliest() uint64 { 97 v := uint64(math.MaxUint64) 98 if !l.empty() { 99 v = l.root.next.seqNum 100 } 101 return v 102 } 103 104 func (l *snapshotList) toSlice() []uint64 { 105 if l.empty() { 106 return nil 107 } 108 var results []uint64 109 for i := l.root.next; i != &l.root; i = i.next { 110 results = append(results, i.seqNum) 111 } 112 return results 113 } 114 115 func (l *snapshotList) pushBack(s *Snapshot) { 116 if s.list != nil || s.prev != nil || s.next != nil { 117 panic("bitalostable: snapshot list is inconsistent") 118 } 119 s.prev = l.root.prev 120 s.prev.next = s 121 s.next = &l.root 122 s.next.prev = s 123 s.list = l 124 } 125 126 func (l *snapshotList) remove(s *Snapshot) { 127 if s == &l.root { 128 panic("bitalostable: cannot remove snapshot list root node") 129 } 130 if s.list != l { 131 panic("bitalostable: snapshot list is inconsistent") 132 } 133 s.prev.next = s.next 134 s.next.prev = s.prev 135 s.next = nil // avoid memory leaks 136 s.prev = nil // avoid memory leaks 137 s.list = nil // avoid memory leaks 138 }