Everett
Loading...
Searching...
No Matches
durability.h
Go to the documentation of this file.
1
13#pragma once
14
15#include <algorithm>
16#include <cstdint>
17#include <limits>
18#include <optional>
19#include <stdexcept>
20#include <string>
21#include <utility>
22#include <vector>
23
24namespace everett {
25 // Protocol events supplied by a backend, not wrappers around fsync's return
26 // value. durable_verified includes content verification and all required
27 // data, allocator, naming and recovery-root persistence for that operation.
30 // old_root_selected asserts that recovery durably selected the old root
31 // AND reconciled candidate reachability while retaining uncertain objects.
32 // It never grants reclamation of a failed output generation.
35
37 // Recipe includes comparator/codec versions, precedence and any context
38 // used to elide tombstones. Input order is semantically significant.
39 std::string recipe;
40 std::vector<std::string> input_versions;
41 bool operator==(merge_identity const &) const = default;
42 };
43
45 std::string input_version;
46 std::uint64_t ordinal = 0;
47 std::uint64_t byte_offset = 0;
48 // This first checkpoint codec pauses at record boundaries and saves the
49 // whole predecessor, so ordinary front coding can restart independently.
50 std::string predecessor_key;
51 bool operator==(merge_input_cursor const &) const = default;
52 };
53
55 std::string exact_id;
56 std::uint64_t bytes = 0;
57 // Opaque identity of a byte-integrity digest, separate from the algebraic
58 // world signature. The backend verifies the named bytes and their length.
59 std::string checksum;
60 bool operator==(sealed_merge_extent const &) const = default;
61 };
62
64 std::string exact_id;
66 std::string output_generation;
67 std::vector<merge_input_cursor> inputs;
68 std::vector<sealed_merge_extent> sealed_output;
70 // Versioned serialized continuation of rank and Elias–Fano builders, partial
71 // group counts/offsets, merge selection and accumulator state. This model
72 // treats the bytes as opaque; the eventual codec validates them on load.
73 std::string metadata_state;
74 std::uint64_t output_records = 0;
75 std::uint64_t completed_work = 0;
76 bool operator==(merge_checkpoint const &) const = default;
77
78 std::uint64_t sealed_bytes() const {
79 std::uint64_t sum = 0;
80 for (auto const & extent : sealed_output) {
81 if (extent.bytes > std::numeric_limits<std::uint64_t>::max() - sum)
82 throw std::overflow_error("merge checkpoint size");
83 sum += extent.bytes;
84 }
85 return sum;
86 }
87 };
88
89 // Executable protocol model only: no files, flushing, checksumming, durable
90 // pin implementation, or thread synchronization occurs here. A backend
91 // owns those operations and supplies truthful completion/recovery events.
92 // One serialized publication owner uses the state machine for one recipe.
94 merge_publication(merge_identity identity, std::string old_manifest,
95 std::string output_generation)
96 : identity_(std::move(identity)), durable_manifest_(std::move(old_manifest)),
97 generation_(std::move(output_generation)) {
98 if (identity_.recipe.empty() || identity_.input_versions.empty() ||
99 durable_manifest_.empty() || generation_.empty())
100 throw std::invalid_argument("missing merge publication identity");
101 for (std::size_t i = 0; i < identity_.input_versions.size(); ++i) {
102 auto const & id = identity_.input_versions[i];
103 if (id.empty()) throw std::invalid_argument("missing merge input identity");
104 for (std::size_t j = 0; j < i; ++j)
105 if (identity_.input_versions[j] == id)
106 throw std::invalid_argument("duplicate merge input identity");
107 }
108 generations_.push_back(generation_);
109 }
110
111 publication_stage stage() const noexcept { return stage_; }
112 std::string const & durable_manifest() const noexcept { return durable_manifest_; }
113 std::string const & output_generation() const noexcept { return generation_; }
114 std::string const & pending_manifest() const noexcept { return pending_manifest_; }
115 std::optional<merge_checkpoint> const & checkpoint() const noexcept { return checkpoint_; }
116 bool old_pins_retained() const noexcept { return old_pins_retained_; }
117 bool can_release_old_pins() const noexcept { return stage_ == publication_stage::published; }
118
119 // A failed checkpoint write never supersedes the previous durable one.
122 validate_checkpoint(candidate);
123 if (candidate.output_generation != generation_)
124 throw std::invalid_argument("checkpoint generation mismatch");
125 if (checkpoint_) {
126 if (candidate.exact_id == checkpoint_->exact_id ||
127 candidate.output_records < checkpoint_->output_records ||
128 candidate.completed_work < checkpoint_->completed_work)
129 throw std::invalid_argument("checkpoint did not advance");
130 for (std::size_t i = 0; i < candidate.inputs.size(); ++i)
131 if (candidate.inputs[i].ordinal < checkpoint_->inputs[i].ordinal ||
132 candidate.inputs[i].byte_offset < checkpoint_->inputs[i].byte_offset)
133 throw std::invalid_argument("checkpoint cursor moved backwards");
134 }
136 checkpoint_ = std::move(candidate);
137 }
138
144
145 void prepare_manifest(std::string exact_id) {
147 if (exact_id.empty() || exact_id == durable_manifest_ ||
148 std::find(manifests_.begin(), manifests_.end(), exact_id) != manifests_.end())
149 throw std::invalid_argument("manifest identity must be fresh");
150 manifests_.push_back(exact_id);
151 pending_manifest_ = std::move(exact_id);
153 }
154
161
162 // Includes write, flush, verification, directory or allocator failures
163 // before publication. A transient-looking error does not relax retention.
166 throw std::logic_error("published merge requires separate cleanup handling");
168 }
169
170 // Fresh output storage is mandatory. Optional retained checkpoint must be
171 // the exact last successful checkpoint and must have been independently
172 // revalidated, together with its inputs and every sealed extent, by the
173 // backend. Without it, regenerate from the original pinned inputs. If a
174 // manifest was attempted, old-root selection must additionally be made
175 // durable and all candidate reachability reconciled before restarting.
176 void resume(std::string fresh_generation, recovery_evidence evidence,
177 std::optional<merge_checkpoint> retained = std::nullopt,
180 require_evidence(evidence);
182 throw std::invalid_argument("uncertain manifest selector needs reconciliation");
183 if (fresh_generation.empty() ||
184 std::find(generations_.begin(), generations_.end(), fresh_generation) != generations_.end())
185 throw std::invalid_argument("merge resumption needs a fresh generation");
186 if (retained && (!checkpoint_ || *retained != *checkpoint_))
187 throw std::invalid_argument("unrecognized recovery checkpoint");
188 generations_.push_back(fresh_generation);
189 generation_ = std::move(fresh_generation);
190 checkpoint_ = std::move(retained);
191 pending_manifest_.clear();
193 }
194
195 // A failed manifest flush has an uncertain outcome, not a guaranteed
196 // rollback. Accept it only when recovery independently verifies the
197 // candidate root AND its complete immutable dependency graph as durable.
200 require_evidence(evidence);
201 if (pending_manifest_.empty()) throw std::logic_error("no manifest to reconcile");
204 }
205
206 // This only grants/releases the model's merge-owned durable retention.
207 // Other snapshots, readers, checkpoints and index dependencies keep pins.
208 // The backend persists retirement before reclaiming bytes or extents.
211 return std::exchange(old_pins_retained_, false);
212 }
213
214 private:
215 void require_stage(publication_stage wanted) const {
216 if (stage_ != wanted) throw std::logic_error("invalid merge publication transition");
217 }
218 static void require_evidence(recovery_evidence evidence) {
220 throw std::invalid_argument("durable recovery evidence required");
221 }
222 void validate_checkpoint(merge_checkpoint const & candidate) const {
223 if (candidate.exact_id.empty() || candidate.identity != identity_ ||
224 candidate.inputs.size() != identity_.input_versions.size())
225 throw std::invalid_argument("checkpoint identity mismatch");
226 for (std::size_t i = 0; i < candidate.inputs.size(); ++i)
227 if (candidate.inputs[i].input_version != identity_.input_versions[i])
228 throw std::invalid_argument("checkpoint input version mismatch");
229 for (std::size_t i = 0; i < candidate.sealed_output.size(); ++i) {
230 auto const & extent = candidate.sealed_output[i];
231 if (extent.exact_id.empty() || extent.checksum.empty())
232 throw std::invalid_argument("checkpoint extent identity missing");
233 for (std::size_t j = 0; j < i; ++j)
234 if (candidate.sealed_output[j].exact_id == extent.exact_id)
235 throw std::invalid_argument("duplicate checkpoint extent");
236 }
237 (void)candidate.sealed_bytes();
238 }
239
241 std::string durable_manifest_;
242 std::string generation_;
243 std::string pending_manifest_;
244 std::vector<std::string> generations_;
245 std::vector<std::string> manifests_;
246 std::optional<merge_checkpoint> checkpoint_;
249 };
250}
Definition active_engine.h:18
recovery_evidence
Definition durability.h:29
publication_stage
Definition durability.h:34
persistence_result
Definition durability.h:28
selector_recovery
Definition durability.h:33
Definition durability.h:63
std::vector< merge_input_cursor > inputs
Definition durability.h:67
std::uint64_t sealed_bytes() const
Definition durability.h:78
std::string previous_output_key
Definition durability.h:69
std::string output_generation
Definition durability.h:66
bool operator==(merge_checkpoint const &) const =default
std::vector< sealed_merge_extent > sealed_output
Definition durability.h:68
std::uint64_t completed_work
Definition durability.h:75
merge_identity identity
Definition durability.h:65
std::uint64_t output_records
Definition durability.h:74
std::string metadata_state
Definition durability.h:73
std::string exact_id
Definition durability.h:64
Definition durability.h:36
std::string recipe
Definition durability.h:39
std::vector< std::string > input_versions
Definition durability.h:40
bool operator==(merge_identity const &) const =default
Definition durability.h:44
std::string input_version
Definition durability.h:45
std::uint64_t ordinal
Definition durability.h:46
bool operator==(merge_input_cursor const &) const =default
std::uint64_t byte_offset
Definition durability.h:47
std::string predecessor_key
Definition durability.h:50
Definition durability.h:93
void accept_recovered_publication(recovery_evidence evidence)
Definition durability.h:198
bool release_old_pins()
Definition durability.h:209
void prepare_manifest(std::string exact_id)
Definition durability.h:145
bool old_pins_retained() const noexcept
Definition durability.h:116
std::string const & durable_manifest() const noexcept
Definition durability.h:112
bool old_pins_retained_
Definition durability.h:248
bool can_release_old_pins() const noexcept
Definition durability.h:117
static void require_evidence(recovery_evidence evidence)
Definition durability.h:218
void complete_manifest(persistence_result result)
Definition durability.h:155
std::optional< merge_checkpoint > checkpoint_
Definition durability.h:246
void require_stage(publication_stage wanted) const
Definition durability.h:215
merge_identity identity_
Definition durability.h:240
publication_stage stage_
Definition durability.h:247
void resume(std::string fresh_generation, recovery_evidence evidence, std::optional< merge_checkpoint > retained=std::nullopt, selector_recovery selector=selector_recovery::unresolved)
Definition durability.h:176
publication_stage stage() const noexcept
Definition durability.h:111
std::vector< std::string > generations_
Definition durability.h:244
std::string generation_
Definition durability.h:242
std::vector< std::string > manifests_
Definition durability.h:245
std::string const & pending_manifest() const noexcept
Definition durability.h:114
std::string durable_manifest_
Definition durability.h:241
void report_io_failure()
Definition durability.h:164
std::string const & output_generation() const noexcept
Definition durability.h:113
void complete_output(persistence_result result)
Definition durability.h:139
void validate_checkpoint(merge_checkpoint const &candidate) const
Definition durability.h:222
std::string pending_manifest_
Definition durability.h:243
merge_publication(merge_identity identity, std::string old_manifest, std::string output_generation)
Definition durability.h:94
std::optional< merge_checkpoint > const & checkpoint() const noexcept
Definition durability.h:115
void complete_checkpoint(merge_checkpoint candidate, persistence_result result)
Definition durability.h:120
Definition durability.h:54
std::string exact_id
Definition durability.h:55
std::string checksum
Definition durability.h:59
std::uint64_t bytes
Definition durability.h:56
bool operator==(sealed_merge_extent const &) const =default