GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 73.4% 80 / 0 / 109
Functions: 72.7% 8 / 0 / 11
Branches: 42.3% 71 / 0 / 168

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