/src/rocksdb/db/snapshot_impl.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | | // This source code is licensed under both the GPLv2 (found in the |
3 | | // COPYING file in the root directory) and Apache 2.0 License |
4 | | // (found in the LICENSE.Apache file in the root directory). |
5 | | // |
6 | | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
7 | | // Use of this source code is governed by a BSD-style license that can be |
8 | | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
9 | | |
10 | | #pragma once |
11 | | #include <vector> |
12 | | |
13 | | #include "db/dbformat.h" |
14 | | #include "rocksdb/db.h" |
15 | | #include "util/autovector.h" |
16 | | |
17 | | namespace ROCKSDB_NAMESPACE { |
18 | | |
19 | | class SnapshotList; |
20 | | |
21 | | // Snapshots are kept in a doubly-linked list in the DB. |
22 | | // Each SnapshotImpl corresponds to a particular sequence number. |
23 | | class SnapshotImpl : public Snapshot { |
24 | | public: |
25 | | SequenceNumber number_; // const after creation |
26 | | // It indicates the smallest uncommitted data at the time the snapshot was |
27 | | // taken. This is currently used by WritePrepared transactions to limit the |
28 | | // scope of queries to IsInSnapshot. |
29 | | SequenceNumber min_uncommitted_ = kMinUnCommittedSeq; |
30 | | |
31 | 6.00k | SequenceNumber GetSequenceNumber() const override { return number_; } |
32 | | |
33 | 0 | int64_t GetUnixTime() const override { return unix_time_; } |
34 | | |
35 | 0 | uint64_t GetTimestamp() const override { return timestamp_; } |
36 | | |
37 | | private: |
38 | | friend class SnapshotList; |
39 | | |
40 | | // SnapshotImpl is kept in a doubly-linked circular list |
41 | | SnapshotImpl* prev_; |
42 | | SnapshotImpl* next_; |
43 | | |
44 | | SnapshotList* list_; // just for sanity checks |
45 | | |
46 | | int64_t unix_time_; |
47 | | |
48 | | uint64_t timestamp_; |
49 | | |
50 | | // Will this snapshot be used by a Transaction to do write-conflict checking? |
51 | | bool is_write_conflict_boundary_; |
52 | | }; |
53 | | |
54 | | class SnapshotList { |
55 | | public: |
56 | 58.3k | SnapshotList() { |
57 | 58.3k | list_.prev_ = &list_; |
58 | 58.3k | list_.next_ = &list_; |
59 | 58.3k | list_.number_ = 0xFFFFFFFFL; // placeholder marker, for debugging |
60 | | // Set all the variables to make UBSAN happy. |
61 | 58.3k | list_.list_ = nullptr; |
62 | 58.3k | list_.unix_time_ = 0; |
63 | 58.3k | list_.timestamp_ = 0; |
64 | 58.3k | list_.is_write_conflict_boundary_ = false; |
65 | 58.3k | count_ = 0; |
66 | 58.3k | } |
67 | | |
68 | | // No copy-construct. |
69 | | SnapshotList(const SnapshotList&) = delete; |
70 | | |
71 | 31.8k | bool empty() const { |
72 | 31.8k | assert(list_.next_ != &list_ || 0 == count_); |
73 | 31.8k | return list_.next_ == &list_; |
74 | 31.8k | } |
75 | 0 | SnapshotImpl* oldest() const { |
76 | 0 | assert(!empty()); |
77 | 0 | return list_.next_; |
78 | 0 | } |
79 | 0 | SnapshotImpl* newest() const { |
80 | 0 | assert(!empty()); |
81 | 0 | return list_.prev_; |
82 | 0 | } |
83 | | |
84 | | SnapshotImpl* New(SnapshotImpl* s, SequenceNumber seq, uint64_t unix_time, |
85 | | bool is_write_conflict_boundary, |
86 | 4.22k | uint64_t ts = std::numeric_limits<uint64_t>::max()) { |
87 | 4.22k | s->number_ = seq; |
88 | 4.22k | s->unix_time_ = unix_time; |
89 | 4.22k | s->timestamp_ = ts; |
90 | 4.22k | s->is_write_conflict_boundary_ = is_write_conflict_boundary; |
91 | 4.22k | s->list_ = this; |
92 | 4.22k | s->next_ = &list_; |
93 | 4.22k | s->prev_ = list_.prev_; |
94 | 4.22k | s->prev_->next_ = s; |
95 | 4.22k | s->next_->prev_ = s; |
96 | 4.22k | count_++; |
97 | 4.22k | return s; |
98 | 4.22k | } |
99 | | |
100 | | // Do not responsible to free the object. |
101 | 4.22k | void Delete(const SnapshotImpl* s) { |
102 | 4.22k | assert(s->list_ == this); |
103 | 4.22k | s->prev_->next_ = s->next_; |
104 | 4.22k | s->next_->prev_ = s->prev_; |
105 | 4.22k | count_--; |
106 | 4.22k | } |
107 | | |
108 | | // retrieve all snapshot numbers up until max_seq. They are sorted in |
109 | | // ascending order (with no duplicates). |
110 | | std::vector<SequenceNumber> GetAll( |
111 | | SequenceNumber* oldest_write_conflict_snapshot = nullptr, |
112 | 27.5k | const SequenceNumber& max_seq = kMaxSequenceNumber) const { |
113 | 27.5k | std::vector<SequenceNumber> ret; |
114 | 27.5k | GetAll(&ret, oldest_write_conflict_snapshot, max_seq); |
115 | 27.5k | return ret; |
116 | 27.5k | } |
117 | | |
118 | | void GetAll(std::vector<SequenceNumber>* snap_vector, |
119 | | SequenceNumber* oldest_write_conflict_snapshot = nullptr, |
120 | 27.5k | const SequenceNumber& max_seq = kMaxSequenceNumber) const { |
121 | 27.5k | std::vector<SequenceNumber>& ret = *snap_vector; |
122 | | // So far we have no use case that would pass a non-empty vector |
123 | 27.5k | assert(ret.size() == 0); |
124 | | |
125 | 27.5k | if (oldest_write_conflict_snapshot != nullptr) { |
126 | 27.5k | *oldest_write_conflict_snapshot = kMaxSequenceNumber; |
127 | 27.5k | } |
128 | | |
129 | 27.5k | if (empty()) { |
130 | 27.5k | return; |
131 | 27.5k | } |
132 | 12 | const SnapshotImpl* s = &list_; |
133 | 24 | while (s->next_ != &list_) { |
134 | 12 | if (s->next_->number_ > max_seq) { |
135 | 0 | break; |
136 | 0 | } |
137 | | // Avoid duplicates |
138 | 12 | if (ret.empty() || ret.back() != s->next_->number_) { |
139 | 12 | ret.push_back(s->next_->number_); |
140 | 12 | } |
141 | | |
142 | 12 | if (oldest_write_conflict_snapshot != nullptr && |
143 | 12 | *oldest_write_conflict_snapshot == kMaxSequenceNumber && |
144 | 12 | s->next_->is_write_conflict_boundary_) { |
145 | | // If this is the first write-conflict boundary snapshot in the list, |
146 | | // it is the oldest |
147 | 0 | *oldest_write_conflict_snapshot = s->next_->number_; |
148 | 0 | } |
149 | | |
150 | 12 | s = s->next_; |
151 | 12 | } |
152 | 12 | return; |
153 | 27.5k | } |
154 | | |
155 | | // get the sequence number of the most recent snapshot |
156 | 0 | SequenceNumber GetNewest() { |
157 | 0 | if (empty()) { |
158 | 0 | return 0; |
159 | 0 | } |
160 | 0 | return newest()->number_; |
161 | 0 | } |
162 | | |
163 | 0 | int64_t GetOldestSnapshotTime() const { |
164 | 0 | if (empty()) { |
165 | 0 | return 0; |
166 | 0 | } else { |
167 | 0 | return oldest()->unix_time_; |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | 0 | int64_t GetOldestSnapshotSequence() const { |
172 | 0 | if (empty()) { |
173 | 0 | return 0; |
174 | 0 | } else { |
175 | 0 | return oldest()->GetSequenceNumber(); |
176 | 0 | } |
177 | 0 | } |
178 | | |
179 | 58.3k | uint64_t count() const { return count_; } |
180 | | |
181 | | private: |
182 | | // Dummy head of doubly-linked list of snapshots |
183 | | SnapshotImpl list_; |
184 | | uint64_t count_; |
185 | | }; |
186 | | |
187 | | // All operations on TimestampedSnapshotList must be protected by db mutex. |
188 | | class TimestampedSnapshotList { |
189 | | public: |
190 | 58.3k | explicit TimestampedSnapshotList() = default; |
191 | | |
192 | 0 | std::shared_ptr<const SnapshotImpl> GetSnapshot(uint64_t ts) const { |
193 | 0 | if (ts == std::numeric_limits<uint64_t>::max() && !snapshots_.empty()) { |
194 | 0 | auto it = snapshots_.rbegin(); |
195 | 0 | assert(it != snapshots_.rend()); |
196 | 0 | return it->second; |
197 | 0 | } |
198 | 0 | auto it = snapshots_.find(ts); |
199 | 0 | if (it == snapshots_.end()) { |
200 | 0 | return std::shared_ptr<const SnapshotImpl>(); |
201 | 0 | } |
202 | 0 | return it->second; |
203 | 0 | } |
204 | | |
205 | | void GetSnapshots( |
206 | | uint64_t ts_lb, uint64_t ts_ub, |
207 | 0 | std::vector<std::shared_ptr<const Snapshot>>& snapshots) const { |
208 | 0 | assert(ts_lb < ts_ub); |
209 | 0 | auto it_low = snapshots_.lower_bound(ts_lb); |
210 | 0 | auto it_high = snapshots_.lower_bound(ts_ub); |
211 | 0 | for (auto it = it_low; it != it_high; ++it) { |
212 | 0 | snapshots.emplace_back(it->second); |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | 0 | void AddSnapshot(const std::shared_ptr<const SnapshotImpl>& snapshot) { |
217 | 0 | assert(snapshot); |
218 | 0 | snapshots_.try_emplace(snapshot->GetTimestamp(), snapshot); |
219 | 0 | } |
220 | | |
221 | | // snapshots_to_release: the container to where the timestamped snapshots will |
222 | | // be moved so that it retains the last reference to the snapshots and the |
223 | | // snapshots won't be actually released which requires db mutex. The |
224 | | // snapshots will be released by caller of ReleaseSnapshotsOlderThan(). |
225 | | void ReleaseSnapshotsOlderThan( |
226 | | uint64_t ts, |
227 | 58.3k | autovector<std::shared_ptr<const SnapshotImpl>>& snapshots_to_release) { |
228 | 58.3k | auto ub = snapshots_.lower_bound(ts); |
229 | 58.3k | for (auto it = snapshots_.begin(); it != ub; ++it) { |
230 | 0 | snapshots_to_release.emplace_back(it->second); |
231 | 0 | } |
232 | 58.3k | snapshots_.erase(snapshots_.begin(), ub); |
233 | 58.3k | } |
234 | | |
235 | | private: |
236 | | std::map<uint64_t, std::shared_ptr<const SnapshotImpl>> snapshots_; |
237 | | }; |
238 | | |
239 | | } // namespace ROCKSDB_NAMESPACE |