Everett
Loading...
Searching...
No Matches
runtime_graph_sealer.h
Go to the documentation of this file.
1
12#pragma once
13
17
19 // The backend owns Catalog/Ids and serializes its own calls. Distinct
20 // backends can resolve the same immutable owner concurrently; its binding
21 // slot serializes production of the one acknowledged identity in this store.
22 template <class P, class Ids, class Ops, class Family> struct graph_sealer {
24 using node_type = typename Family::node_type;
25 using native_type = typename Family::native_type;
26 using pair_type = typename node_type::pair_type;
27 using native_pointer = typename node_type::native_pointer;
28 using mapped_pointer = decltype(std::declval<node_type const &>().mapped());
29 using mapped_type = std::remove_const_t<typename mapped_pointer::element_type>;
30 using mapped_native_type = typename mapped_type::native_type;
33
34 graph_sealer(catalog_type & catalog, Ids & ids, std::string * last_operation = nullptr)
35 : catalog_(catalog), ids_(ids), last_operation_(last_operation) {}
36
37 // Reserve one bounded set of independent, ready publication units. Each
38 // unit retains its ordinary seal acknowledgment. Dependency discovery and
39 // slot preparation finish before any nonblocking producer claim is held.
40 // The remaining graph uses the ordinary serial walk below.
41 std::size_t prepare_ready(std::span<pair_type const> roots,
42 std::span<native_pointer const> natives) {
43 constexpr std::size_t limit = 16;
44 std::vector<pair_type> unbound;
45 std::unordered_set<node_type const *> seen;
46 std::unordered_set<native_type const *> paired;
47 auto visit = [&](auto && self, pair_type const & pair) -> void {
48 if (!pair || !seen.insert(pair.get()).second ||
49 pair->bindings_.find(catalog_.identity(), catalog_.root())) return;
50 if (!pair->built()) return; // Ordinary admission diagnoses this case.
51 paired.insert(pair->native_owner().get());
52 self(self, pair->main_target());
53 unbound.push_back(pair);
54 };
55 for (auto const & pair : roots) visit(visit, pair);
56
57 std::vector<ready_unit> units;
58 units.reserve(limit);
59 std::unordered_set<native_type const *> selected;
60 for (auto const & pair : unbound) {
61 if (units.size() == limit) break;
62 auto main = pair->main_target() ? pair->main_target()->bindings_.find(catalog_.identity(), catalog_.root()) : nullptr;
63 auto secondary = pair->secondary_target() ? pair->secondary_target()->bindings_.find(catalog_.identity(), catalog_.root()) : nullptr;
64 if ((pair->main_target() && !main) || (pair->secondary_target() && !secondary)) continue;
65 auto const & owner = pair->native_owner();
66 if (!owner) continue;
67 auto native = owner->bindings_.find(catalog_.identity(), catalog_.root());
68 if (!native && (!batch_owned(owner) || !selected.insert(owner.get()).second)) continue;
72 units.emplace_back(pair, owner, std::move(main), std::move(secondary), std::move(native));
73 auto & unit = units.back();
74 unit.pair_claim.emplace(pair->bindings_.prepare(catalog_.identity(), catalog_.root()));
75 if (!unit.native) unit.native_claim.emplace(owner->bindings_.prepare(catalog_.identity(), catalog_.root()));
76 }
77 for (auto const & native : natives) {
78 if (units.size() == limit) break;
79 // A native which belongs to an unbound pair must stay available for
80 // native/index fusion, even when that pair's dependencies are unready.
81 if (!native || paired.contains(native.get()) || !batch_owned(native) ||
82 !selected.insert(native.get()).second ||
83 native->bindings_.find(catalog_.identity(), catalog_.root())) continue;
84 units.emplace_back(pair_type{}, native);
85 units.back().native_claim.emplace(native->bindings_.prepare(catalog_.identity(), catalog_.root()));
86 }
87 if (units.size() < 2) return 0;
88
89 std::vector<ready_unit *> claimed;
90 claimed.reserve(units.size());
91 for (auto & unit : units) {
92 if (unit.pair_claim) {
93 if (!unit.pair_claim->try_lock()) continue;
94 if (unit.pair_claim->value()) { unit.pair_claim->release(); continue; }
95 }
96 if (unit.native_claim) {
97 if (!unit.native_claim->try_lock()) {
98 if (unit.pair_claim) unit.pair_claim->release();
99 continue;
100 }
101 if (unit.native_claim->value()) {
102 unit.native_claim->release();
103 if (unit.pair_claim) unit.pair_claim->release();
104 continue; // Re-resolve the changed plan in ordinary fallback.
105 }
106 }
107 claimed.push_back(&unit);
108 }
109 if (claimed.size() < 2) return 0; // Claims leave scope before fallback.
110
111 std::vector<catalog_object_reservation> outputs;
112 std::vector<blob_identity> inputs;
113 outputs.reserve(claimed.size() * 2);
114 for (auto * unit : claimed) {
115 if (unit->native) {
116 unit->native_id = unit->native->receipt.object;
117 } else {
118 unit->native_id = ids_();
119 outputs.push_back({*unit->native_id, file_kind::native_blob});
120 }
121 if (unit->pair) {
122 unit->index_id = ids_();
123 outputs.push_back({*unit->index_id, file_kind::fractional_index});
124 }
125 if (unit->main) inputs.push_back(unit->main->identity);
126 }
127 std::sort(inputs.begin(), inputs.end(), [](auto const & a, auto const & b) {
128 return a.native.hex() < b.native.hex() || (a.native == b.native && a.index.hex() < b.index.hex());
129 });
130 inputs.erase(std::unique(inputs.begin(), inputs.end()), inputs.end());
131 auto attempt = reserve(outputs, inputs);
132 for (auto * unit : claimed) finish_ready(*unit, attempt);
133 return claimed.size();
134 }
135
136 std::shared_ptr<native_binding_type const> ensure_native(native_pointer const & native) {
137 if (!native) throw std::invalid_argument("null runtime native owner");
138 auto result = native->bindings_.get_or_create(catalog_.identity(), catalog_.root(), [&] {
139 return produce_native(native);
140 });
141 // A binding attests to acknowledged construction. Import into this
142 // backend still checks the current envelope, without scanning its body.
144 return result;
145 }
146
147 std::shared_ptr<pair_binding_type const> ensure_pair(pair_type const & pair) {
148 if (!pair) throw std::invalid_argument("null runtime pair owner");
149 auto result = pair->bindings_.get_or_create(catalog_.identity(), catalog_.root(), [&] {
150 if (!pair->built()) throw std::invalid_argument("mapped pair has no binding in this catalog");
151 // Dependencies can share this pair's native owner. Resolve them before
152 // taking its producer lock, so aliases select the acknowledged fallback.
153 auto main = pair->main_target() ? ensure_pair(pair->main_target()) : nullptr;
154 auto secondary = pair->secondary_target() ? ensure_native(pair->secondary_target()) : nullptr;
155 auto const & owner = pair->native_owner();
156 if (!owner) throw std::invalid_argument("null runtime native owner");
157 std::optional<pair_seal> fused;
158 std::shared_ptr<typename mapped_type::index_type const> fused_index;
159 auto native = owner->bindings_.get_or_create(catalog_.identity(), catalog_.root(), [&] {
160 if constexpr (requires { owner->sealed(); })
161 if (owner->sealed()) return produce_native(owner);
162 if (!owner->owned()) return produce_native(owner);
163 blob_identity id{ids_(), ids_()};
164 std::array outputs{catalog_object_reservation{id.native, file_kind::native_blob},
165 catalog_object_reservation{id.index, file_kind::fractional_index}};
166 std::array<blob_identity, 1> input{main ? main->identity : id};
167 auto attempt = reserve(outputs, std::span<blob_identity const>(input.data(), main ? 1 : 0));
168 auto native_receipt = encode_native(owner).seal(catalog_.root(), id.native, attempt);
169 auto encoded = encode_cola_sections(*pair->built(), id.native,
170 main ? std::optional<blob_identity>(main->identity) : std::nullopt,
171 secondary ? std::optional<object_id>(secondary->receipt.object) : std::nullopt);
172 auto index_receipt = encoded.seal(catalog_.root(), id.index, attempt);
173 auto [mapped_native, index] = catalog_.template seal_native_pair<mapped_type>(
174 operation(), id, native_receipt, index_receipt);
175 auto binding = std::make_shared<native_binding_type const>(
176 native_seal{catalog_.identity(), std::move(native_receipt)}, std::move(mapped_native));
177 fused.emplace(pair_seal{catalog_.identity(), id, std::move(index_receipt)});
178 fused_index = std::move(index);
179 return binding;
180 });
182 if (fused) {
183 auto mapped = mapped_type::bind(fused->identity, native->mapped, std::move(fused_index),
184 main ? main->mapped : nullptr, secondary ? secondary->mapped : nullptr,
185 secondary ? std::optional<object_id>(secondary->receipt.object) : std::nullopt);
186 return std::make_shared<pair_binding_type const>(std::move(*fused), std::move(mapped));
187 }
188 blob_identity id{native->receipt.object, ids_()};
189 std::array<blob_identity, 1> input{main ? main->identity : id};
190 auto attempt = reserve(id.index, file_kind::fractional_index,
191 std::span<blob_identity const>(input.data(), main ? 1 : 0));
192 auto encoded = encode_cola_sections(*pair->built(), id.native,
193 main ? std::optional<blob_identity>(main->identity) : std::nullopt,
194 secondary ? std::optional<object_id>(secondary->receipt.object) : std::nullopt);
195 auto receipt = encoded.seal(catalog_.root(), id.index, attempt);
196 auto index = catalog_.template seal_pair<mapped_type>(operation(), id, receipt);
197 auto mapped = mapped_type::bind(id, native->mapped, std::move(index),
198 main ? main->mapped : nullptr, secondary ? secondary->mapped : nullptr,
199 secondary ? std::optional<object_id>(secondary->receipt.object) : std::nullopt);
200 return std::make_shared<pair_binding_type const>(
201 pair_seal{catalog_.identity(), std::move(id), std::move(receipt)}, std::move(mapped));
202 });
204 return result;
205 }
206
207 // Mapping admission has already established the exact graph. Bind its
208 // facade owners to the completed catalog rows without retaining a second
209 // registry of weak owners. Normal reads inspect metadata only.
210 void bind_native(native_pointer const & native, object_id const & id) {
211 auto result = native->bindings_.get_or_create(catalog_.identity(), catalog_.root(), [&] {
212 return std::make_shared<native_binding_type const>(native_seal{
213 catalog_.identity(), catalog_.sealed_receipt(id, file_kind::native_blob)}, native->mapped());
214 });
215 if (result->receipt.object != id) throw std::invalid_argument("native owner has another catalog identity");
216 }
217 void bind_pair(pair_type const & pair, blob_identity const & id) {
218 auto result = pair->bindings_.get_or_create(catalog_.identity(), catalog_.root(), [&] {
219 return std::make_shared<pair_binding_type const>(pair_seal{
220 catalog_.identity(), id, catalog_.sealed_receipt(id.index, file_kind::fractional_index)}, pair->mapped());
221 });
222 if (result->identity != id) throw std::invalid_argument("pair owner has another catalog identity");
223 }
224 object_id native_id(native_pointer const & native) const {
225 auto value = native->bindings_.find(catalog_.identity(), catalog_.root());
226 if (!value) throw std::logic_error("runtime native has not been sealed");
227 return value->receipt.object;
228 }
229 blob_identity pair_id(pair_type const & pair) const {
230 auto value = pair->bindings_.find(catalog_.identity(), catalog_.root());
231 if (!value) throw std::logic_error("runtime pair has not been sealed");
232 return value->identity;
233 }
234
236 auto binding = native->bindings_.find(catalog_.identity(), catalog_.root());
237 if (!binding) throw std::logic_error("runtime native has not been sealed");
238 if (native->mapped() == binding->mapped) return native;
239 // Only a distinct facade is cached. A mapped owner returns itself above,
240 // so the memo never acquires a shared-pointer reference to its own owner.
241 return native->mapped_owners_.get_or_create(catalog_.identity(), catalog_.root(), [&] {
242 auto result = native_type::from_mapped(binding->mapped);
243 result->bindings_.get_or_create(catalog_.identity(), catalog_.root(), [&] { return binding; });
244 return result;
245 });
246 }
248 auto binding = pair->bindings_.find(catalog_.identity(), catalog_.root());
249 if (!binding) throw std::logic_error("runtime pair has not been sealed");
250 if (pair->canonical_mapped() && pair->mapped() == binding->mapped) return pair;
251 return pair->mapped_owners_.get_or_create(catalog_.identity(), catalog_.root(), [&] {
252 auto result = node_type::from_mapped_parts(binding->mapped, mapped_native(pair->native_owner()),
253 pair->main_target() ? mapped_pair(pair->main_target()) : nullptr,
254 pair->secondary_target() ? mapped_native(pair->secondary_target()) : nullptr);
255 result->bindings_.get_or_create(catalog_.identity(), catalog_.root(), [&] { return binding; });
256 return result;
257 });
258 }
259
260 private:
263 struct ready_unit {
266 std::shared_ptr<pair_binding_type const> main;
267 std::shared_ptr<native_binding_type const> secondary, native;
268 std::optional<pair_producer> pair_claim;
269 std::optional<native_producer> native_claim;
270 std::optional<object_id> native_id, index_id;
271 ready_unit(pair_type p, native_pointer n, std::shared_ptr<pair_binding_type const> m = {},
272 std::shared_ptr<native_binding_type const> s = {}, std::shared_ptr<native_binding_type const> value = {})
273 : pair(std::move(p)), owner(std::move(n)), main(std::move(m)), secondary(std::move(s)), native(std::move(value)) {}
274 };
275 static bool batch_owned(native_pointer const & native) {
276 if constexpr (requires { native->sealed(); }) if (native->sealed()) return false;
277 return bool(native->owned());
278 }
279 void finish_ready(ready_unit & unit, object_attempt_id const & attempt) {
280 std::optional<object_seal_receipt> native_receipt;
281 if (!unit.native) native_receipt.emplace(encode_native(unit.owner).seal(catalog_.root(), *unit.native_id, attempt));
282 if (!unit.pair) {
283 catalog_.record_sealed(operation(), *native_receipt);
284 auto mapped = std::make_shared<mapped_native_type const>(mapped_native_type::open(native_receipt->path));
285 auto binding = std::make_shared<native_binding_type const>(
286 native_seal{catalog_.identity(), std::move(*native_receipt)}, std::move(mapped));
287 unit.native_claim->install(std::move(binding));
288 return;
289 }
290 blob_identity id{*unit.native_id, *unit.index_id};
291 auto encoded = encode_cola_sections(*unit.pair->built(), id.native,
292 unit.main ? std::optional<blob_identity>(unit.main->identity) : std::nullopt,
293 unit.secondary ? std::optional<object_id>(unit.secondary->receipt.object) : std::nullopt);
294 auto receipt = encoded.seal(catalog_.root(), id.index, attempt);
295 std::shared_ptr<typename mapped_type::index_type const> index;
296 if (native_receipt) {
297 auto [mapped_native, prepared_index] = catalog_.template seal_native_pair<mapped_type>(
298 operation(), id, *native_receipt, receipt);
299 unit.native = std::make_shared<native_binding_type const>(
300 native_seal{catalog_.identity(), std::move(*native_receipt)}, std::move(mapped_native));
301 unit.native_claim->install(unit.native);
302 index = std::move(prepared_index);
303 } else index = catalog_.template seal_pair<mapped_type>(operation(), id, receipt);
304 auto mapped = mapped_type::bind(id, unit.native->mapped, std::move(index),
305 unit.main ? unit.main->mapped : nullptr, unit.secondary ? unit.secondary->mapped : nullptr,
306 unit.secondary ? std::optional<object_id>(unit.secondary->receipt.object) : std::nullopt);
307 auto binding = std::make_shared<pair_binding_type const>(
308 pair_seal{catalog_.identity(), id, std::move(receipt)}, std::move(mapped));
309 unit.pair_claim->install(std::move(binding));
310 }
312 Ids & ids_;
313 std::string * last_operation_;
314 std::string operation() {
315 auto id = ids_().hex();
316 if (last_operation_) *last_operation_ = id;
317 return id;
318 }
319 auto encode_native(native_pointer const & native) {
320 if constexpr (requires { typename Family::storage_type; })
321 return Family::storage_type::encode_native(*native->owned());
322 else return encode_native_sections(*native->owned());
323 }
324 std::shared_ptr<native_binding_type const> produce_native(native_pointer const & native) {
325 if constexpr (requires { native->sealed(); }) {
326 if (auto sealed = native->sealed()) {
327 if (sealed->catalog != catalog_.identity())
328 throw std::invalid_argument("native seal belongs to another catalog");
329 catalog_.verify_sealed(sealed->receipt, file_kind::native_blob);
330 return std::make_shared<native_binding_type const>(*sealed, native->mapped());
331 }
332 }
333 if (!native->owned()) throw std::invalid_argument("mapped native has no binding in this catalog");
334 auto id = ids_();
335 auto attempt = reserve(id, file_kind::native_blob);
336 auto receipt = encode_native(native).seal(catalog_.root(), id, attempt);
337 catalog_.record_sealed(operation(), receipt);
338 auto mapped = std::make_shared<mapped_native_type const>(mapped_native_type::open(receipt.path));
339 return std::make_shared<native_binding_type const>(
340 native_seal{catalog_.identity(), std::move(receipt)}, std::move(mapped));
341 }
342 object_attempt_id reserve(object_id const & id, file_kind kind, std::span<blob_identity const> inputs = {}) {
343 std::array<catalog_object_reservation, 1> outputs{{{id, kind}}};
344 return reserve(outputs, inputs);
345 }
346 object_attempt_id reserve(std::span<catalog_object_reservation const> outputs,
347 std::span<blob_identity const> inputs = {}) {
348 object_attempt_id attempt(ids_().hex());
349 auto owner = ids_().hex();
350 catalog_.reserve(operation(), attempt, owner, inputs, outputs);
351 return attempt;
352 }
353 };
354}
Definition runtime_checkpoint.h:23
encoded_cola_sections< P > encode_cola_sections(cola_index< P, Native, Main > const &source, object_id const &native_id, std::optional< blob_identity > main_id={}, std::optional< object_id > secondary_id={})
Definition cola_sections.h:256
encoded_sections< P > encode_native_sections(profile_array< P, stream_role::native > const &native)
Definition sections.h:405
file_kind
Definition object_path.h:25
Selects runtime checkpoint codecs and encodes binary admission intervals.
Describes completed native and index objects retained by runtime owners.
Stores retained roots, timeline generations and exact file graphs in optional SQLite metadata.
Definition sections.h:34
Definition catalog_bindings.h:41
Definition sections.h:442
Definition runtime_seal.h:30
Definition runtime_seal.h:19
Definition object_writer.h:39
Definition object_path.h:38
Definition runtime_seal.h:36
Definition runtime_seal.h:24
blob_identity identity
Definition runtime_seal.h:26
Definition runtime_graph_sealer.h:263
std::shared_ptr< native_binding_type const > native
Definition runtime_graph_sealer.h:267
std::optional< object_id > native_id
Definition runtime_graph_sealer.h:270
std::optional< pair_producer > pair_claim
Definition runtime_graph_sealer.h:268
std::optional< object_id > index_id
Definition runtime_graph_sealer.h:270
native_pointer owner
Definition runtime_graph_sealer.h:265
std::optional< native_producer > native_claim
Definition runtime_graph_sealer.h:269
std::shared_ptr< pair_binding_type const > main
Definition runtime_graph_sealer.h:266
std::shared_ptr< native_binding_type const > secondary
Definition runtime_graph_sealer.h:267
pair_type pair
Definition runtime_graph_sealer.h:264
ready_unit(pair_type p, native_pointer n, std::shared_ptr< pair_binding_type const > m={}, std::shared_ptr< native_binding_type const > s={}, std::shared_ptr< native_binding_type const > value={})
Definition runtime_graph_sealer.h:271
Definition runtime_graph_sealer.h:22
typename node_type::pair_type pair_type
Definition runtime_graph_sealer.h:26
static bool batch_owned(native_pointer const &native)
Definition runtime_graph_sealer.h:275
std::string * last_operation_
Definition runtime_graph_sealer.h:313
catalog_type & catalog_
Definition runtime_graph_sealer.h:311
typename Family::native_type native_type
Definition runtime_graph_sealer.h:25
object_attempt_id reserve(std::span< catalog_object_reservation const > outputs, std::span< blob_identity const > inputs={})
Definition runtime_graph_sealer.h:346
graph_sealer(catalog_type &catalog, Ids &ids, std::string *last_operation=nullptr)
Definition runtime_graph_sealer.h:34
void finish_ready(ready_unit &unit, object_attempt_id const &attempt)
Definition runtime_graph_sealer.h:279
typename node_type::native_pointer native_pointer
Definition runtime_graph_sealer.h:27
native_pointer mapped_native(native_pointer const &native)
Definition runtime_graph_sealer.h:235
std::shared_ptr< native_binding_type const > produce_native(native_pointer const &native)
Definition runtime_graph_sealer.h:324
decltype(std::declval< node_type const & >().mapped()) mapped_pointer
Definition runtime_graph_sealer.h:28
pair_type mapped_pair(pair_type const &pair)
Definition runtime_graph_sealer.h:247
std::string operation()
Definition runtime_graph_sealer.h:314
Ids & ids_
Definition runtime_graph_sealer.h:312
typename catalog_bindings< pair_binding_type >::producer pair_producer
Definition runtime_graph_sealer.h:262
typename catalog_bindings< native_binding_type >::producer native_producer
Definition runtime_graph_sealer.h:261
void bind_pair(pair_type const &pair, blob_identity const &id)
Definition runtime_graph_sealer.h:217
object_attempt_id reserve(object_id const &id, file_kind kind, std::span< blob_identity const > inputs={})
Definition runtime_graph_sealer.h:342
typename mapped_type::native_type mapped_native_type
Definition runtime_graph_sealer.h:30
std::shared_ptr< pair_binding_type const > ensure_pair(pair_type const &pair)
Definition runtime_graph_sealer.h:147
blob_identity pair_id(pair_type const &pair) const
Definition runtime_graph_sealer.h:229
auto encode_native(native_pointer const &native)
Definition runtime_graph_sealer.h:319
void bind_native(native_pointer const &native, object_id const &id)
Definition runtime_graph_sealer.h:210
std::size_t prepare_ready(std::span< pair_type const > roots, std::span< native_pointer const > natives)
Definition runtime_graph_sealer.h:41
typename Family::node_type node_type
Definition runtime_graph_sealer.h:24
object_id native_id(native_pointer const &native) const
Definition runtime_graph_sealer.h:224
std::remove_const_t< typename mapped_pointer::element_type > mapped_type
Definition runtime_graph_sealer.h:29
std::shared_ptr< native_binding_type const > ensure_native(native_pointer const &native)
Definition runtime_graph_sealer.h:136
Definition sqlite_catalog.h:351
void verify_sealed(object_seal_receipt const &receipt, file_kind expected) const
Definition sqlite_catalog.h:482
std::filesystem::path const & root() const &noexcept
Definition sqlite_catalog.h:396
void record_sealed(std::string_view op, object_seal_receipt const &receipt)
Definition sqlite_catalog.h:552
object_id const & identity() const &
Definition sqlite_catalog.h:398
void reserve(std::string_view op, object_attempt_id const &attempt, std::string_view owner, std::span< blob_identity const > inputs, std::span< catalog_object_reservation const > outputs)
Definition sqlite_catalog.h:518