src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
182 / 228
79.8%
Branches:
148 / 300
49.3%
Functions:
22 / 26
84.6%

storage/value_store/ssd_static_layout_value_store.h
Line Branch Exec Source
1 #pragma once
2
3 #include <algorithm>
4 #include <atomic>
5 #include <cstdint>
6 #include <cstring>
7 #include <memory>
8 #include <array>
9 #include <shared_mutex>
10 #include <sstream>
11 #include <stdexcept>
12 #include <unordered_map>
13 #include <vector>
14
15 #include "base/factory.h"
16 #include "storage/io_backend/io_backend.h"
17 #include "storage/ssd_layout/ssd_layout.h"
18 #include "storage/value_store/value_store.h"
19
20 class SsdStaticLayoutValueStore : public ValueStore {
21 public:
22 struct Stats {
23 uint64_t logical_rows = 0;
24 uint64_t physical_copies = 0;
25 uint64_t page_reads = 0;
26 uint64_t requested_rows = 0;
27 uint64_t effective_rows = 0;
28 uint64_t bytes_read = 0;
29 };
30
31 32 explicit SsdStaticLayoutValueStore(const BaseKVConfig& config) {
32
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 const auto& value = config.json_config_.at("value");
33
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 const auto& layout = value.at("ssd_layout");
34
2/4
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32 times.
✗ Branch 5 not taken.
32 const std::string strategy_name = layout.at("strategy").get<std::string>();
35
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 const auto strategy = ssd_layout::ParseStrategy(strategy_name);
36
4/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 8 times.
32 if (strategy != ssd_layout::Strategy::kBandana &&
37 strategy != ssd_layout::Strategy::kMaxEmbed) {
38
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 throw std::invalid_argument(
39 4 "SSD_STATIC_LAYOUT_VALUE_STORE requires bandana or maxembed");
40 }
41 const uint32_t row_size = static_cast<uint32_t>(
42
2/4
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
30 value.at("default_value_size_hint").get<uint64_t>());
43 artifact_ =
44
3/6
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
30 ssd_layout::LoadArtifact(layout.at("mapping_path").get<std::string>());
45
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 ssd_layout::CheckArtifact(artifact_, strategy, row_size);
46
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (artifact_.page_size != PAGE_SIZE) {
47
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ throw std::invalid_argument("SSD layout page size must equal PAGE_SIZE");
48 }
49
2/2
✓ Branch 1 taken 170 times.
✓ Branch 2 taken 30 times.
200 for (uint32_t index = 0; index < artifact_.keys.size(); ++index) {
50
1/2
✓ Branch 2 taken 170 times.
✗ Branch 3 not taken.
170 key_to_row_.emplace(artifact_.keys[index], index);
51 }
52
2/2
✓ Branch 4 taken 170 times.
✓ Branch 5 taken 30 times.
200 for (const auto& row : artifact_.copies) {
53 170 copy_count_ += row.size();
54 }
55
56 const json io =
57
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 value.contains("io")
58
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 ? value.at("io")
59
4/10
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 28 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
32 : value.value("ssd_allocator", json::object())
60
9/14
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 28 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 28 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
62 .value("io", json::object());
61
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 BaseKVConfig io_config = config;
62 30 auto& json = io_config.json_config_;
63 const uint64_t base_offset =
64
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 io.value("base_offset_bytes", static_cast<uint64_t>(0));
65
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (base_offset % PAGE_SIZE != 0) {
66
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ throw std::invalid_argument(
67 ✗ "SSD layout base offset must be page aligned");
68 }
69 30 page_id_offset_ = base_offset / PAGE_SIZE;
70
3/6
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
30 json["io_backend_type"] = io.value("type", "IOURING");
71
4/8
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 30 times.
✗ Branch 11 not taken.
30 json["file_path"] = value.at("path").get<std::string>();
72
2/4
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 30 times.
✗ Branch 6 not taken.
30 json["queue_cnt"] = io.value("queue_depth", 512);
73
1/2
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
30 json["page_id_offset"] = page_id_offset_;
74 using IOF = base::Factory<IOBackend, const BaseKVConfig&>;
75
3/6
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
30 io_backend_.reset(IOF::NewInstance(json.at("io_backend_type"), io_config));
76
1/2
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
30 io_backend_->init();
77 40 }
78
79 ssd_layout::Strategy strategy() const { return artifact_.strategy; }
80 uint64_t checksum() const { return artifact_.checksum; }
81 10 Stats stats() const {
82 10 Stats out;
83 10 out.logical_rows = artifact_.keys.size();
84 10 out.physical_copies = copy_count_;
85 10 out.page_reads = page_reads_.load(std::memory_order_relaxed);
86 10 out.requested_rows = requested_rows_.load(std::memory_order_relaxed);
87 10 out.effective_rows = effective_rows_.load(std::memory_order_relaxed);
88 10 out.bytes_read = bytes_read_.load(std::memory_order_relaxed);
89 10 return out;
90 }
91
92 2 uint64_t Alloc(size_t) override { return RejectAlloc(); }
93 2 uint64_t AllocAndWrite(const void*, size_t) override { return RejectAlloc(); }
94 std::vector<uint64_t>
95 ✗ BatchAllocAndWrite(const std::vector<WriteSpec>&) override {
96
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ throw std::invalid_argument(
97 ✗ "SSD_STATIC_LAYOUT_VALUE_STORE placement is fixed");
98 }
99 14 uint64_t AllocAndWrite(uint64_t key, const void* data, size_t size) override {
100 14 const uint64_t handle = HandleForKey(key);
101 14 Write(handle, data, size);
102 14 return handle;
103 }
104 std::vector<uint64_t>
105 ✗ BatchAllocAndWrite(const std::vector<uint64_t>& keys,
106 const std::vector<WriteSpec>& specs) override {
107
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if (keys.size() != specs.size()) {
108
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ throw std::invalid_argument("static SSD layout batch size mismatch");
109 }
110 ✗ std::vector<uint64_t> handles;
111
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ handles.reserve(keys.size());
112
0/2
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ for (uint64_t key : keys) {
113
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ handles.push_back(HandleForKey(key));
114 }
115
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ BatchWrite(handles, specs);
116 ✗ return handles;
117 ✗ }
118 16 ValueStore::LayoutId GetLayoutId() const override {
119 16 return {static_cast<uint32_t>(artifact_.strategy), artifact_.checksum};
120 }
121
122 470 void Write(uint64_t handle, const void* data, size_t size) override {
123
2/2
✓ Branch 1 taken 466 times.
✓ Branch 2 taken 4 times.
470 const auto& copies = CopiesOf(handle);
124
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 464 times.
466 if (size != artifact_.row_size) {
125
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 throw std::invalid_argument("static SSD layout value size mismatch");
126 }
127 464 std::vector<uint64_t> pages;
128
1/2
✓ Branch 2 taken 464 times.
✗ Branch 3 not taken.
464 pages.reserve(copies.size());
129
2/2
✓ Branch 5 taken 492 times.
✓ Branch 6 taken 464 times.
956 for (const auto& copy : copies) {
130
1/2
✓ Branch 1 taken 492 times.
✗ Branch 2 not taken.
492 pages.push_back(copy.page_id);
131 }
132
2/4
✓ Branch 1 taken 464 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 464 times.
✗ Branch 5 not taken.
464 auto locked = LockPages(pages, true);
133
2/2
✓ Branch 5 taken 492 times.
✓ Branch 6 taken 464 times.
956 for (const auto& copy : copies) {
134
1/2
✓ Branch 1 taken 492 times.
✗ Branch 2 not taken.
492 UpdatePage(copy, data, size);
135 }
136 464 }
137
138 4 void BatchWrite(const std::vector<uint64_t>& handles,
139 const std::vector<WriteSpec>& specs) override {
140
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (handles.size() != specs.size()) {
141
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ throw std::invalid_argument("static SSD layout batch size mismatch");
142 }
143
2/2
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 4 times.
28 for (size_t i = 0; i < handles.size(); ++i) {
144 24 Write(handles[i], specs[i].data, specs[i].size);
145 }
146 4 }
147
148 20 size_t Read(uint64_t handle, void* out_buf, size_t buf_size) override {
149
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 const auto& copies = CopiesOf(handle);
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (buf_size < artifact_.row_size) {
151
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ throw std::invalid_argument("static SSD layout read buffer is too small");
152 }
153 20 const auto& copy = copies.front();
154
2/4
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
40 auto locked = LockPages({copy.page_id}, false);
155
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 ReadPage(copy, out_buf);
156 20 requested_rows_.fetch_add(1, std::memory_order_relaxed);
157 20 effective_rows_.fetch_add(1, std::memory_order_relaxed);
158 20 return artifact_.row_size;
159 20 }
160
161 12 void BatchRead(const std::vector<uint64_t>& handles,
162 std::vector<ReadResult>& out_results) override {
163
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 out_results.resize(handles.size());
164
1/2
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
12 std::vector<char> done(handles.size(), 1);
165 std::vector<const std::vector<ssd_layout::Location>*> copies(
166
1/2
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
12 handles.size());
167 12 size_t remaining = 0;
168
2/2
✓ Branch 1 taken 54 times.
✓ Branch 2 taken 12 times.
66 for (size_t i = 0; i < handles.size(); ++i) {
169
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 54 times.
54 if (handles[i] == kValueHandleNone) {
170 ✗ out_results[i].data.clear();
171 ✗ continue;
172 }
173
1/2
✓ Branch 2 taken 54 times.
✗ Branch 3 not taken.
54 copies[i] = &CopiesOf(handles[i]);
174 54 done[i] = 0;
175 54 ++remaining;
176
1/2
✓ Branch 2 taken 54 times.
✗ Branch 3 not taken.
54 out_results[i].data.resize(artifact_.row_size);
177 }
178 12 requested_rows_.fetch_add(remaining, std::memory_order_relaxed);
179 // ponytail: rescan candidate pages each round. Invert page->request if a
180 // batch profile shows this scan above the SSD read.
181
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 12 times.
26 while (remaining > 0) {
182 14 std::unordered_map<uint64_t, uint32_t> cover;
183
2/2
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 14 times.
80 for (size_t i = 0; i < done.size(); ++i) {
184
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 60 times.
66 if (done[i]) {
185 6 continue;
186 }
187
2/2
✓ Branch 5 taken 90 times.
✓ Branch 6 taken 60 times.
150 for (const auto& copy : *copies[i]) {
188
1/2
✓ Branch 1 taken 90 times.
✗ Branch 2 not taken.
90 ++cover[copy.page_id];
189 }
190 }
191 14 uint64_t best_page = UINT64_MAX;
192 14 uint32_t best_cover = 0;
193
2/2
✓ Branch 5 taken 26 times.
✓ Branch 6 taken 14 times.
40 for (const auto& item : cover) {
194
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 18 times.
26 if (item.second > best_cover ||
195
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 (item.second == best_cover && item.first < best_page)) {
196 22 best_page = item.first;
197 22 best_cover = item.second;
198 }
199 }
200
2/4
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
28 auto locked = LockPages({best_page}, false);
201
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 char* page = ReadRawPage(best_page, true);
202 14 uint32_t hit = 0;
203
2/2
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 14 times.
80 for (size_t i = 0; i < done.size(); ++i) {
204
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 60 times.
66 if (done[i]) {
205 6 continue;
206 }
207
2/2
✓ Branch 6 taken 84 times.
✓ Branch 7 taken 6 times.
90 for (const auto& copy : *copies[i]) {
208
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 54 times.
84 if (copy.page_id != best_page) {
209 30 continue;
210 }
211 54 std::memcpy(out_results[i].data.data(),
212 54 page + copy.byte_offset,
213 54 artifact_.row_size);
214 54 done[i] = 1;
215 54 --remaining;
216 54 ++hit;
217 54 break;
218 }
219 }
220
1/2
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 io_backend_->FreeBuffer(page);
221 14 effective_rows_.fetch_add(hit, std::memory_order_relaxed);
222 14 }
223 12 }
224
225 2 void Free(uint64_t) override {}
226 38 size_t SlotCapacity(uint64_t handle) const override {
227 38 CopiesOf(handle);
228 36 return artifact_.row_size;
229 }
230
231 ✗ void ResetStats() override {
232 ✗ io_backend_->ResetReadStats();
233 ✗ page_reads_.store(0, std::memory_order_relaxed);
234 ✗ requested_rows_.store(0, std::memory_order_relaxed);
235 ✗ effective_rows_.store(0, std::memory_order_relaxed);
236 ✗ bytes_read_.store(0, std::memory_order_relaxed);
237 ✗ }
238
239 ✗ std::string ExtraResultFields() const override {
240 ✗ const Stats snapshot = stats();
241 ✗ const double rows_per_page =
242 ✗ snapshot.page_reads == 0
243
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ ? 0.0
244 ✗ : static_cast<double>(snapshot.effective_rows) /
245 ✗ snapshot.page_reads;
246
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ std::ostringstream out;
247 ✗ out << " layout_strategy=" << ssd_layout::StrategyName(artifact_.strategy)
248
0/10
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ << " layout_logical_rows=" << snapshot.logical_rows
249
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ << " layout_physical_copies=" << snapshot.physical_copies
250
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ << " layout_page_reads=" << snapshot.page_reads
251
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ << " layout_requested_rows=" << snapshot.requested_rows
252
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ << " layout_effective_rows=" << snapshot.effective_rows
253
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ << " layout_rows_per_page=" << rows_per_page
254
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ << " layout_bytes_read=" << snapshot.bytes_read;
255
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ return out.str();
256 ✗ }
257
258 private:
259 struct PageLock {
260 std::vector<std::shared_mutex*> mutexes;
261 bool exclusive = false;
262 498 PageLock() = default;
263 PageLock(const PageLock&) = delete;
264 PageLock& operator=(const PageLock&) = delete;
265 PageLock(PageLock&& other) noexcept : exclusive(other.exclusive) {
266 mutexes.swap(other.mutexes);
267 }
268 498 ~PageLock() {
269
2/2
✓ Branch 4 taken 526 times.
✓ Branch 5 taken 498 times.
1024 for (auto it = mutexes.rbegin(); it != mutexes.rend(); ++it) {
270
2/2
✓ Branch 0 taken 492 times.
✓ Branch 1 taken 34 times.
526 if (exclusive) {
271 492 (*it)->unlock();
272 } else {
273 34 (*it)->unlock_shared();
274 }
275 }
276 498 }
277 };
278
279 4 static uint64_t RejectAlloc() {
280
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 throw std::invalid_argument(
281 8 "SSD_STATIC_LAYOUT_VALUE_STORE placement is fixed");
282 }
283
284 14 uint64_t HandleForKey(uint64_t key) const {
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (key > ssd_layout::kMaxKey) {
286
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ throw std::invalid_argument("static SSD layout key is out of range");
287 }
288 14 return key + 1;
289 }
290
291 582 uint64_t DecodeKey(uint64_t handle) const {
292
3/4
✓ Branch 0 taken 582 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 580 times.
582 if (handle == kValueHandleNone || handle > ssd_layout::kMaxKey + 1) {
293
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 throw std::invalid_argument("static SSD layout handle is invalid");
294 }
295 580 return handle - 1;
296 }
297
298 582 const std::vector<ssd_layout::Location>& CopiesOf(uint64_t handle) const {
299
3/4
✓ Branch 1 taken 580 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 580 times.
✗ Branch 5 not taken.
582 const auto found = key_to_row_.find(DecodeKey(handle));
300
2/2
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 576 times.
580 if (found == key_to_row_.end()) {
301
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 throw std::invalid_argument("static SSD layout key is not mapped");
302 }
303 1152 return artifact_.copies[found->second];
304 }
305
306 498 PageLock LockPages(std::vector<uint64_t> pages, bool exclusive) {
307 498 std::vector<std::shared_mutex*> mutexes;
308
1/2
✓ Branch 2 taken 498 times.
✗ Branch 3 not taken.
498 mutexes.reserve(pages.size());
309
2/2
✓ Branch 4 taken 526 times.
✓ Branch 5 taken 498 times.
1024 for (uint64_t page : pages) {
310
1/2
✓ Branch 3 taken 526 times.
✗ Branch 4 not taken.
526 mutexes.push_back(&page_locks_[page % page_locks_.size()]);
311 }
312
1/2
✓ Branch 3 taken 498 times.
✗ Branch 4 not taken.
498 std::sort(mutexes.begin(), mutexes.end());
313
2/4
✓ Branch 5 taken 498 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 498 times.
✗ Branch 10 not taken.
498 mutexes.erase(std::unique(mutexes.begin(), mutexes.end()), mutexes.end());
314 498 PageLock locked;
315 498 locked.exclusive = exclusive;
316
1/2
✓ Branch 1 taken 498 times.
✗ Branch 2 not taken.
498 locked.mutexes = mutexes;
317
2/2
✓ Branch 5 taken 526 times.
✓ Branch 6 taken 498 times.
1024 for (auto* mutex : locked.mutexes) {
318
2/2
✓ Branch 0 taken 492 times.
✓ Branch 1 taken 34 times.
526 if (exclusive) {
319
1/2
✓ Branch 1 taken 492 times.
✗ Branch 2 not taken.
492 mutex->lock();
320 } else {
321
1/2
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
34 mutex->lock_shared();
322 }
323 }
324 996 return locked;
325 498 }
326
327 526 char* ReadRawPage(uint64_t page_id, bool count_read) {
328 526 char* buffer = io_backend_->AllocateBuffer();
329
2/4
✓ Branch 3 taken 526 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 526 times.
✗ Branch 7 not taken.
526 io_backend_->BatchReadPages({{page_id + page_id_offset_, buffer, 1}});
330
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 492 times.
526 if (count_read) {
331 34 page_reads_.fetch_add(1, std::memory_order_relaxed);
332 34 bytes_read_.fetch_add(PAGE_SIZE, std::memory_order_relaxed);
333 }
334 526 return buffer;
335 }
336
337 20 void ReadPage(const ssd_layout::Location& copy, void* out) {
338 20 char* page = ReadRawPage(copy.page_id, true);
339 20 std::memcpy(out, page + copy.byte_offset, artifact_.row_size);
340 20 io_backend_->FreeBuffer(page);
341 20 }
342
343 void
344 492 UpdatePage(const ssd_layout::Location& copy, const void* data, size_t size) {
345 492 char* page = ReadRawPage(copy.page_id, false);
346 492 std::memcpy(page + copy.byte_offset, data, size);
347
2/4
✓ Branch 3 taken 492 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 492 times.
✗ Branch 7 not taken.
492 io_backend_->BatchWritePages({{copy.page_id + page_id_offset_, page, 1}});
348 492 io_backend_->FreeBuffer(page);
349 492 }
350
351 ssd_layout::Artifact artifact_;
352 std::unordered_map<uint64_t, uint32_t> key_to_row_;
353 std::unique_ptr<IOBackend> io_backend_;
354 uint64_t page_id_offset_ = 0;
355 uint64_t copy_count_ = 0;
356 std::array<std::shared_mutex, 64> page_locks_;
357 std::atomic<uint64_t> page_reads_{0};
358 std::atomic<uint64_t> requested_rows_{0};
359 std::atomic<uint64_t> effective_rows_{0};
360 std::atomic<uint64_t> bytes_read_{0};
361 };
362
363 FACTORY_REGISTER(ValueStore,
364 SSD_STATIC_LAYOUT_VALUE_STORE,
365 SsdStaticLayoutValueStore,
366 const BaseKVConfig&);
367