Everett
Loading...
Searching...
No Matches
mapped_cola.h
Go to the documentation of this file.
1
13#pragma once
14
15#include <everett/cola_query.h>
17
18#include <array>
19#include <memory>
20#include <optional>
21#include <string>
22#include <unordered_map>
23#include <unordered_set>
24#include <set>
25#include <utility>
26#include <vector>
27
28namespace everett {
29 template <class Blob> void scan_mapped_cola(Blob const & source);
30 // Each main node pins its own KV02/IX03 pair, the exact next main pair, and
31 // the next secondary's native file. Secondary search ends there. Identities
32 // are caller-authenticated declarations; binding checks them and shapes.
33 template <class P> struct mapped_cola_blob {
34 using policy_type = P;
36 using native_pointer = std::shared_ptr<native_type const>;
38 using index_pointer = std::shared_ptr<index_type const>;
39 using pair_type = std::shared_ptr<mapped_cola_blob const>;
44
46 pair_type main = {}, native_pointer secondary = {}, std::optional<object_id> secondary_id = {}) {
47 if (!native || !index || identity.native != index->native_id())
48 error_detail::raise<std::invalid_argument>("invalid COLA mapped pair components");
49 if (bool(index->main_id()) != bool(main) || (main && *index->main_id() != main->identity()))
50 error_detail::raise<std::invalid_argument>("COLA main target identity mismatch");
51 if (bool(secondary) != bool(secondary_id) || secondary_id != index->secondary_id())
52 error_detail::raise<std::invalid_argument>("COLA secondary target identity mismatch");
53 auto secondary_count = secondary ? secondary->size() : 0;
54 if (index->borrowed(0).size() != (main ? main->group_count() : 0) ||
55 index->borrowed(1).size() != secondary_count / P::group_size + (secondary_count % P::group_size != 0))
56 error_detail::raise<std::invalid_argument>("COLA mapped target sample count mismatch");
57 auto view = index->view(native->view());
58 return pair_type(new mapped_cola_blob(std::move(identity), std::move(native), std::move(index),
59 std::move(main), std::move(secondary), std::move(view)));
60 }
61 blob_identity const & identity() const & noexcept { return identity_; }
62 blob_identity const & identity() const && = delete;
63 native_pointer native_object() const noexcept { return native_; }
64 index_pointer index_object() const noexcept { return index_; }
65 pair_type main_target() const noexcept { return main_; }
66 native_pointer secondary_target() const noexcept { return secondary_; }
67 cola_index_view<P> view() const & { return view_; }
68 cola_index_view<P> view() const && = delete;
69 std::uint64_t virtual_size() const noexcept { return view_.virtual_size(); }
70 std::uint64_t group_count() const noexcept { return view_.group_count(); }
71
72 // Explicit recovery/validation walk. Rebuilds compact rank/EF metadata and
73 // reconstructs sequential key frontiers, never an array of every full key.
74 void scan() const { scan_mapped_cola(*this); }
75 private:
86
87 };
88
89 // Explicit shared recovery context. Each mapped native and index is scanned
90 // once even when several checkpoint roots share their downstream suffix.
91 template <class Blob> struct mapped_cola_scan {
92 using P = typename Blob::policy_type;
93 using native_type = typename Blob::native_type;
94 using index_type = typename Blob::index_type;
95 void operator()(Blob const & head) {
96 if (failed_) throw std::logic_error("failed COLA recovery context");
97 try { scan(head); } catch (...) { failed_ = true; throw; }
98 }
99 private:
100 bool failed_ = false;
101 // Keep control-block identities even after the caller releases a mapping.
102 // A later allocation at the same address must still be scanned.
103 std::set<std::weak_ptr<index_type const>, std::owner_less<>> indexes_;
104 std::set<std::weak_ptr<native_type const>, std::owner_less<>> natives_;
105 void scan(Blob const & head) {
106 std::unordered_set<Blob const *> path;
107 for (auto current = &head; current; current = current->main_target().get()) {
108 if (!path.insert(current).second) throw std::invalid_argument("cyclic COLA recovery graph");
109 auto index = current->index_object();
110 if (!indexes_.insert(index).second) break;
111 auto native = current->native_object(), secondary = current->secondary_target();
112 if (natives_.insert(native).second) native->scan();
113 if (secondary && natives_.insert(secondary).second) secondary->scan();
114 index->scan();
115 scan_routes(*current);
116 }
117 }
118 static void scan_routes(Blob const & source) {
119 auto view = source.view();
120 auto native = view.native().cursor();
121 std::array borrowed{view.borrowed(0).cursor(), view.borrowed(1).cursor()};
122 using native_cursor = decltype(native);
123 using borrowed_cursors = decltype(borrowed);
124 struct merged {
125 native_cursor & native;
126 borrowed_cursors & borrowed;
127 bool done() const noexcept { return native.done() && borrowed[0].done() && borrowed[1].done(); }
128 unsigned origin() const {
129 unsigned result = 3; bit_view best;
130 if (!native.done()) { result = 0; best = native.peek().key.prefix; }
131 for (unsigned i = 0; i != 2; ++i)
132 if (!borrowed[i].done() && (result == 3 || compare_bits<typename P::architecture>(borrowed[i].peek().key.prefix, best) < 0)) {
133 result = i + 1; best = borrowed[i].peek().key.prefix;
134 }
135 if (result == 3) throw std::out_of_range("COLA recovery cursor at end");
136 return result;
137 }
138 bit_view key(unsigned origin) const { return origin ? borrowed[origin - 1].peek().key.prefix : native.peek().key.prefix; }
139 void advance(unsigned origin) { if (origin) borrowed[origin - 1].advance(); else native.advance(); }
140 } cursor{native, borrowed};
141 bit_string previous_native;
142 std::array<bit_string, 2> previous_borrowed;
143 std::array<std::uint64_t, 2> borrowed_count{}, population{};
144 bool had_native = false;
145 std::uint64_t ordinal = 0;
146 while (!cursor.done()) {
147 auto origin = cursor.origin();
148 auto key = cursor.key(origin);
149 auto group = ordinal / P::group_size;
150 if (ordinal % P::group_size == 0) {
151 for (unsigned route = 0; route != 2; ++route) {
152 auto lcp = borrowed_count[route] ? compare_common_bits<typename P::architecture>(previous_borrowed[route].view(), key).common_bits : 0;
153 if (view.cut_lcps(route)[group] != lcp || view.interleave(route).template rank<typename P::architecture>(group) != borrowed_count[route])
154 error_detail::raise<std::invalid_argument>("COLA cut or rank disagrees with keys");
155 }
156 population = {};
157 }
158 if (origin) {
159 auto route = origin - 1;
160 bool expected = had_native && compare_bits<typename P::architecture>(previous_native.view(), key) == 0;
161 if (view.false_borrow(route, borrowed_count[route]) != expected)
162 error_detail::raise<std::invalid_argument>("COLA false-borrow flag disagrees with keys");
163 previous_borrowed[route] = bit_string::copy(key);
164 ++borrowed_count[route]; ++population[route];
165 } else { previous_native = bit_string::copy(key); had_native = true; }
166 cursor.advance(origin);
167 ++ordinal;
168 if (ordinal % P::group_size == 0 || cursor.done())
169 for (unsigned route = 0; route != 2; ++route)
170 if (view.interleave(route).class_at(group) != population[route])
171 error_detail::raise<std::invalid_argument>("COLA population disagrees with keys");
172 }
173 if (ordinal != source.virtual_size() || borrowed_count[0] != view.borrowed(0).size() ||
174 borrowed_count[1] != view.borrowed(1).size())
175 error_detail::raise<std::invalid_argument>("COLA merged stream count mismatch");
176 auto samples = view.borrowed(0).cursor();
177 if (auto main = source.main_target()) {
179 while (!target.done()) {
180 if (samples.done() || compare_bits<typename P::architecture>(samples.peek().key.prefix, target.peek().key) != 0)
181 error_detail::raise<std::invalid_argument>("COLA main sample differs from target");
182 samples.advance(); target.advance();
183 }
184 }
185 if (!samples.done()) error_detail::raise<std::invalid_argument>("COLA trailing main samples");
186 auto side_samples = view.borrowed(1).cursor();
187 if (auto secondary = source.secondary_target()) {
188 auto target = secondary->view().cursor();
189 std::uint64_t ordinal = 0;
190 while (!target.done()) {
191 if (ordinal % P::group_size == 0) {
192 if (side_samples.done() || compare_bits<typename P::architecture>(side_samples.peek().key.prefix, target.peek().key.prefix) != 0)
193 error_detail::raise<std::invalid_argument>("COLA secondary sample differs from target");
194 side_samples.advance();
195 }
196 target.advance(); ++ordinal;
197 }
198 }
199 if (!side_samples.done()) error_detail::raise<std::invalid_argument>("COLA trailing secondary samples");
200 }
201 };
202 template <class Blob> void scan_mapped_cola(Blob const & source) { mapped_cola_scan<Blob>{}(source); }
203
204 // Build only new borrowed payload/navigation while retaining received native
205 // bytes and exact mapped targets. Seal this artifact's IX03 sections, then
206 // bind the result as a mapped_cola_blob for homogeneous query traversal.
209
211
212 // Interns exact immutable targets across every root of a checkpoint. Opening
213 // reads directories only; payload scans remain explicit recovery operations.
214 template <class P, class Blob = mapped_cola_blob<P>> struct mapped_cola_resolver {
215 using blob = Blob;
216 using native_type = typename blob::native_type;
217 using index_type = typename blob::index_type;
218 using native_pointer = typename blob::native_pointer;
219 using pair_type = typename blob::pair_type;
220 explicit mapped_cola_resolver(std::filesystem::path root) : root_(std::move(root)) {}
222 auto found = natives_.find(id.hex());
223 if (found == natives_.end()) found = natives_.emplace(id.hex(), std::make_shared<native_type const>(
224 native_type::open(root_ / object_path(id, file_kind::native_blob)))).first;
225 return found->second;
226 }
228 struct pending { blob_identity identity; native_pointer native, secondary; typename blob::index_pointer index; };
229 std::vector<pending> chain;
230 std::unordered_set<std::string> seen;
231 std::optional<blob_identity> current = head;
232 pair_type target;
233 while (current) {
234 if (auto known = pairs_.find(current->index.hex()); known != pairs_.end()) {
235 if (known->second->identity() != *current)
236 error_detail::raise<std::invalid_argument>("COLA reused index has another native identity");
237 target = known->second; break;
238 }
239 if (!seen.insert(current->index.hex()).second)
240 error_detail::raise<std::invalid_argument>("cyclic COLA object identities");
241 auto index = std::make_shared<index_type const>(
242 index_type::open(root_ / object_path(current->index, file_kind::fractional_index)));
243 if (index->native_id() != current->native)
244 error_detail::raise<std::invalid_argument>("COLA chain native identity mismatch");
245 chain.push_back({*current, native(current->native), index->secondary_id() ? native(*index->secondary_id()) : native_pointer{}, index});
246 current = index->main_id();
247 }
248 for (auto i = chain.rbegin(); i != chain.rend(); ++i) {
249 target = blob::bind(i->identity, std::move(i->native), i->index,
250 std::move(target), std::move(i->secondary), i->index->secondary_id());
251 pairs_.emplace(i->identity.index.hex(), target);
252 }
253 return target;
254 }
255 private:
256 std::filesystem::path root_;
257 std::unordered_map<std::string, native_pointer> natives_;
258 std::unordered_map<std::string, pair_type> pairs_;
259 };
260
262 std::filesystem::path const & root, blob_identity const & head) {
263 mapped_cola_resolver<P> resolver(root);
264 return mapped_cola_query_root<P>::adopt_prepared(resolver.pair(head));
265 }
266}
Enumerates native matches along a COLA main chain and its terminal secondaries.
Encodes two-target COLA routing in portable IX03 sections.
void raise(char const *message)
Definition error_detail.h:22
Definition active_engine.h:18
void scan_mapped_cola(Blob const &source)
Definition mapped_cola.h:202
mapped_cola_query_root< P > open_mapped_cola_query(std::filesystem::path const &root, blob_identity const &head)
Definition mapped_cola.h:261
std::filesystem::path object_path(object_id const &id, file_kind kind)
Definition object_path.h:52
Definition profile.h:166
bit_view view() const &
Definition profile.h:178
static bit_string copy(bit_view source)
Definition profile.h:181
Definition profile.h:56
Definition sections.h:34
object_id native
Definition sections.h:35
Definition cola_index.h:496
Definition cola_index.h:114
Definition cola_index.h:315
Definition cola_query.h:40
static cola_query_root adopt_prepared(pair_type source)
Definition cola_query.h:47
Definition cola_index.h:420
void advance()
Definition cola_index.h:433
bool done() const noexcept
Definition cola_index.h:423
cola_sample_view< P > peek() const &
Definition cola_index.h:425
Definition mapped_cola.h:33
cola_index_view< P > view_
Definition mapped_cola.h:81
void scan() const
Definition mapped_cola.h:74
cola_index_view< P > view() const &&=delete
blob_identity const & identity() const &&=delete
native_pointer native_object() const noexcept
Definition mapped_cola.h:63
std::shared_ptr< index_type const > index_pointer
Definition mapped_cola.h:38
mapped_cola_blob & operator=(mapped_cola_blob &&)=delete
blob_identity const & identity() const &noexcept
Definition mapped_cola.h:61
static pair_type bind(blob_identity identity, native_pointer native, index_pointer index, pair_type main={}, native_pointer secondary={}, std::optional< object_id > secondary_id={})
Definition mapped_cola.h:45
native_pointer secondary_target() const noexcept
Definition mapped_cola.h:66
blob_identity identity_
Definition mapped_cola.h:76
std::shared_ptr< mapped_cola_blob const > pair_type
Definition mapped_cola.h:39
mapped_cola_blob & operator=(mapped_cola_blob const &)=delete
pair_type main_target() const noexcept
Definition mapped_cola.h:65
index_pointer index_
Definition mapped_cola.h:78
std::shared_ptr< native_type const > native_pointer
Definition mapped_cola.h:36
mapped_cola_blob(mapped_cola_blob const &)=delete
P policy_type
Definition mapped_cola.h:34
index_pointer index_object() const noexcept
Definition mapped_cola.h:64
native_pointer native_
Definition mapped_cola.h:77
std::uint64_t virtual_size() const noexcept
Definition mapped_cola.h:69
pair_type main_
Definition mapped_cola.h:79
mapped_cola_blob(blob_identity identity, native_pointer native, index_pointer index, pair_type main, native_pointer secondary, cola_index_view< P > view)
Definition mapped_cola.h:82
mapped_cola_blob(mapped_cola_blob &&)=delete
native_pointer secondary_
Definition mapped_cola.h:80
std::uint64_t group_count() const noexcept
Definition mapped_cola.h:70
cola_index_view< P > view() const &
Definition mapped_cola.h:67
Definition cola_sections.h:266
Definition mapped_cola.h:214
std::unordered_map< std::string, pair_type > pairs_
Definition mapped_cola.h:258
typename blob::index_type index_type
Definition mapped_cola.h:217
std::unordered_map< std::string, native_pointer > natives_
Definition mapped_cola.h:257
typename blob::native_pointer native_pointer
Definition mapped_cola.h:218
typename blob::native_type native_type
Definition mapped_cola.h:216
native_pointer native(object_id const &id)
Definition mapped_cola.h:221
mapped_cola_resolver(std::filesystem::path root)
Definition mapped_cola.h:220
typename blob::pair_type pair_type
Definition mapped_cola.h:219
std::filesystem::path root_
Definition mapped_cola.h:256
pair_type pair(blob_identity const &head)
Definition mapped_cola.h:227
Blob blob
Definition mapped_cola.h:215
Definition mapped_cola.h:91
void operator()(Blob const &head)
Definition mapped_cola.h:95
static void scan_routes(Blob const &source)
Definition mapped_cola.h:118
typename Blob::index_type index_type
Definition mapped_cola.h:94
typename Blob::native_type native_type
Definition mapped_cola.h:93
void scan(Blob const &head)
Definition mapped_cola.h:105
std::set< std::weak_ptr< index_type const >, std::owner_less<> > indexes_
Definition mapped_cola.h:103
typename Blob::policy_type P
Definition mapped_cola.h:92
bool failed_
Definition mapped_cola.h:100
std::set< std::weak_ptr< native_type const >, std::owner_less<> > natives_
Definition mapped_cola.h:104
Definition sections.h:442
Definition object_path.h:38