Everett
Loading...
Searching...
No Matches
transaction.h
Go to the documentation of this file.
1
12#pragma once
13
14#include <everett/connection.h>
15#include <everett/nursery_map.h>
16
17namespace everett {
18 namespace transaction_detail {
19 template <class S> inline constexpr bool state_is_arrow =
20 std::same_as<S, unsorted<std::optional<std::string>>>;
21 template <simd::architecture Arch> struct key_less {
22 bool operator()(bit_string const & a, bit_string const & b) const { return compare_bits<Arch>(a.view(), b.view()) < 0; }
23 };
24 }
25
26 // A transaction is single-owner mutable state. Frozen snapshots and branches
27 // retain immutable nursery roots, staged files and the original commit base.
28 template <class Core> struct transaction {
29 using policy_type = typename Core::policy_type;
30 using family_type = typename Core::runtime_family;
32 using world_type = typename Core::world_type;
40 std::optional<bit_string> original;
41 };
45
46 private:
47 struct origin {
48 std::filesystem::path root;
49 std::weak_ptr<session_type> session;
51 std::uint64_t maintenance_budget;
52 };
53 struct stage {
54 std::shared_ptr<scope_type> scope;
56 bool changed;
57 stage(world_type state, std::shared_ptr<scope_type> lease, bool modified)
58 : scope(std::move(lease)), world(std::move(state)), changed(modified) {}
59 };
60
61 public:
63 template <class S = typed_detail::default_sort_t<policy_type>> auto get(typed_detail::key_t<S> const & key) const {
64 auto encoded = transport::template encode<S>(key);
65 auto entry = nursery_.find(encoded);
66 if (!entry) return stage_->world.template get<S>(key);
67 auto before = [&] {
68 if constexpr (typed_detail::replacement<S>) return sort_semantics<S>::initial(key);
69 else return stage_->world.template get<S>(key);
70 }();
71 return sort_semantics<S>::apply(key, std::move(before), typed_detail::value<policy_type, S>(entry->arrow.view()));
72 }
73 std::size_t buffered_keys() const noexcept { return nursery_.size(); }
74 transaction branch() const { return transaction(*this); }
75 private:
76 std::shared_ptr<origin const> origin_;
77 std::shared_ptr<stage const> stage_;
79 snapshot_type(std::shared_ptr<origin const> source, std::shared_ptr<stage const> base,
80 typename nursery_type::snapshot_type values)
81 : origin_(std::move(source)), stage_(std::move(base)), nursery_(std::move(values)) {}
83 };
84
85 transaction(transaction const &) = delete;
86 transaction & operator=(transaction const &) = delete;
87 transaction(transaction &&) = default;
89
92 return {origin_, stage_, nursery_->freeze()};
93 }
94 template <class S = typed_detail::default_sort_t<policy_type>> auto get(typed_detail::key_t<S> const & key) const {
96 auto encoded = transport::template encode<S>(key);
97 auto entry = nursery_->find(encoded);
98 if (!entry) return stage_->world.template get<S>(key);
99 auto before = [&] {
100 if constexpr (typed_detail::replacement<S>) return sort_semantics<S>::initial(key);
101 else return stage_->world.template get<S>(key);
102 }();
103 return sort_semantics<S>::apply(key, std::move(before), typed_detail::value<policy_type, S>(entry->arrow.view()));
104 }
105 template <class S = typed_detail::default_sort_t<policy_type>> void change(
106 typed_detail::key_t<S> const & key, typed_detail::arrow_t<S> const & arrow) {
108 using semantics = sort_semantics<S>;
109 auto encoded = transport::template encode<S>(key);
110 auto previous = nursery_->find(encoded);
111 auto base = [&] {
112 if constexpr (transaction_detail::state_is_arrow<S>) {
113 if (previous) return semantics::apply(key, semantics::initial(key),
114 typed_detail::value<policy_type, S>(previous->original->view()));
115 }
116 return stage_->world.template get<S>(key);
117 }();
118 auto before = previous ? semantics::apply(key, base,
119 typed_detail::value<policy_type, S>(previous->arrow.view())) : base;
120 auto after = semantics::apply(key, before, arrow);
121 if constexpr (typed_detail::replacement<S>) {
122 if (!semantics::present(key, before) && !semantics::present(key, after))
123 throw std::invalid_argument("transaction deletion targets an absent key");
124 }
125 // Encoding and composition finish before the mutable nursery is touched.
126 auto combined = [&] {
127 if constexpr (typed_detail::replacement<S>) return arrow;
128 else if (previous) return semantics::compose(key,
129 typed_detail::value<policy_type, S>(previous->arrow.view()), arrow);
130 else return arrow;
131 }();
132 auto value = typed_detail::value<policy_type, S>(combined);
133 if constexpr (typed_detail::replacement<S>) {
134 if (after == base) { nursery_->erase(encoded); return; }
135 }
136 std::optional<bit_string> original;
137 if constexpr (transaction_detail::state_is_arrow<S>) original.emplace(typed_detail::value<policy_type, S>(base));
138 nursery_->insert_or_assign(std::move(encoded), nursery_value{std::move(value), std::move(original)});
139 }
140 template <class S = typed_detail::default_sort_t<policy_type>> void put(
141 typed_detail::key_t<S> const & key, typed_detail::state_t<S> const & value)
142 requires typed_detail::replacement<S> { change<S>(key, value); }
143 template <class S = typed_detail::default_sort_t<policy_type>> void erase(typed_detail::key_t<S> const & key)
144 requires typed_detail::replacement<S> { change<S>(key, sort_semantics<S>::erase(key)); }
145
146 // Empty flushes allocate no Core, catalog scope or output file. A nonempty
147 // flush completes typed validation and seals a private checkpoint only.
150 if (nursery_->empty()) return snapshot();
151 auto batch = Core::batch();
152 nursery_->for_each([&](bit_string const & key, nursery_value const & value) {
153 transport::dispatch(key.view(), [&]<class S>(std::type_identity<S>, auto const & decoded) {
154 batch.template change<S>(decoded, typed_detail::value<policy_type, S>(value.arrow.view()));
155 });
156 });
157 auto input = std::move(batch).finish();
159 try {
160 ensure_core();
161 while (!core_->admission_ready()) (void)core_->advance(origin_->maintenance_budget);
162 auto updated = core_->contribute(std::move(input));
163 store_->prepare(updated.runtime());
164 auto next = std::make_shared<stage const>(stage{std::move(updated), scope_, true});
165 // Preserve the old stage and frozen roots if any preceding operation
166 // fails; after this point replacement of both local roots is noexcept.
167 *nursery_ = std::move(empty);
168 stage_ = std::move(next);
169 } catch (...) { failed_ = true; throw; }
170 return snapshot();
171 }
174 auto target = origin_->session.lock();
175 if (!target) throw session_closed();
176 (void)flush();
177 if (stage_->changed && !core_) ensure_core(); // branch of a flushed snapshot
178 typename engine_type::prepared_transaction prepared{std::move(core_), origin_->base, scope_};
179 typename engine_type::contribution_type command(std::move(prepared));
180 // Submission owns the candidate from here; every failure leaves the
181 // original named world unchanged locally, with uncertain SQL outcomes
182 // reported through the same ticket contract as ordinary contributions.
183 closed_ = true;
184 auto result = target->submit(std::move(command));
185 release();
186 return result;
187 }
188 stored_type commit() { return commit_async().get()->world; }
189 void abort() noexcept { closed_ = true; release(); }
190 bool failed() const noexcept { return failed_ || (nursery_ && nursery_->failed()); }
191
192 private:
193 std::shared_ptr<origin const> origin_;
194 std::shared_ptr<stage const> stage_;
195 std::optional<nursery_type> nursery_{std::in_place};
196 // Destruction order keeps the scope alive until both SQLite connections
197 // and every private builder have been released.
198 std::shared_ptr<scope_type> scope_;
199 std::unique_ptr<store_type> store_;
200 std::unique_ptr<Core> core_;
201 bool closed_ = false, failed_ = false;
202
204 : origin_(std::make_shared<origin const>(origin{connection.root_, connection.session_,
205 connection.snapshot(), connection.options_.limits->maintenance_budget})),
206 stage_(std::make_shared<stage const>(stage{origin_->base, {}, false})) {}
207 explicit transaction(snapshot_type const & saved)
208 : origin_(saved.origin_), stage_(saved.stage_), nursery_(nursery_type::thaw(saved.nursery_)), scope_(stage_->scope) {}
209 void ensure_core() {
210 if (core_) return;
211 if (!scope_) scope_ = scope_type::create(origin_->root);
212 if (!store_) store_ = std::make_unique<store_type>(store_type::open(origin_->root, {}, scope_->options()));
213 auto const & schema = stage_->world.metadata().schema_id;
214 if constexpr (requires { family_type::open_storage(origin_->root, schema, scope_->options()); })
215 core_ = std::make_unique<Core>(Core::from_snapshot(stage_->world,
216 family_type::open_storage(origin_->root, schema, scope_->options())));
217 else {
218 static_assert(!requires { family_type::open_storage(origin_->root); },
219 "streaming transaction storage must accept private catalog options");
220 core_ = std::make_unique<Core>(Core::from_snapshot(stage_->world));
221 }
222 }
223 void require_active() const {
224 if (closed_ || !origin_) throw std::logic_error("closed Everett transaction");
225 if (failed()) throw std::logic_error("failed Everett transaction; abort it");
226 }
227 void release() noexcept {
228 core_.reset(); store_.reset(); scope_.reset(); stage_.reset(); origin_.reset();
229 nursery_.reset();
230 }
232 };
233
234 template <class Core> auto connection<Core>::begin() -> transaction_type { return transaction_type(*this); }
235}
Opens durable named typed sessions with mutable commands and immutable saved worlds.
constexpr bool state_is_arrow
Definition transaction.h:19
typename sort_codec< S >::value_codec::value_type arrow_t
Definition typed_world.h:53
typename transport< P, Family >::type transport_t
Definition typed_world.h:152
typename sort_codec< S >::key_codec::value_type key_t
Definition typed_world.h:52
typename sort_semantics< S >::state_type state_t
Definition typed_world.h:54
Definition active_engine.h:18
Keeps ordered nursery edits behind fully persistent snapshots.
Definition profile.h:166
bit_view view() const &
Definition profile.h:178
Definition connection.h:294
runtime_store< policy_type, random_object_ids, sqlite_catalog_ops, typename Core::runtime_family > store_type
Definition connection.h:304
persistent_engine< Core > engine_type
Definition connection.h:297
typename session_type::ticket ticket
Definition connection.h:302
session< engine_type > session_type
Definition connection.h:298
typename engine_type::world_type world_type
Definition connection.h:299
Definition nursery_map.h:67
std::size_t size() const noexcept
Definition nursery_map.h:80
Value const * find(Key const &key) const
Definition nursery_map.h:79
Definition nursery_map.h:33
Definition private_construction.h:27
Definition session.h:46
Definition typed_world.h:31
Definition transaction.h:38
std::optional< bit_string > original
Definition transaction.h:40
bit_string arrow
Definition transaction.h:39
Definition transaction.h:47
std::weak_ptr< session_type > session
Definition transaction.h:49
std::uint64_t maintenance_budget
Definition transaction.h:51
std::filesystem::path root
Definition transaction.h:48
stored_type base
Definition transaction.h:50
Definition transaction.h:62
std::shared_ptr< stage const > stage_
Definition transaction.h:77
auto get(typed_detail::key_t< S > const &key) const
Definition transaction.h:63
std::size_t buffered_keys() const noexcept
Definition transaction.h:73
snapshot_type(std::shared_ptr< origin const > source, std::shared_ptr< stage const > base, typename nursery_type::snapshot_type values)
Definition transaction.h:79
nursery_type::snapshot_type nursery_
Definition transaction.h:78
friend transaction
Definition transaction.h:82
std::shared_ptr< origin const > origin_
Definition transaction.h:76
transaction branch() const
Definition transaction.h:74
Definition transaction.h:53
world_type world
Definition transaction.h:55
bool changed
Definition transaction.h:56
stage(world_type state, std::shared_ptr< scope_type > lease, bool modified)
Definition transaction.h:57
std::shared_ptr< scope_type > scope
Definition transaction.h:54
Definition transaction.h:21
bool operator()(bit_string const &a, bit_string const &b) const
Definition transaction.h:22
Definition transaction.h:28
bool failed_
Definition transaction.h:201
typename connection_type::engine_type engine_type
Definition transaction.h:34
typename Core::policy_type policy_type
Definition transaction.h:29
typename Core::world_type world_type
Definition transaction.h:32
transaction(snapshot_type const &saved)
Definition transaction.h:207
std::shared_ptr< origin const > origin_
Definition transaction.h:193
snapshot_type snapshot()
Definition transaction.h:90
std::shared_ptr< scope_type > scope_
Definition transaction.h:198
void erase(typed_detail::key_t< S > const &key)
Definition transaction.h:143
friend connection_type
Definition transaction.h:231
transaction(transaction const &)=delete
void ensure_core()
Definition transaction.h:209
bool closed_
Definition transaction.h:201
typename Core::runtime_family family_type
Definition transaction.h:30
transaction & operator=(transaction &&)=delete
transaction(connection_type &connection)
Definition transaction.h:203
void release() noexcept
Definition transaction.h:227
void change(typed_detail::key_t< S > const &key, typed_detail::arrow_t< S > const &arrow)
Definition transaction.h:105
typed_detail::transport_t< policy_type, family_type > transport
Definition transaction.h:31
typename connection_type::store_type store_type
Definition transaction.h:44
typename connection_type::session_type session_type
Definition transaction.h:35
std::shared_ptr< stage const > stage_
Definition transaction.h:194
auto get(typed_detail::key_t< S > const &key) const
Definition transaction.h:94
transaction & operator=(transaction const &)=delete
typename connection_type::ticket ticket
Definition transaction.h:37
typename connection_type::world_type stored_type
Definition transaction.h:36
stored_type commit()
Definition transaction.h:188
void abort() noexcept
Definition transaction.h:189
std::unique_ptr< store_type > store_
Definition transaction.h:199
ticket commit_async()
Definition transaction.h:172
transaction(transaction &&)=default
snapshot_type flush()
Definition transaction.h:148
void put(typed_detail::key_t< S > const &key, typed_detail::state_t< S > const &value)
Definition transaction.h:140
std::unique_ptr< Core > core_
Definition transaction.h:200
bool failed() const noexcept
Definition transaction.h:190
void require_active() const
Definition transaction.h:223
std::optional< nursery_type > nursery_
Definition transaction.h:195
Definition multiverse.h:41