1mod error;
4mod recovery;
5mod selection;
6
7pub use error::ViewChangeError;
8
9use ozzy_proto::NodeId;
10
11use crate::{
12 Configuration, Digest, JournalGeneration, NormalReplica, OpNumber, Prefix, ReplicaSnapshot,
13 ReplicationError, Scope, Status, SyncTicket, VoterIndex, VoterSet, Voters, WriteTicket,
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct FrozenLog {
23 pub last_normal_view: u64,
25 pub accepted: Prefix,
27 pub committed: Prefix,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub struct RecoveredState {
39 pub scope: Scope,
41 pub log: FrozenLog,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub struct PromiseTicket {
53 scope: Scope,
54 generation: JournalGeneration,
55 log: FrozenLog,
56}
57
58impl PromiseTicket {
59 pub const fn scope(self) -> Scope {
61 self.scope
62 }
63 pub const fn generation(self) -> JournalGeneration {
65 self.generation
66 }
67 pub const fn log(self) -> FrozenLog {
69 self.log
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub struct StartViewChange {
76 pub scope: Scope,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct DoViewChange {
83 pub scope: Scope,
85 pub generation: JournalGeneration,
87 pub log: FrozenLog,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub struct LogSource {
94 pub voter: NodeId,
96 pub generation: JournalGeneration,
98 pub accepted: Prefix,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub struct SelectedView {
108 scope: Scope,
109 source: LogSource,
110 committed: Prefix,
111 retained: Prefix,
112 voters: VoterSet,
113}
114
115impl SelectedView {
116 pub const fn scope(self) -> Scope {
118 self.scope
119 }
120 pub const fn source(self) -> LogSource {
122 self.source
123 }
124 pub const fn committed(self) -> Prefix {
126 self.committed
127 }
128 pub(crate) const fn retained(self) -> Prefix {
131 self.retained
132 }
133 pub const fn voter_mask(self) -> u8 {
135 self.voters.mask()
136 }
137}
138
139#[derive(Debug)]
147pub struct ViewChange {
148 pub(crate) normal: NormalReplica,
149 pub(crate) configuration: Configuration,
150 pub(crate) local: VoterIndex,
151 view: u64,
152 promise: Promise,
153 pending: Option<PromiseTicket>,
154 starts: VoterSet,
155 reports: Voters<Option<DoViewChange>>,
156 pub(crate) selected: Option<SelectedView>,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161enum Promise {
162 Prior(u64),
165 Completed(PromiseTicket),
168}
169
170impl Promise {
171 const fn view(self) -> u64 {
172 match self {
173 Self::Prior(view) => view,
174 Self::Completed(ticket) => ticket.scope.view,
175 }
176 }
177}
178
179impl ViewChange {
180 pub fn history_sources(&self) -> [Option<LogSource>; 3] {
183 VoterIndex::ALL.map(|voter| {
184 self.reports[voter].map(|report| LogSource {
185 voter: self.configuration.voter(voter),
186 generation: report.generation,
187 accepted: report.log.accepted,
188 })
189 })
190 }
191
192 pub fn lookup_ops(&self) -> [Option<OpNumber>; 8] {
195 let local = self.normal.snapshot();
196 let mut ops = [None; 8];
197 ops[6] = Some(local.committed.op);
198 ops[7] = Some(local.accepted.op);
199 let (slots, _) = ops.as_chunks_mut::<2>();
200 for ([committed, accepted], report) in slots.iter_mut().zip(self.reports.values()) {
201 if let Some(report) = report {
202 *committed = Some(report.log.committed.op);
203 *accepted = Some(report.log.accepted.op);
204 }
205 }
206 ops
207 }
208
209 pub(crate) fn begin(
210 mut normal: NormalReplica,
211 configuration: Configuration,
212 local: VoterIndex,
213 view: u64,
214 ) -> Result<Self, ViewChangeError> {
215 normal.fence();
216 if normal.snapshot().status == Status::Faulted {
217 return Err(ViewChangeError::Faulted);
218 }
219 if view <= configuration.scope().view {
220 return Err(ViewChangeError::ViewNotHigher);
221 }
222 Ok(Self {
223 normal,
224 configuration,
225 local,
226 view,
227 promise: Promise::Prior(configuration.scope().view),
228 pending: None,
229 starts: VoterSet::EMPTY,
230 reports: Voters::splat(None),
231 selected: None,
232 })
233 }
234
235 pub fn normal_snapshot(&self) -> ReplicaSnapshot {
237 self.normal.snapshot()
238 }
239
240 pub fn scope(&self) -> Scope {
242 Scope {
243 view: self.view,
244 ..self.configuration.scope()
245 }
246 }
247
248 pub const fn promised_view(&self) -> u64 {
250 self.promise.view()
251 }
252
253 pub fn advance_view(&mut self, view: u64) -> Result<(), ViewChangeError> {
259 self.require_active()?;
260 if view <= self.view {
261 return Err(ViewChangeError::ViewNotHigher);
262 }
263 self.view = view;
264 self.starts = VoterSet::EMPTY;
265 self.reports = Voters::splat(None);
266 self.selected = None;
267 Ok(())
268 }
269
270 pub fn complete_write(&mut self, ticket: WriteTicket) -> Result<(), ViewChangeError> {
272 Ok(self.normal.complete_write(ticket)?)
273 }
274
275 pub fn begin_sync(&self) -> Result<SyncTicket, ViewChangeError> {
277 Ok(self.normal.begin_sync()?)
278 }
279
280 pub fn complete_sync(&mut self, ticket: SyncTicket) -> Result<(), ViewChangeError> {
282 Ok(self.normal.complete_sync(ticket)?)
283 }
284
285 pub fn fail_io(&mut self, generation: JournalGeneration) -> Result<(), ViewChangeError> {
287 Ok(self.normal.fail_io(generation)?)
288 }
289
290 pub fn begin_promise(&mut self) -> Result<PromiseTicket, ViewChangeError> {
295 self.require_active()?;
296 if self.pending.is_some() {
297 return Err(ViewChangeError::PromisePending);
298 }
299 if self.promise.view() == self.view {
300 return Err(ViewChangeError::AlreadyPromised);
301 }
302 let snapshot = self.normal.snapshot();
303 if snapshot.journal.durable != snapshot.accepted.op {
304 return Err(ViewChangeError::StoragePending);
305 }
306 let ticket = PromiseTicket {
307 scope: self.scope(),
308 generation: snapshot.journal.generation,
309 log: FrozenLog {
310 last_normal_view: snapshot.scope.view,
311 accepted: snapshot.accepted,
312 committed: snapshot.committed,
313 },
314 };
315 self.pending = Some(ticket);
316 Ok(ticket)
317 }
318
319 pub fn complete_promise(&mut self, ticket: PromiseTicket) -> Result<(), ViewChangeError> {
321 self.require_active()?;
322 if self.promise == Promise::Completed(ticket) {
323 return Ok(());
324 }
325 self.require_ticket(ticket)?;
326 self.pending = None;
327 self.promise = Promise::Completed(ticket);
328 if ticket.scope.view == self.view {
329 self.starts.insert(self.local);
330 }
331 Ok(())
332 }
333
334 pub fn fail_promise(&mut self, ticket: PromiseTicket) -> Result<(), ViewChangeError> {
336 self.require_active()?;
337 self.require_ticket(ticket)?;
338 self.fail_io(ticket.generation)
339 }
340
341 pub fn start_message(&self) -> Result<StartViewChange, ViewChangeError> {
343 self.require_promised()?;
344 Ok(StartViewChange {
345 scope: self.scope(),
346 })
347 }
348
349 pub fn receive_start(
352 &mut self,
353 from: NodeId,
354 message: StartViewChange,
355 ) -> Result<(), ViewChangeError> {
356 self.require_scope(message.scope)?;
357 let voter = self.configuration.voter_index(from)?;
358 if voter == self.local {
359 return Err(ReplicationError::WrongRole.into());
360 }
361 self.starts.insert(voter);
362 Ok(())
363 }
364
365 pub fn report(&mut self) -> Result<DoViewChange, ViewChangeError> {
368 let promise = self.require_promised()?;
369 if self.starts.len() < 2 {
370 return Err(ViewChangeError::StartQuorumMissing);
371 }
372 let report = DoViewChange {
373 scope: self.scope(),
374 generation: promise.generation,
375 log: promise.log,
376 };
377 self.reports[self.local] = Some(report);
378 Ok(report)
379 }
380
381 pub fn receive_report(
389 &mut self,
390 from: NodeId,
391 report: DoViewChange,
392 ) -> Result<(), ViewChangeError> {
393 self.require_scope(report.scope)?;
394 self.require_primary()?;
395 let voter = self.configuration.voter_index(from)?;
396 if voter == self.local {
397 return Err(ReplicationError::WrongRole.into());
398 }
399 validate_report(report)?;
400 if let Some(previous) = self.reports[voter] {
401 if previous.log != report.log {
402 return Err(self.conflict());
403 }
404 return Ok(());
409 }
410 if self.selected.is_some() {
411 return Err(ViewChangeError::SelectionFrozen);
412 }
413 self.reports[voter] = Some(report);
414 Ok(())
415 }
416
417 fn require_active(&self) -> Result<(), ViewChangeError> {
418 if self.normal.snapshot().status == Status::Faulted {
419 return Err(ViewChangeError::Faulted);
420 }
421 Ok(())
422 }
423
424 pub(crate) fn require_scope(&self, scope: Scope) -> Result<(), ViewChangeError> {
425 self.require_active()?;
426 if scope != self.scope() {
427 return Err(ReplicationError::ScopeMismatch.into());
428 }
429 Ok(())
430 }
431
432 pub(crate) fn require_primary(&self) -> Result<(), ViewChangeError> {
433 if VoterIndex::primary(self.view) != self.local {
434 return Err(ReplicationError::WrongRole.into());
435 }
436 Ok(())
437 }
438
439 pub(crate) fn require_promised(&self) -> Result<PromiseTicket, ViewChangeError> {
441 self.require_active()?;
442 match self.promise {
443 Promise::Completed(ticket) if ticket.scope.view == self.view => Ok(ticket),
444 Promise::Prior(_) | Promise::Completed(_) => Err(ViewChangeError::PromiseRequired),
445 }
446 }
447
448 fn require_ticket(&self, ticket: PromiseTicket) -> Result<(), ViewChangeError> {
449 if self.pending != Some(ticket) {
450 return Err(ViewChangeError::StalePromise);
451 }
452 Ok(())
453 }
454
455 pub(crate) fn conflict(&mut self) -> ViewChangeError {
456 self.normal.fault();
457 ViewChangeError::ConflictingHistory
458 }
459}
460
461pub(crate) fn valid_prefix(prefix: Prefix) -> bool {
462 prefix.op.0 != u64::MAX && ((prefix.op.0 == 0) == (prefix.digest == Digest::ZERO))
463}
464
465fn validate_report(report: DoViewChange) -> Result<(), ViewChangeError> {
466 let log = report.log;
467 if log.last_normal_view >= report.scope.view
468 || !valid_prefix(log.accepted)
469 || !valid_prefix(log.committed)
470 || log.committed.op > log.accepted.op
471 || (log.committed.op == log.accepted.op && log.committed != log.accepted)
472 {
473 return Err(ViewChangeError::InvalidReport);
474 }
475 Ok(())
476}