Everett
Loading...
Searching...
No Matches
runtime_store.h
Go to the documentation of this file.
1
12#pragma once
13
18
19#include <array>
20#include <unordered_map>
21#include <unordered_set>
22
23#if defined(__APPLE__) || defined(__linux__)
24#include <sys/random.h>
25#endif
26
27namespace everett {
28 // Physical and operation identities use OS entropy; semantic fingerprints
29 // are unrelated. Catalog reservations and exclusive installation still
30 // reject a collision instead of replacing an existing object.
33#if defined(__APPLE__) || defined(__linux__)
34 std::array<unsigned char, 16> bytes{};
35 if (::getentropy(bytes.data(), bytes.size()))
36 throw std::system_error(errno, std::generic_category(), "Everett identity entropy");
37 constexpr char digits[] = "0123456789abcdef";
38 std::string text(32, '0');
39 for (std::size_t i = 0; i != bytes.size(); ++i) {
40 text[i * 2] = digits[bytes[i] >> 4]; text[i * 2 + 1] = digits[bytes[i] & 15];
41 }
42 return object_id(std::move(text));
43#else
44 throw std::system_error(std::make_error_code(std::errc::operation_not_supported), "Everett identity entropy");
45#endif
46 }
47 };
48
49 template <class P, class Family = binary_runtime_family<P>> struct stored_runtime {
51 typename Family::snapshot_type snapshot;
52 std::vector<std::byte> semantic;
53 };
54
55 // Single-owner adapter over a trusted, existing, durable directory. All
56 // persisted roots remain catalog-pinned. Private failures disable this
57 // adapter; reopening selects only complete root/checkpoint publications.
58 template <class P, class Ids = random_object_ids, class Ops = sqlite_catalog_ops, class Family = binary_runtime_family<P>> struct runtime_store {
59 using policy_type = P;
60 using family_type = Family;
62 using snapshot_type = typename Family::snapshot_type;
64 using node_type = typename Family::node_type;
65 using native_type = typename Family::native_type;
66 using pair_type = typename node_type::pair_type;
67 using native_pointer = typename node_type::native_pointer;
68 using mapped_pointer = decltype(std::declval<node_type const &>().mapped());
69 using mapped_type = std::remove_const_t<typename mapped_pointer::element_type>;
72 static_assert(std::is_same_v<P, typename Family::policy_type>);
73
74 static runtime_store create(std::filesystem::path const & root, Ids ids = {},
75 catalog_options options = {}, Ops ops = {}) {
76 auto identity = ids();
77 return runtime_store(catalog_type::create_sessions(root, identity, options, std::move(ops)), std::move(ids));
78 }
79 static runtime_store open(std::filesystem::path const & root, Ids ids = {},
80 catalog_options options = {}, Ops ops = {}) {
81 auto db = catalog_type::open(root, options, std::move(ops));
82 if (db.schema_version() != 4) throw std::invalid_argument("named runtime needs catalog version 4");
83 return runtime_store(std::move(db), std::move(ids));
84 }
85 runtime_store(runtime_store const &) = delete;
87 runtime_store(runtime_store &&) noexcept(std::is_nothrow_move_constructible_v<Ids>) = default;
88 runtime_store & operator=(runtime_store &&) = delete;
89 bool failed() const noexcept { return failed_ || catalog_.poisoned(); }
90 std::filesystem::path const & root() const & noexcept { return catalog_.root(); }
91 std::filesystem::path const & root() const && = delete;
92
93 std::optional<stored_type> find(std::string_view name) {
95 auto head = catalog_.find_session(name);
96 return head ? std::optional<stored_type>{restore(std::move(*head))} : std::nullopt;
97 }
98 std::optional<stored_type> find_save(std::string_view name) {
100 auto head = catalog_.find_saved_session(name);
101 return head ? std::optional<stored_type>{restore(std::move(*head))} : std::nullopt;
102 }
103 // Seal the complete visible/hidden frontier under construction owners.
104 // This creates no named generation and grants no publication authority.
105 void prepare(snapshot_type const & source) {
107 try { (void)persist(source); }
108 catch (...) { failed_ = true; throw; }
109 }
110 stored_type create_session(std::string_view name, snapshot_type const & source,
111 std::span<std::byte const> semantic = {}) {
113 try {
114 auto id = persist(source);
115 auto checkpoint = codec_type::encode(source, semantic,
116 [&](pair_type const & pair) { return sealer().pair_id(pair); },
117 [&](native_pointer const & native) { return sealer().native_id(native); });
118 auto op = operation();
119 return restore(catalog_.create_session(op, name, id.head, checkpoint, id.auxiliary), &source);
120 } catch (...) { failed_ = true; throw; }
121 }
122 stored_type publish(catalog_session_head const & expected, snapshot_type const & source,
123 std::span<std::byte const> semantic = {}) {
125 try {
126 auto id = persist(source);
127 auto checkpoint = codec_type::encode(source, semantic,
128 [&](pair_type const & pair) { return sealer().pair_id(pair); },
129 [&](native_pointer const & native) { return sealer().native_id(native); });
130 auto op = operation();
131 auto published = catalog_.publish_session(op, expected, id.head, checkpoint, id.auxiliary);
132 if (!published.published) throw std::runtime_error("named Everett session was advanced by another connection");
133 return restore(std::move(published.head), &source);
134 } catch (...) { failed_ = true; throw; }
135 }
136 stored_type fork(std::string_view name, catalog_session_head const & source) {
138 try { auto op = operation(); return restore(catalog_.fork_session(op, name, source)); }
139 catch (...) { failed_ = true; throw; }
140 }
141 void save(std::string_view name, catalog_session_head const & source) {
143 try { auto op = operation(); catalog_.save_session(op, name, source); }
144 catch (...) { failed_ = true; throw; }
145 }
146 std::string const & last_operation() const noexcept { return last_operation_; }
147
148 private:
152 Ids ids_;
153 bool failed_ = false;
154 std::string last_operation_;
155
156 runtime_store(catalog_type catalog, Ids ids) : catalog_(std::move(catalog)), ids_(std::move(ids)) {}
157 void require_active() const { if (failed()) throw std::logic_error("failed Everett runtime store; reopen it"); }
158 std::string operation() { last_operation_ = ids_().hex(); return last_operation_; }
160 // Mapping one checkpoint shares physical mappings and typed facades across
161 // every visible and hidden root, preserving exact immutable dependencies.
162 struct resolver {
165 std::unordered_map<std::string, native_pointer> natives;
166 std::unordered_map<std::string, pair_type> pairs;
167 bool restricted = false;
168 explicit resolver(runtime_store & value) : store(value), mapped(value.root()) {}
170 auto found = natives.find(id.hex());
171 if (found == natives.end()) {
172 if (restricted) throw std::invalid_argument("checkpoint native has no durable pin");
173 auto value = native_type::from_mapped(mapped.native(id));
174 store.sealer().bind_native(value, id);
175 found = natives.emplace(id.hex(), std::move(value)).first;
176 }
177 return found->second;
178 }
180 if (auto found = pairs.find(id.index.hex()); found != pairs.end()) {
181 if (found->second->mapped()->identity() != id)
182 throw std::invalid_argument("checkpoint index has another native identity");
183 return found->second;
184 }
185 if (restricted) {
186 throw std::invalid_argument("checkpoint pair has no durable pin");
187 }
188 auto physical = mapped.pair(id);
189 std::vector<mapped_pointer> pending;
190 pair_type result;
191 for (auto p = physical; p; p = p->main_target()) {
192 if (auto known = pairs.find(p->identity().index.hex()); known != pairs.end()) { result = known->second; break; }
193 pending.push_back(p);
194 }
195 for (auto i = pending.rbegin(); i != pending.rend(); ++i) {
196 auto const & p = *i;
197 result = node_type::from_mapped_parts(p, native(p->identity().native), result,
198 p->index_object()->secondary_id() ? native(*p->index_object()->secondary_id()) : native_pointer{});
199 pairs.emplace(p->identity().index.hex(), result);
200 store.sealer().bind_pair(result, p->identity());
201 }
202 return result;
203 }
204 bool seed_native(object_id const & id, native_pointer value) {
205 auto [found, inserted] = natives.try_emplace(id.hex(), value);
206 return inserted || found->second == value;
207 }
208 bool seed_root(pair_type value) {
209 auto const & id = value->mapped()->identity();
210 auto [found, inserted] = pairs.try_emplace(id.index.hex(), value);
211 return inserted || found->second == value;
212 }
213 };
214 struct roots {
215 std::vector<pair_type> pairs;
216 std::vector<typename registry_type::native_root> natives;
217 };
218 template <class Resolver> static std::optional<roots> retained_roots(catalog_session_head const & head, Resolver & loaded) {
219 roots result;
220 result.pairs.reserve(head.auxiliary.pairs.size() + 1);
221 result.natives.reserve(head.auxiliary.natives.size());
222 auto pair = loaded.pair(head.timeline.head);
223 if (!pair) return std::nullopt;
224 result.pairs.push_back(std::move(pair));
225 for (auto const & id : head.auxiliary.pairs) {
226 pair = loaded.pair(id);
227 if (!pair) return std::nullopt;
228 result.pairs.push_back(std::move(pair));
229 }
230 for (auto const & id : head.auxiliary.natives) {
231 auto native = loaded.native(id);
232 if (!native) return std::nullopt;
233 result.natives.emplace_back(id, std::move(native));
234 }
235 return result;
236 }
239 pair_type pair(blob_identity const & id) const {
240 auto value = retained.pair(id);
241 if (!value) throw std::invalid_argument("checkpoint pair has no durable pin");
242 return value;
243 }
244 native_pointer native(object_id const & id) const {
245 auto value = retained.native(id);
246 if (!value) throw std::invalid_argument("checkpoint native has no durable pin");
247 return value;
248 }
249 };
252 try {
253 auto restored = codec_type::decode(head.checkpoint, head.timeline.head, loaded);
254 return {std::move(head), std::move(restored.snapshot), std::move(restored.semantic)};
255 } catch (...) {
256 // A failed checkpoint never leaves an ambiguous authorization cache.
257 retained_.clear(); throw;
258 }
259 }
260 stored_type restore(catalog_session_head head, snapshot_type const * source = nullptr) {
261 if (source) {
262 resolver candidates(*this);
263 auto graph = sealer();
264 bool coherent = true;
265 codec_type::collect(*source, [&](pair_type const & pair) {
266 if (pair) coherent = candidates.seed_root(graph.mapped_pair(pair)) && coherent;
267 }, [&](native_pointer const & native) {
268 if (native) coherent = candidates.seed_native(graph.native_id(native), graph.mapped_native(native)) && coherent;
269 });
270 // Candidate roots provide owners, not authority. Only the roots in the
271 // acknowledged catalog head acquire registry references. Shared live
272 // owners stop at their local count; new edges are checked once.
273 if (coherent) {
274 candidates.restricted = true;
275 auto selected = retained_roots(head, candidates);
276 if (!selected) throw std::logic_error("published root has no prepared owner");
277 if (retained_.replace(std::move(selected->pairs), std::move(selected->natives)))
278 return decode_retained(std::move(head));
279 }
280 } else if (auto selected = retained_roots(head, retained_)) {
281 if (retained_.replace(std::move(selected->pairs), std::move(selected->natives)))
282 return decode_retained(std::move(head));
283 }
284 // An imported graph may contain distinct owners for the same physical
285 // identity, even deep in a new prefix. Intern the entire incoming graph
286 // afresh in that exceptional case. The old registry remains intact until
287 // this new closure has been validated and decoded.
288 resolver loaded(*this);
289 auto selected = retained_roots(head, loaded);
290 if (!selected) throw std::logic_error("durable root has no mapped owner");
291 loaded.restricted = true;
293 if (!replacement.replace(std::move(selected->pairs), std::move(selected->natives)))
294 throw std::invalid_argument("mapped checkpoint has conflicting facade identities");
296 auto restored = codec_type::decode(head.checkpoint, head.timeline.head, pinned);
297 retained_ = std::move(replacement);
298 return {std::move(head), std::move(restored.snapshot), std::move(restored.semantic)};
299 }
302 std::vector<pair_type> roots;
303 std::vector<native_pointer> native_roots;
304 std::unordered_set<node_type const *> seen_pairs;
305 std::unordered_set<native_type const *> seen_natives;
306 codec_type::collect(source, [&](pair_type value) {
307 if (value && seen_pairs.insert(value.get()).second) roots.push_back(std::move(value));
308 }, [&](native_pointer value) {
309 if (value && seen_natives.insert(value.get()).second) native_roots.push_back(std::move(value));
310 });
311 auto graph = sealer();
312 (void)graph.prepare_ready(roots, native_roots);
313 // Each new owner resolves its immediate dependencies once. An existing
314 // bound suffix stops this walk, irrespective of how many roots share it.
315 // Finish each remaining root before starting another. The ready batch
316 // has released every claim before this blocking dependency walk.
317 for (auto const & pair : roots) (void)graph.ensure_pair(pair);
318 for (auto const & native : native_roots) (void)graph.ensure_native(native);
319 auto primary = graph.pair_id(source.query_root().head());
321 for (auto const & pair : roots) retained.pairs.push_back(graph.pair_id(pair));
322 for (auto const & native : native_roots) retained.natives.push_back(graph.native_id(native));
323 return {primary, catalog_detail::canonical_auxiliary(std::move(retained), primary)};
324 }
325 };
326}
catalog_auxiliary_roots canonical_auxiliary(catalog_auxiliary_roots value, blob_identity const &primary)
Definition sqlite_catalog.h:136
void identity(bytes &out, object_id const &value)
Definition sqlite_catalog.h:117
Definition active_engine.h:18
Selects runtime checkpoint codecs and encodes binary admission intervals.
Seals new runtime owners once and stops at acknowledged shared dependencies.
Retains one publication's exact owner closure without revisiting shared suffixes.
Stores retained roots, timeline generations and exact file graphs in optional SQLite metadata.
Definition sections.h:34
Definition sqlite_catalog.h:71
std::vector< blob_identity > pairs
Definition sqlite_catalog.h:72
std::vector< object_id > natives
Definition sqlite_catalog.h:73
Definition sqlite_catalog.h:39
Definition sqlite_catalog.h:76
catalog_auxiliary_roots auxiliary
Definition sqlite_catalog.h:79
catalog_timeline_head timeline
Definition sqlite_catalog.h:77
std::vector< std::byte > checkpoint
Definition sqlite_catalog.h:78
blob_identity head
Definition sqlite_catalog.h:60
native_pointer native(object_id const &id)
Definition mapped_cola.h:221
pair_type pair(blob_identity const &head)
Definition mapped_cola.h:227
Definition object_path.h:38
Definition runtime_store.h:31
object_id operator()() const
Definition runtime_store.h:32
Definition runtime_checkpoint.h:62
Definition runtime_store.h:300
catalog_auxiliary_roots auxiliary
Definition runtime_store.h:300
blob_identity head
Definition runtime_store.h:300
Definition runtime_store.h:237
pair_type pair(blob_identity const &id) const
Definition runtime_store.h:239
registry_type const & retained
Definition runtime_store.h:238
native_pointer native(object_id const &id) const
Definition runtime_store.h:244
Definition runtime_store.h:162
pair_type pair(blob_identity const &id)
Definition runtime_store.h:179
native_pointer native(object_id const &id)
Definition runtime_store.h:169
runtime_store & store
Definition runtime_store.h:163
bool restricted
Definition runtime_store.h:167
bool seed_root(pair_type value)
Definition runtime_store.h:208
bool seed_native(object_id const &id, native_pointer value)
Definition runtime_store.h:204
resolver(runtime_store &value)
Definition runtime_store.h:168
std::unordered_map< std::string, pair_type > pairs
Definition runtime_store.h:166
mapped_resolver mapped
Definition runtime_store.h:164
std::unordered_map< std::string, native_pointer > natives
Definition runtime_store.h:165
Definition runtime_store.h:214
std::vector< pair_type > pairs
Definition runtime_store.h:215
std::vector< typename registry_type::native_root > natives
Definition runtime_store.h:216
Definition runtime_graph_sealer.h:22
void clear() noexcept
Definition runtime_registry.h:80
native_pointer native(object_id const &id) const
Definition runtime_registry.h:54
pair_type pair(blob_identity const &id) const
Definition runtime_registry.h:47
bool replace(std::vector< pair_type > pairs, std::vector< native_root > natives)
Definition runtime_registry.h:64
Definition runtime_store.h:58
auto sealer()
Definition runtime_store.h:159
stored_type publish(catalog_session_head const &expected, snapshot_type const &source, std::span< std::byte const > semantic={})
Definition runtime_store.h:122
Family family_type
Definition runtime_store.h:60
std::optional< stored_type > find_save(std::string_view name)
Definition runtime_store.h:98
typename Family::node_type node_type
Definition runtime_store.h:64
stored_type create_session(std::string_view name, snapshot_type const &source, std::span< std::byte const > semantic={})
Definition runtime_store.h:110
typename node_type::pair_type pair_type
Definition runtime_store.h:66
stored_type restore(catalog_session_head head, snapshot_type const *source=nullptr)
Definition runtime_store.h:260
typename node_type::native_pointer native_pointer
Definition runtime_store.h:67
runtime_store & operator=(runtime_store const &)=delete
stored_type decode_retained(catalog_session_head head)
Definition runtime_store.h:250
typename Family::native_type native_type
Definition runtime_store.h:65
P policy_type
Definition runtime_store.h:59
Ids ids_
Definition runtime_store.h:152
typename Family::snapshot_type snapshot_type
Definition runtime_store.h:62
persisted persist(snapshot_type const &source)
Definition runtime_store.h:301
runtime_store(catalog_type catalog, Ids ids)
Definition runtime_store.h:156
void prepare(snapshot_type const &source)
Definition runtime_store.h:105
void save(std::string_view name, catalog_session_head const &source)
Definition runtime_store.h:141
stored_type fork(std::string_view name, catalog_session_head const &source)
Definition runtime_store.h:136
std::string operation()
Definition runtime_store.h:158
static runtime_store open(std::filesystem::path const &root, Ids ids={}, catalog_options options={}, Ops ops={})
Definition runtime_store.h:79
std::string last_operation_
Definition runtime_store.h:154
bool failed_
Definition runtime_store.h:153
bool failed() const noexcept
Definition runtime_store.h:89
std::string const & last_operation() const noexcept
Definition runtime_store.h:146
registry_type retained_
Definition runtime_store.h:150
static runtime_store create(std::filesystem::path const &root, Ids ids={}, catalog_options options={}, Ops ops={})
Definition runtime_store.h:74
runtime_store(runtime_store const &)=delete
catalog_type catalog_
Definition runtime_store.h:151
static std::optional< roots > retained_roots(catalog_session_head const &head, Resolver &loaded)
Definition runtime_store.h:218
std::filesystem::path const & root() const &&=delete
std::optional< stored_type > find(std::string_view name)
Definition runtime_store.h:93
std::filesystem::path const & root() const &noexcept
Definition runtime_store.h:90
std::remove_const_t< typename mapped_pointer::element_type > mapped_type
Definition runtime_store.h:69
runtime_store(runtime_store &&) noexcept(std::is_nothrow_move_constructible_v< Ids >)=default
void require_active() const
Definition runtime_store.h:157
decltype(std::declval< node_type const & >().mapped()) mapped_pointer
Definition runtime_store.h:68
Definition sqlite_catalog.h:351
std::optional< catalog_session_head > find_session(std::string_view name) const
Definition sqlite_catalog.h:925
static sqlite_catalog create_sessions(std::filesystem::path const &root, object_id const &identity, catalog_options options={}, Ops ops={})
Definition sqlite_catalog.h:375
static sqlite_catalog open(std::filesystem::path const &root, catalog_options options={}, Ops ops={})
Definition sqlite_catalog.h:379
std::filesystem::path const & root() const &noexcept
Definition sqlite_catalog.h:396
void save_session(std::string_view op, std::string_view name, catalog_session_head const &source)
Definition sqlite_catalog.h:999
bool poisoned() const noexcept
Definition sqlite_catalog.h:391
catalog_session_head create_session(std::string_view op, std::string_view name, blob_identity const &head, std::span< std::byte const > checkpoint, catalog_auxiliary_roots auxiliary={})
Definition sqlite_catalog.h:934
std::optional< catalog_session_head > find_saved_session(std::string_view name) const
Definition sqlite_catalog.h:1018
catalog_session_head fork_session(std::string_view op, std::string_view name, catalog_session_head const &source)
Definition sqlite_catalog.h:980
catalog_session_publication publish_session(std::string_view op, catalog_session_head const &expected, blob_identity const &candidate, std::span< std::byte const > checkpoint, catalog_auxiliary_roots auxiliary={})
Definition sqlite_catalog.h:950
Definition runtime_store.h:49
catalog_session_head head
Definition runtime_store.h:50
Family::snapshot_type snapshot
Definition runtime_store.h:51
std::vector< std::byte > semantic
Definition runtime_store.h:52