1use std::time::Duration;
4
5use ozzy_proto::NodeId;
6
7use super::{Action, DriverError, ReplicaDriver, Role};
8use crate::wire::Control;
9use crate::{
10 Commit, Digest, InstallOutcome, InstallTicket, InstallingView, JournalGeneration, LogSource,
11 OpNumber, Prefix, PreparedOperation, ReplicationError, Scope, StartView,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct ActivationTicket {
18 local: NodeId,
19 scope: Scope,
20 generation: JournalGeneration,
21 through: Prefix,
22 applied: Prefix,
23}
24
25impl ActivationTicket {
26 pub const fn local(self) -> NodeId {
28 self.local
29 }
30 pub const fn scope(self) -> Scope {
32 self.scope
33 }
34 pub const fn generation(self) -> JournalGeneration {
36 self.generation
37 }
38 pub const fn through(self) -> Prefix {
40 self.through
41 }
42 pub const fn applied(self) -> Prefix {
44 self.applied
45 }
46}
47
48impl ReplicaDriver {
49 pub(super) fn activation_action(&mut self) -> Result<Option<Action>, DriverError> {
50 if self.activation_pending == 0 {
51 return Ok(None);
52 }
53 let start = self
54 .normal()
55 .expect("normal role")
56 .start_view()?
57 .expect("activation response requires an installed view");
58 let voter = self.activation_pending.trailing_zeros() as usize;
59 self.activation_pending &= !(1 << voter);
60 Ok(Some(Action::Send {
61 to: self.configuration.voters()[voter],
62 message: Control::Commit(Commit {
63 scope: start.scope,
64 committed: start.accepted,
65 }),
66 }))
67 }
68
69 pub fn begin_activation(&self) -> Result<ActivationTicket, DriverError> {
73 let normal = self.normal().ok_or(ReplicationError::NotNormal)?;
74 let through = normal.activation_prefix()?;
75 let snapshot = normal.snapshot();
76 Ok(ActivationTicket {
77 local: self.local,
78 scope: snapshot.scope,
79 generation: snapshot.journal.generation,
80 through,
81 applied: snapshot.applied,
82 })
83 }
84
85 pub fn complete_activation(&mut self, ticket: ActivationTicket) -> Result<(), DriverError> {
88 if self.begin_activation().ok() != Some(ticket) {
89 return Err(DriverError::StaleValidation);
90 }
91 self.apply_through(ticket.through)
92 }
93
94 pub fn installation_ticket(&self) -> Option<InstallTicket> {
96 match self.role.as_ref().expect("owned role") {
97 Role::Installing(installing) => Some(installing.ticket()),
98 Role::Normal(_) | Role::ViewChanging(_) => None,
99 }
100 }
101
102 pub fn begin_primary_install(
107 &mut self,
108 generation: JournalGeneration,
109 ) -> Result<InstallTicket, DriverError> {
110 self.begin_install(None, generation, |_, _| None)
111 }
112
113 pub fn begin_backup_install(
117 &mut self,
118 from: NodeId,
119 start: StartView,
120 generation: JournalGeneration,
121 lookup: impl FnMut(LogSource, OpNumber) -> Option<Digest>,
122 ) -> Result<InstallTicket, DriverError> {
123 self.begin_install(Some((from, start)), generation, lookup)
124 }
125
126 fn begin_install(
127 &mut self,
128 backup: Option<(NodeId, StartView)>,
129 generation: JournalGeneration,
130 lookup: impl FnMut(LogSource, OpNumber) -> Option<Digest>,
131 ) -> Result<InstallTicket, DriverError> {
132 self.changing()?;
133 let Role::ViewChanging(changing) = self.role.take().expect("owned role") else {
134 unreachable!("view-changing checked");
135 };
136 let admitted = match backup {
137 Some((from, start)) => changing.begin_backup_install(from, start, generation, lookup),
138 None => changing.begin_primary_install(generation),
139 };
140 match admitted {
141 Ok(installing) => {
142 let ticket = installing.ticket();
143 self.role = Some(Role::Installing(installing));
144 self.election_deadline = self.now + self.election_delay;
145 Ok(ticket)
146 }
147 Err(rejected) => {
148 let error = rejected.error();
149 self.role = Some(Role::ViewChanging(rejected.into_view_change()));
150 Err(error.into())
151 }
152 }
153 }
154
155 pub fn validate_install_suffix(
159 &mut self,
160 operations: &[PreparedOperation],
161 ) -> Result<(), DriverError> {
162 Ok(self.installing_mut()?.validate_suffix(operations)?)
163 }
164
165 pub fn complete_installation(
170 &mut self,
171 ticket: InstallTicket,
172 applied: Prefix,
173 now: Duration,
174 ) -> Result<(), DriverError> {
175 self.observe_time(now)?;
176 let outcome = self.installing_mut()?.complete(ticket, applied)?;
177 let changing = matches!(outcome, InstallOutcome::ViewChanging(_));
178 self.role = Some(match outcome {
179 InstallOutcome::Normal(normal) => Role::Normal(normal),
180 InstallOutcome::ViewChanging(changing) => Role::ViewChanging(changing),
181 });
182 self.contact_deadline = now + self.timing.primary_timeout;
183 self.persistence_deadline = now + self.timing.primary_timeout;
184 self.progress_deadline = now + self.progress_timeout();
185 self.election_deadline = now + self.election_delay;
186 self.retry_at = now;
187 self.start_pending = changing;
188 self.report_pending = changing;
189 self.commit_pending = false;
190 self.activation_pending = 0;
191 Ok(())
192 }
193
194 pub fn complete_abandon(
199 &mut self,
200 ticket: InstallTicket,
201 now: Duration,
202 ) -> Result<(), DriverError> {
203 self.observe_time(now)?;
204 let changing = self.installing_mut()?.complete_abandon(ticket)?;
205 self.role = Some(Role::ViewChanging(changing));
206 self.election_deadline = now + self.election_delay;
207 self.retry_at = now;
208 self.start_pending = true;
209 self.report_pending = true;
210 self.commit_pending = false;
211 self.activation_pending = 0;
212 Ok(())
213 }
214
215 fn installing_mut(&mut self) -> Result<&mut InstallingView, DriverError> {
216 match self.role.as_mut().expect("owned role") {
217 Role::Installing(installing) => Ok(installing),
218 Role::Normal(_) | Role::ViewChanging(_) => Err(ReplicationError::WrongRole.into()),
219 }
220 }
221}