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 | 670 | const auto& v = config.json_config_.at("value"); | |
| 19 | 670 | if (!v.contains("path") || v.at("path").get<std::string>().empty()) { | |
| 20 | ✗ | throw std::invalid_argument( | |
| 21 | ✗ | "SsdValueStore requires non-empty value.path"); | |
| 22 | } | ||
| 23 | 670 | const auto& ssd = v.at("ssd_allocator"); | |
| 24 | 670 | const auto& io = ssd.at("io"); | |
| 25 | |||
| 26 | 670 | BaseKVConfig io_cfg = config; | |
| 27 | 670 | auto& j = io_cfg.json_config_; | |
| 28 | 670 | j["io_backend_type"] = io.value("type", "IOURING"); | |
| 29 | 670 | j["file_path"] = v.at("path").get<std::string>(); | |
| 30 | 670 | j["queue_cnt"] = io.value("queue_depth", 512); | |
| 31 | 670 | j["page_id_offset"] = | |
| 32 | 1340 | io.value("base_offset_bytes", static_cast<uint64_t>(0)) / PAGE_SIZE; | |
| 33 | |||
| 34 | using IOF = base::Factory<IOBackend, const BaseKVConfig&>; | ||
| 35 | 670 | io_backend_.reset(IOF::NewInstance(j.at("io_backend_type"), io_cfg)); | |
| 36 | 670 | io_backend_->init(); | |
| 37 | |||
| 38 | 670 | const std::string allocator_type = ssd.value("type", "SSD_SLAB"); | |
| 39 | 670 | const uint64_t capacity_bytes = ssd.at("capacity_bytes").get<uint64_t>(); | |
| 40 | const uint64_t base_offset = | ||
| 41 | 670 | ssd.value("base_offset_bytes", static_cast<uint64_t>(0)); | |
| 42 | 670 | if (allocator_type == "SSD_SLAB") { | |
| 43 | 2 | std::vector<int> classes{128, 256, 512, 1024, 4096}; | |
| 44 | 2 | if (ssd.contains("size_classes")) { | |
| 45 | 2 | classes = ssd.at("size_classes").get<std::vector<int>>(); | |
| 46 | } | ||
| 47 | 2 | allocator_.reset(new SsdSlabAllocator( | |
| 48 | 2 | io_backend_.get(), classes, capacity_bytes, base_offset)); | |
| 49 | 670 | } else if (allocator_type == "SSD_BUDDY") { | |
| 50 | 668 | const int min_block = ssd.value("min_block_size", 128); | |
| 51 | 668 | const int max_block = ssd.value("max_block_size", 4096); | |
| 52 | 668 | int levels = 1; | |
| 53 | 6680 | for (int size = min_block; size < max_block; size <<= 1) { | |
| 54 | 6012 | ++levels; | |
| 55 | } | ||
| 56 | 668 | allocator_.reset(new SsdBuddyAllocator( | |
| 57 | 668 | io_backend_.get(), min_block, levels, capacity_bytes, base_offset)); | |
| 58 | } else { | ||
| 59 | ✗ | 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 | ✗ | ? kValueHandleNone | |
| 67 | ✗ | : handle; | |
| 68 | } | ||
| 69 | |||
| 70 | ✗ | void Write(uint64_t handle, const void* data, size_t size) override { | |
| 71 | ✗ | 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 | 98400 | ? kValueHandleNone | |
| 80 | 98400 | : handle; | |
| 81 | } | ||
| 82 | |||
| 83 | 51986 | size_t Read(uint64_t handle, void* out_buf, size_t buf_size) override { | |
| 84 | 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 | 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 | ✗ | if (handles.size() != specs.size()) { | |
| 103 | ✗ | throw std::invalid_argument("SsdValueStore::BatchWrite size mismatch"); | |
| 104 | } | ||
| 105 | ✗ | for (size_t i = 0; i < handles.size(); ++i) { | |
| 106 | ✗ | 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 | 448 | out_results.resize(handles.size()); | |
| 117 | 448 | std::vector<SsdBlockAllocator::ReadEntry> entries; | |
| 118 | 448 | std::vector<size_t> indices; | |
| 119 | 448 | entries.reserve(std::min(handles.size(), kMaxBatchIoEntries)); | |
| 120 | 448 | indices.reserve(std::min(handles.size(), kMaxBatchIoEntries)); | |
| 121 | 448 | auto flush = [&]() { | |
| 122 | 448 | std::vector<size_t> sizes; | |
| 123 | 448 | allocator_->BatchRead(entries, sizes); | |
| 124 | 448 | if (sizes.size() != indices.size()) { | |
| 125 | ✗ | throw std::runtime_error( | |
| 126 | ✗ | "SsdValueStore::BatchRead result size mismatch"); | |
| 127 | } | ||
| 128 | 22074 | for (size_t i = 0; i < indices.size(); ++i) { | |
| 129 | 21626 | out_results[indices[i]].data.resize(sizes[i]); | |
| 130 | } | ||
| 131 | 448 | entries.clear(); | |
| 132 | 448 | indices.clear(); | |
| 133 | 448 | }; | |
| 134 | 22074 | for (size_t i = 0; i < handles.size(); ++i) { | |
| 135 | 21626 | if (handles[i] == kValueHandleNone) { | |
| 136 | ✗ | out_results[i].data.clear(); | |
| 137 | ✗ | continue; | |
| 138 | } | ||
| 139 | 21626 | out_results[i].data.resize(SlotCapacity(handles[i])); | |
| 140 | 21626 | entries.push_back({handles[i], out_results[i].data.data()}); | |
| 141 | 21626 | indices.push_back(i); | |
| 142 | 21626 | if (entries.size() == kMaxBatchIoEntries) { | |
| 143 | ✗ | flush(); | |
| 144 | } | ||
| 145 | } | ||
| 146 | 448 | if (!entries.empty()) { | |
| 147 | 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 | 108 | handles.reserve(specs.size()); | |
| 156 | 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 | 108 | entries.reserve(end - begin); | |
| 160 | 486 | for (size_t i = begin; i < end; ++i) { | |
| 161 | 378 | entries.push_back({specs[i].data, specs[i].size}); | |
| 162 | } | ||
| 163 | std::vector<uint64_t> chunk_handles = | ||
| 164 | 108 | allocator_->BatchAllocAndWrite(entries); | |
| 165 | 486 | for (uint64_t handle : chunk_handles) { | |
| 166 | 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 | 4 | std::ostringstream out; | |
| 179 | 4 | out << " layout_strategy=plain" << " layout_page_reads=" << stats.page_reads | |
| 180 | 4 | << " layout_bytes_read=" << stats.bytes_read; | |
| 181 | 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 |