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 Prefix, PreparedOperation, ReceivedStart, ReplicationError, Scope, StartView, ViewChangeError,
12 VoterSet,
13};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct ActivationTicket {
19 local: NodeId,
20 scope: Scope,
21 generation: JournalGeneration,
22 through: Prefix,
23 applied: Prefix,
24}
25
26impl ActivationTicket {
27 pub const fn local(self) -> NodeId {
29 self.local
30 }
31 pub const fn scope(self) -> Scope {
33 self.scope
34 }
35 pub const fn generation(self) -> JournalGeneration {
37 self.generation
38 }
39 pub const fn through(self) -> Prefix {
41 self.through
42 }
43 pub const fn applied(self) -> Prefix {
45 self.applied
46 }
47}
48
49impl ReplicaDriver {
50 pub(super) fn activation_action(&mut self) -> Result<Option<Action>, DriverError> {
53 if self.activation_pending.is_empty() {
54 return Ok(None);
55 }
56 let Some(start) = self.require_normal()?.start_view()? else {
57 self.activation_pending = VoterSet::EMPTY;
58 return Ok(None);
59 };
60 let Some(voter) = self.activation_pending.pop_first() else {
61 return Ok(None);
62 };
63 Ok(Some(Action::Send {
64 to: self.configuration.voter(voter),
65 message: Control::Commit(Commit {
66 scope: start.scope,
67 committed: start.accepted,
68 }),
69 }))
70 }
71
72 pub fn begin_activation(&self) -> Result<ActivationTicket, DriverError> {
76 let normal = self.normal().ok_or(ReplicationError::NotNormal)?;
77 let through = normal.activation_prefix()?;
78 let snapshot = normal.snapshot();
79 Ok(ActivationTicket {
80 local: self.configuration.voter(self.local),
81 scope: snapshot.scope,
82 generation: snapshot.journal.generation,
83 through,
84 applied: snapshot.applied,
85 })
86 }
87
88 pub fn complete_activation(&mut self, ticket: ActivationTicket) -> Result<(), DriverError> {
91 if self.begin_activation().ok() != Some(ticket) {
92 return Err(DriverError::StaleValidation);
93 }
94 self.apply_through(ticket.through)
95 }
96
97 pub fn installation_ticket(&self) -> Option<InstallTicket> {
99 match &self.role {
100 Role::Installing(installing) => Some(installing.ticket()),
101 Role::Normal(_) | Role::ViewChanging(_) | Role::Fenced(_) => None,
102 }
103 }
104
105 pub fn begin_primary_install(
110 &mut self,
111 generation: JournalGeneration,
112 ) -> Result<InstallTicket, DriverError> {
113 self.begin_install(None, generation, |_, _| None)
114 }
115
116 pub fn begin_backup_install(
120 &mut self,
121 from: NodeId,
122 start: StartView,
123 generation: JournalGeneration,
124 lookup: impl FnMut(LogSource, Prefix) -> Option<Digest>,
125 ) -> Result<InstallTicket, DriverError> {
126 self.begin_install(Some(ReceivedStart { from, start }), generation, lookup)
127 }
128
129 fn begin_install(
130 &mut self,
131 backup: Option<ReceivedStart>,
132 generation: JournalGeneration,
133 lookup: impl FnMut(LogSource, Prefix) -> Option<Digest>,
134 ) -> Result<InstallTicket, DriverError> {
135 self.changing()?;
136 let Role::ViewChanging(changing) = self.take_role() else {
137 return Err(ViewChangeError::Faulted.into());
138 };
139 let admitted = match backup {
140 Some(received) => changing.begin_backup_install(received, generation, lookup),
141 None => changing.begin_primary_install(generation),
142 };
143 match admitted {
144 Ok(installing) => {
145 let ticket = installing.ticket();
146 self.role = Role::Installing(installing);
147 self.election_deadline = self.now.saturating_add(self.election_delay);
148 Ok(ticket)
149 }
150 Err(rejected) => {
151 let error = rejected.error();
152 self.role = Role::ViewChanging(rejected.into_view_change());
153 Err(error.into())
154 }
155 }
156 }
157
158 pub fn validate_install_suffix(
162 &mut self,
163 operations: &[PreparedOperation],
164 ) -> Result<(), DriverError> {
165 Ok(self.installing_mut()?.validate_suffix(operations)?)
166 }
167
168 pub fn history_progress(&mut self, now: Duration) -> Result<(), DriverError> {
177 self.observe_time(now)?;
178 if matches!(self.role, Role::ViewChanging(_) | Role::Installing(_)) {
179 self.election_deadline = self
180 .election_deadline
181 .max(now.saturating_add(self.election_delay));
182 }
183 Ok(())
184 }
185
186 pub fn complete_installation(
191 &mut self,
192 ticket: InstallTicket,
193 applied: Prefix,
194 now: Duration,
195 ) -> Result<(), DriverError> {
196 self.observe_time(now)?;
197 let outcome = self.installing_mut()?.complete(ticket, applied)?;
198 let changing = matches!(outcome, InstallOutcome::ViewChanging(_));
199 self.role = match outcome {
200 InstallOutcome::Normal(normal) => Role::Normal(normal),
201 InstallOutcome::ViewChanging(changing) => Role::ViewChanging(changing),
202 };
203 self.contact_deadline = now.saturating_add(self.timing.primary_timeout);
204 self.persistence_deadline = now.saturating_add(self.timing.primary_timeout);
205 self.progress_deadline = now.saturating_add(self.progress_timeout());
206 self.election_deadline = now.saturating_add(self.election_delay);
207 self.retry_at = now;
208 self.start_pending = changing;
209 self.report_pending = changing;
210 self.commit_pending = false;
211 self.activation_pending = VoterSet::EMPTY;
212 Ok(())
213 }
214
215 pub fn complete_abandon(
220 &mut self,
221 ticket: InstallTicket,
222 now: Duration,
223 ) -> Result<(), DriverError> {
224 self.observe_time(now)?;
225 let changing = self.installing_mut()?.complete_abandon(ticket)?;
226 self.role = Role::ViewChanging(changing);
227 self.election_deadline = now.saturating_add(self.election_delay);
228 self.retry_at = now;
229 self.start_pending = true;
230 self.report_pending = true;
231 self.commit_pending = false;
232 self.activation_pending = VoterSet::EMPTY;
233 Ok(())
234 }
235
236 fn installing_mut(&mut self) -> Result<&mut InstallingView, DriverError> {
237 match &mut self.role {
238 Role::Installing(installing) => Ok(installing),
239 Role::Normal(_) | Role::ViewChanging(_) => Err(ReplicationError::WrongRole.into()),
240 Role::Fenced(_) => Err(ViewChangeError::Faulted.into()),
241 }
242 }
243}