src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
93 / 119
78.2%
Branches:
86 / 182
47.3%
Functions:
10 / 13
76.9%

storage/value_store/ssd_value_store.h
Line Branch Exec Source
1 #pragma once
2
3 #include <algorithm>
4 #include <memory>
5 #include <sstream>
6 #include <stdexcept>
7 #include <vector>
8
9 #include "base/factory.h"
10 #include "storage/allocator/ssd/ssd_buddy_allocator.h"
11 #include "storage/allocator/ssd/ssd_slab_allocator.h"
12 #include "storage/io_backend/io_backend.h"
13 #include "storage/value_store/value_store.h"
14
15 class SsdValueStore : public ValueStore {
16 public:
17 670 explicit SsdValueStore(const BaseKVConfig& config) {
18
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 const auto& v = config.json_config_.at("value");
19
7/16
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 670 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 670 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 670 times.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 670 times.
✓ Branch 14 taken 670 times.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 670 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
670 if (!v.contains("path") || v.at("path").get<std::string>().empty()) {
20
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ throw std::invalid_argument(
21 ✗ "SsdValueStore requires non-empty value.path");
22 }
23
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 const auto& ssd = v.at("ssd_allocator");
24
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 const auto& io = ssd.at("io");
25
26
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 BaseKVConfig io_cfg = config;
27 670 auto& j = io_cfg.json_config_;
28
3/6
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 670 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 670 times.
✗ Branch 8 not taken.
670 j["io_backend_type"] = io.value("type", "IOURING");
29
4/8
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 670 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 670 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 670 times.
✗ Branch 11 not taken.
670 j["file_path"] = v.at("path").get<std::string>();
30
2/4
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 670 times.
✗ Branch 6 not taken.
670 j["queue_cnt"] = io.value("queue_depth", 512);
31
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 j["page_id_offset"] =
32
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
1340 io.value("base_offset_bytes", static_cast<uint64_t>(0)) / PAGE_SIZE;
33
34 using IOF = base::Factory<IOBackend, const BaseKVConfig&>;
35
3/6
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 670 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 670 times.
✗ Branch 8 not taken.
670 io_backend_.reset(IOF::NewInstance(j.at("io_backend_type"), io_cfg));
36
1/2
✓ Branch 2 taken 670 times.
✗ Branch 3 not taken.
670 io_backend_->init();
37
38
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 const std::string allocator_type = ssd.value("type", "SSD_SLAB");
39
2/4
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 670 times.
✗ Branch 5 not taken.
670 const uint64_t capacity_bytes = ssd.at("capacity_bytes").get<uint64_t>();
40 const uint64_t base_offset =
41
1/2
✓ Branch 1 taken 670 times.
✗ Branch 2 not taken.
670 ssd.value("base_offset_bytes", static_cast<uint64_t>(0));
42
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 668 times.
670 if (allocator_type == "SSD_SLAB") {
43
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 std::vector<int> classes{128, 256, 512, 1024, 4096};
44
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 if (ssd.contains("size_classes")) {
45
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 classes = ssd.at("size_classes").get<std::vector<int>>();
46 }
47 2 allocator_.reset(new SsdSlabAllocator(
48
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
2 io_backend_.get(), classes, capacity_bytes, base_offset));
49
1/2
✓ Branch 2 taken 668 times.
✗ Branch 3 not taken.
670 } else if (allocator_type == "SSD_BUDDY") {
50
1/2
✓ Branch 1 taken 668 times.
✗ Branch 2 not taken.
668 const int min_block = ssd.value("min_block_size", 128);
51
1/2
✓ Branch 1 taken 668 times.
✗ Branch 2 not taken.
668 const int max_block = ssd.value("max_block_size", 4096);
52 668 int levels = 1;
53
2/2
✓ Branch 0 taken 6012 times.
✓ Branch 1 taken 668 times.
6680 for (int size = min_block; size < max_block; size <<= 1) {
54 6012 ++levels;
55 }
56 668 allocator_.reset(new SsdBuddyAllocator(
57
2/4
✓ Branch 2 taken 668 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 668 times.
✗ Branch 6 not taken.
668 io_backend_.get(), min_block, levels, capacity_bytes, base_offset));
58 } else {
59
0/4
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ throw std::invalid_argument("unknown SSD allocator: " + allocator_type);
60 }
61 670 }
62
63 ✗ uint64_t Alloc(size_t size) override {
64 ✗ const uint64_t handle = allocator_->Alloc(size);
65 return handle == SsdBlockAllocator::kInvalidHandle
66
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ ? kValueHandleNone
67 ✗ : handle;
68 }
69
70 ✗ void Write(uint64_t handle, const void* data, size_t size) override {
71
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (handle != kValueHandleNone) {
72 ✗ allocator_->Write(handle, data, size);
73 }
74 ✗ }
75
76 98400 uint64_t AllocAndWrite(const void* data, size_t size) override {
77 98400 const uint64_t handle = allocator_->AllocAndWrite(data, size);
78 return handle == SsdBlockAllocator::kInvalidHandle
79
1/2
✓ Branch 0 taken 98400 times.
✗ Branch 1 not taken.
98400 ? kValueHandleNone
80 98400 : handle;
81 }
82
83 51986 size_t Read(uint64_t handle, void* out_buf, size_t buf_size) override {
84
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51986 times.
51986 if (handle == kValueHandleNone) {
85 ✗ return 0;
86 }
87 51986 return allocator_->Read(handle, out_buf, buf_size);
88 }
89
90 41982 void Free(uint64_t handle) override {
91
1/2
✓ Branch 0 taken 41982 times.
✗ Branch 1 not taken.
41982 if (handle != kValueHandleNone) {
92 41982 allocator_->Free(handle);
93 }
94 41982 }
95
96 73612 size_t SlotCapacity(uint64_t handle) const override {
97 73612 return allocator_->SlotCapacity(handle);
98 }
99
100 ✗ void BatchWrite(const std::vector<uint64_t>& handles,
101 const std::vector<WriteSpec>& specs) override {
102
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if (handles.size() != specs.size()) {
103
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ throw std::invalid_argument("SsdValueStore::BatchWrite size mismatch");
104 }
105
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ for (size_t i = 0; i < handles.size(); ++i) {
106
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ if (handles[i] == kValueHandleNone) {
107 ✗ continue;
108 }
109 ✗ allocator_->Write(handles[i], specs[i].data, specs[i].size);
110 }
111 ✗ }
112
113 448 void BatchRead(const std::vector<uint64_t>& handles,
114 std::vector<ReadResult>& out_results) override {
115 448 constexpr size_t kMaxBatchIoEntries = 1024;
116
1/2
✓ Branch 2 taken 448 times.
✗ Branch 3 not taken.
448 out_results.resize(handles.size());
117 448 std::vector<SsdBlockAllocator::ReadEntry> entries;
118 448 std::vector<size_t> indices;
119
1/2
✓ Branch 3 taken 448 times.
✗ Branch 4 not taken.
448 entries.reserve(std::min(handles.size(), kMaxBatchIoEntries));
120
1/2
✓ Branch 3 taken 448 times.
✗ Branch 4 not taken.
448 indices.reserve(std::min(handles.size(), kMaxBatchIoEntries));
121 448 auto flush = [&]() {
122 448 std::vector<size_t> sizes;
123
1/2
✓ Branch 2 taken 448 times.
✗ Branch 3 not taken.
448 allocator_->BatchRead(entries, sizes);
124
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 448 times.
448 if (sizes.size() != indices.size()) {
125
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ throw std::runtime_error(
126 ✗ "SsdValueStore::BatchRead result size mismatch");
127 }
128
2/2
✓ Branch 1 taken 21626 times.
✓ Branch 2 taken 448 times.
22074 for (size_t i = 0; i < indices.size(); ++i) {
129
1/2
✓ Branch 4 taken 21626 times.
✗ Branch 5 not taken.
21626 out_results[indices[i]].data.resize(sizes[i]);
130 }
131 448 entries.clear();
132 448 indices.clear();
133 448 };
134
2/2
✓ Branch 1 taken 21626 times.
✓ Branch 2 taken 448 times.
22074 for (size_t i = 0; i < handles.size(); ++i) {
135
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 21626 times.
21626 if (handles[i] == kValueHandleNone) {
136 ✗ out_results[i].data.clear();
137 ✗ continue;
138 }
139
2/4
✓ Branch 3 taken 21626 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 21626 times.
✗ Branch 7 not taken.
21626 out_results[i].data.resize(SlotCapacity(handles[i]));
140
1/2
✓ Branch 4 taken 21626 times.
✗ Branch 5 not taken.
21626 entries.push_back({handles[i], out_results[i].data.data()});
141
1/2
✓ Branch 1 taken 21626 times.
✗ Branch 2 not taken.
21626 indices.push_back(i);
142
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 21626 times.
21626 if (entries.size() == kMaxBatchIoEntries) {
143
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ flush();
144 }
145 }
146
1/2
✓ Branch 1 taken 448 times.
✗ Branch 2 not taken.
448 if (!entries.empty()) {
147
1/2
✓ Branch 1 taken 448 times.
✗ Branch 2 not taken.
448 flush();
148 }
149 448 }
150
151 std::vector<uint64_t>
152 108 BatchAllocAndWrite(const std::vector<WriteSpec>& specs) override {
153 108 constexpr size_t kMaxBatchIoEntries = 1024;
154 108 std::vector<uint64_t> handles;
155
1/2
✓ Branch 2 taken 108 times.
✗ Branch 3 not taken.
108 handles.reserve(specs.size());
156
2/2
✓ Branch 1 taken 108 times.
✓ Branch 2 taken 108 times.
216 for (size_t begin = 0; begin < specs.size(); begin += kMaxBatchIoEntries) {
157 108 const size_t end = std::min(begin + kMaxBatchIoEntries, specs.size());
158 108 std::vector<SsdBlockAllocator::WriteEntry> entries;
159
1/2
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
108 entries.reserve(end - begin);
160
2/2
✓ Branch 0 taken 378 times.
✓ Branch 1 taken 108 times.
486 for (size_t i = begin; i < end; ++i) {
161
1/2
✓ Branch 3 taken 378 times.
✗ Branch 4 not taken.
378 entries.push_back({specs[i].data, specs[i].size});
162 }
163 std::vector<uint64_t> chunk_handles =
164
1/2
✓ Branch 2 taken 108 times.
✗ Branch 3 not taken.
108 allocator_->BatchAllocAndWrite(entries);
165
2/2
✓ Branch 5 taken 378 times.
✓ Branch 6 taken 108 times.
486 for (uint64_t handle : chunk_handles) {
166
2/4
✓ Branch 0 taken 378 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 378 times.
✗ Branch 4 not taken.
378 handles.push_back(handle == SsdBlockAllocator::kInvalidHandle
167 ? kValueHandleNone
168 : handle);
169 }
170 108 }
171 108 return handles;
172 ✗ }
173
174 2 void ResetStats() override { io_backend_->ResetReadStats(); }
175
176 4 std::string ExtraResultFields() const override {
177 4 const auto stats = io_backend_->GetReadStats();
178
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 std::ostringstream out;
179
3/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
4 out << " layout_strategy=plain" << " layout_page_reads=" << stats.page_reads
180
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 << " layout_bytes_read=" << stats.bytes_read;
181
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
8 return out.str();
182 4 }
183
184 private:
185 std::unique_ptr<IOBackend> io_backend_;
186 std::unique_ptr<SsdBlockAllocator> allocator_;
187 };
188
189 FACTORY_REGISTER(
190 ValueStore, SSD_VALUE_STORE, SsdValueStore, const BaseKVConfig&);
191