ozzy_replication/driver/
exit_view.rs1use super::{Action, DriverError, ReplicaDriver, Role};
4use crate::wire::Control;
5use crate::{Scope, StartViewChange, VoterIndex, VoterSet};
6use ozzy_proto::NodeId;
7use std::time::Duration;
8
9impl ReplicaDriver {
10 pub(super) fn receive_exit(
11 &mut self,
12 voter: VoterIndex,
13 scope: Scope,
14 ) -> Result<(), DriverError> {
15 self.require_healthy()?;
16 if scope.view < self.scope().view {
19 if self.now < self.exit_echo_at[voter] {
29 return Ok(());
30 }
31 if self.promise_pending() {
32 self.exit_echo_at[voter] = self.now.saturating_add(self.timing.retransmit);
33 let mut asked = match self.exit_echo {
34 Some((old, asked)) if old == scope => asked,
35 _ => VoterSet::EMPTY,
36 };
37 asked.insert(voter);
38 self.exit_echo = Some((scope, asked));
39 } else if matches!(self.role, Role::Normal(_) | Role::Installing(_)) {
40 self.exit_echo_at[voter] = self.now.saturating_add(self.timing.retransmit);
47 self.fence_answers.insert(voter);
48 }
49 return Ok(());
50 }
51 if scope.view > self.scope().view {
52 if matches!(self.role, Role::Normal(_)) && self.now >= self.exit_echo_at[voter] {
59 self.exit_echo_at[voter] = self.now.saturating_add(self.timing.retransmit);
60 self.stale_answers.insert(voter);
61 }
62 return Ok(());
63 }
64 if scope != self.scope() {
65 return Ok(());
66 }
67 if let Some(normal) = self.normal() {
68 let snapshot = normal.snapshot();
69 if !self.normal_timed_out(&snapshot) {
70 self.withdraw_exit();
71 }
72 }
73 self.exit_heard_at[voter] = self.now;
74 self.exit_votes.insert(voter);
75 self.advance_if_exit_quorum()
76 }
77
78 pub fn observe_recovering(&mut self, from: NodeId, now: Duration) -> Result<(), DriverError> {
83 self.observe_time(now)?;
84 self.require_healthy()?;
85 let voter = self.configuration.voter_index(from)?;
86 if voter != self.local
87 && voter == VoterIndex::primary(self.scope().view)
88 && matches!(self.role, Role::ViewChanging(_))
89 {
90 self.request_exit()?;
91 }
92 Ok(())
93 }
94
95 pub(super) fn request_exit(&mut self) -> Result<(), DriverError> {
96 self.exit_retry_at.get_or_insert(self.now);
97 self.exit_votes.insert(self.local);
98 self.advance_if_exit_quorum()
99 }
100
101 fn advance_if_exit_quorum(&mut self) -> Result<(), DriverError> {
102 for voter in self.local.others() {
106 let heard = self.exit_heard_at[voter];
107 if self.now >= heard.saturating_add(self.timing.primary_timeout) {
108 self.exit_votes.remove(voter);
109 }
110 }
111 if self.exit_votes.len() >= 2 {
112 if self
113 .normal()
114 .is_none_or(|normal| !normal.snapshot().ready_for_appends)
115 {
116 self.election_delay = self
117 .election_delay
118 .saturating_mul(2)
119 .min(self.timing.max_election_timeout);
120 }
121 self.next_view()?;
122 }
123 Ok(())
124 }
125
126 pub(super) fn withdraw_exit(&mut self) {
127 self.exit_votes.remove(self.local);
128 self.exit_retry_at = None;
129 }
130
131 pub(super) fn promise_pending(&self) -> bool {
132 matches!(
133 &self.role,
134 Role::ViewChanging(changing) if changing.promised_view() != changing.scope().view
135 )
136 }
137
138 pub(super) fn exit_echo_action(&mut self) -> Option<Action> {
143 if let Some((scope, mut asked)) = self.exit_echo.take()
144 && let Some(voter) = asked.pop_first()
145 {
146 if !asked.is_empty() {
147 self.exit_echo = Some((scope, asked));
148 }
149 return Some(Action::Send {
150 to: self.configuration.voter(voter),
151 message: Control::ExitView(scope),
152 });
153 }
154 if let Some(voter) = self.fence_answers.pop_first() {
155 return Some(Action::Send {
156 to: self.configuration.voter(voter),
157 message: Control::StartViewChange(StartViewChange {
158 scope: self.scope(),
159 }),
160 });
161 }
162 let voter = self.stale_answers.pop_first()?;
163 Some(Action::Send {
164 to: self.configuration.voter(voter),
165 message: Control::ExitView(self.scope()),
166 })
167 }
168
169 pub(super) fn exit_action(&mut self) -> Option<Action> {
170 if self.now < self.exit_retry_at? {
171 return None;
172 }
173 self.exit_retry_at = Some(self.now.saturating_add(self.timing.retransmit));
174 Some(Action::Broadcast(Control::ExitView(self.scope())))
175 }
176}