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