1mod open;
4
5use std::time::Duration;
6
7use crate::OpNumber;
8use ozzy_proto::RequestId;
9
10use super::{
11 Channel, FlowError, Operation, PipelineLimits, Prefix, Probe, ProbeError, ProbeScheduler,
12 ProbeTiming, Report, Scope, Sender, VecDeque, ledger,
13};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct OpenRequest {
21 request_id: Option<RequestId>,
22 report: Report,
23}
24
25impl OpenRequest {
26 pub const fn report(self) -> Report {
28 self.report
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub struct Repair {
35 pub channel: Channel,
37 pub after: Prefix,
39 pub through: Prefix,
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum StatusOutcome {
46 Ignored,
48 Observed,
50 Verify,
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
56pub enum TransmitError {
57 #[error(transparent)]
59 Flow(#[from] FlowError),
60 #[error(transparent)]
62 Probe(#[from] ProbeError),
63}
64
65#[derive(Debug)]
72pub struct Transmitter {
73 scope: Scope,
74 limits: PipelineLimits,
75 probes: ProbeScheduler,
76 sender: Option<Sender>,
77 announced_channel: Option<Channel>,
78 spare: VecDeque<Operation>,
80 active: bool,
81 candidate: Option<OpenRequest>,
82 candidate_limit: Option<OpNumber>,
83 repair: Option<Repair>,
84 broadcast: bool,
85 catch_up: Option<OpNumber>,
86 receipt_progress: ozzy_core::live::LiveProgress,
87}
88
89impl Transmitter {
90 pub fn new(
93 scope: Scope,
94 limits: PipelineLimits,
95 first: RequestId,
96 timing: ProbeTiming,
97 now: Duration,
98 ) -> Result<Self, TransmitError> {
99 let probes = ProbeScheduler::new(first, timing, now)?;
101 Ok(Self {
102 scope,
103 limits,
104 probes,
105 sender: None,
106 announced_channel: None,
107 spare: ledger(limits)?,
108 active: false,
109 candidate: None,
110 candidate_limit: None,
111 repair: None,
112 broadcast: false,
113 catch_up: None,
114 receipt_progress: ozzy_core::live::LiveProgress::new(timing.maximum),
115 })
116 }
117
118 pub fn enable_broadcast(&mut self) {
121 self.broadcast = true;
122 self.probes.pace_new_work();
123 }
124
125 pub fn needs_catch_up(&self) -> bool {
128 self.catch_up.is_some_and(|target| {
129 self.sender()
130 .is_some_and(|sender| sender.sent().op < target)
131 })
132 }
133
134 pub const fn catch_up_through(&self) -> Option<OpNumber> {
136 self.catch_up
137 }
138
139 pub fn sender(&self) -> Option<&Sender> {
141 self.sender.as_ref().filter(|_| self.active)
142 }
143
144 pub const fn candidate(&self) -> Option<OpenRequest> {
146 self.candidate
147 }
148
149 pub const fn pending_probe(&self) -> Option<Probe> {
151 self.probes.pending()
152 }
153
154 pub fn replace_session(&mut self, now: Duration) -> Result<(), TransmitError> {
159 self.probes.invalidate(now)?;
160 self.announced_channel = None;
161 self.candidate = None;
162 self.candidate_limit = None;
163 Ok(())
164 }
165
166 pub fn change_scope(&mut self, scope: Scope, now: Duration) -> Result<(), TransmitError> {
169 if scope == self.scope {
170 return Ok(());
171 }
172 self.probes.invalidate(now)?;
173 self.scope = scope;
174 self.active = false;
175 self.announced_channel = None;
176 self.candidate = None;
177 self.candidate_limit = None;
178 self.repair = None;
179 self.catch_up = None;
180 self.receipt_progress.reset();
181 Ok(())
182 }
183
184 pub fn next_probe(&self) -> Option<Duration> {
187 self.repair.is_none().then(|| self.probes.due())
188 }
189
190 pub fn poll_probe(
197 &mut self,
198 available: Prefix,
199 transport_pending: bool,
200 now: Duration,
201 ) -> Result<Option<Probe>, TransmitError> {
202 if transport_pending || self.repair.is_some() {
203 return Ok(None);
204 }
205 let tail = if self.broadcast {
206 available
207 } else {
208 self.sender().map_or(available, Sender::sent)
209 };
210 Ok(self
211 .probes
212 .poll(self.scope, tail, available.op.max(tail.op), now)?)
213 }
214
215 pub fn record_send(&mut self, operations: &[Operation]) -> Result<(), FlowError> {
218 let Some(sender) = self.sender.as_mut().filter(|_| self.active) else {
219 return Err(FlowError::Channel);
220 };
221 if self.repair.is_some()
222 || self.broadcast
223 && self.catch_up.is_some_and(|through| {
224 operations.last().is_some_and(|op| op.prefix.op > through)
225 })
226 {
227 return Err(FlowError::Capacity);
228 }
229 sender.record_send(sender.channel(), operations)
230 }
231
232 pub fn observe(
235 &mut self,
236 report: Report,
237 request_id: Option<RequestId>,
238 now: Duration,
239 ) -> Result<StatusOutcome, TransmitError> {
240 self.observe_with_repair_limit(report, request_id, None, now)
241 }
242
243 pub fn observe_with_repair_limit(
246 &mut self,
247 report: Report,
248 request_id: Option<RequestId>,
249 repair_limit: Option<OpNumber>,
250 now: Duration,
251 ) -> Result<StatusOutcome, TransmitError> {
252 if report.channel.scope != self.scope {
253 return Ok(StatusOutcome::Ignored);
254 }
255 let correlated = if let Some(id) = request_id {
256 let Some(probe) = self.probes.pending().filter(|probe| probe.request_id == id) else {
257 return Ok(StatusOutcome::Ignored);
258 };
259 self.probes.completion_deadline(probe.scope, id, now)?;
260 Some(probe)
261 } else {
262 None
263 };
264 report.validate_shape()?;
265 let request = OpenRequest { request_id, report };
266 if self
267 .sender()
268 .is_none_or(|sender| sender.channel() != report.channel)
269 {
270 if correlated.is_none() {
271 if self.sender().is_some() && self.announced_channel != Some(report.channel) {
275 self.probes.expedite(now)?;
276 self.announced_channel = Some(report.channel);
277 }
278 return Ok(StatusOutcome::Ignored);
279 }
280 return Ok(self.hold(request, repair_limit));
281 }
282 if self.superseded(report) {
283 return Ok(StatusOutcome::Ignored);
284 }
285 if self.broadcast
286 && self
287 .sender()
288 .is_some_and(|sender| report.received.op > sender.sent().op)
289 {
290 return Ok(self.hold(request, repair_limit));
291 }
292 let changed = self.observe_receipt(report, now)?;
293 if let Some(probe) = correlated {
294 self.answer_probe(probe, repair_limit, now)?;
295 }
296 self.trim_repair();
297 Ok(if changed || correlated.is_some() {
298 StatusOutcome::Observed
299 } else {
300 StatusOutcome::Ignored
301 })
302 }
303
304 fn hold(&mut self, request: OpenRequest, repair_limit: Option<OpNumber>) -> StatusOutcome {
307 if self.superseded(request.report) {
308 return StatusOutcome::Ignored;
309 }
310 self.candidate = Some(request);
311 self.candidate_limit = repair_limit;
312 StatusOutcome::Verify
313 }
314
315 fn superseded(&self, report: Report) -> bool {
316 self.candidate.is_some_and(|old| {
317 old.report.channel == report.channel && old.report.revision > report.revision
318 })
319 }
320
321 fn clear_candidate(&mut self) {
322 self.candidate = None;
323 self.candidate_limit = None;
324 }
325
326 fn observe_receipt(&mut self, report: Report, now: Duration) -> Result<bool, FlowError> {
328 let Some(sender) = self.sender.as_mut() else {
329 return Ok(false);
330 };
331 let before = sender.received();
332 let changed = sender.observe(report)?;
333 if sender.received().op > before.op {
334 self.receipt_progress.advanced(sender.received().op.0, now);
335 }
336 Ok(changed)
337 }
338
339 fn answer_probe(
342 &mut self,
343 probe: Probe,
344 repair_limit: Option<OpNumber>,
345 now: Duration,
346 ) -> Result<(), TransmitError> {
347 if let Some(sender) = self.sender.as_ref() {
348 let target = if self.broadcast {
349 repair_limit.unwrap_or(probe.tail.op).min(probe.tail.op)
350 } else {
351 probe.tail.op
352 };
353 if self.broadcast {
354 self.catch_up = (sender.received().op < target).then_some(target);
355 }
356 let through = sender
357 .outstanding()
358 .take_while(|op| op.prefix.op <= target)
359 .last()
360 .map_or(sender.received(), |op| op.prefix);
361 let quiet = !self.broadcast
364 || self
365 .receipt_progress
366 .repair_limit(sender.received().op.0, None, now)
367 .is_none();
368 self.repair = if quiet && sender.received().op < through.op {
369 Some(Repair {
370 channel: sender.channel(),
371 after: sender.received(),
372 through,
373 })
374 } else {
375 None
376 };
377 }
378 self.probes.complete(probe.scope, probe.request_id, now)?;
379 self.clear_candidate();
380 Ok(())
381 }
382
383 pub fn repair(&self, transport_pending: bool) -> Option<Repair> {
385 if transport_pending { None } else { self.repair }
386 }
387
388 pub fn record_repair(&mut self, repair: Repair, through: Prefix) -> Result<(), FlowError> {
391 if self.repair != Some(repair) {
392 return Err(FlowError::Channel);
393 }
394 if through.op <= repair.after.op
395 || through.op > repair.through.op
396 || self
397 .sender()
398 .is_none_or(|sender| !sender.outstanding().any(|op| op.prefix == through))
399 {
400 return Err(FlowError::History);
401 }
402 self.repair = if through == repair.through {
403 None
404 } else {
405 Some(Repair {
406 after: through,
407 ..repair
408 })
409 };
410 Ok(())
411 }
412
413 fn trim_repair(&mut self) {
414 if self.catch_up.is_some_and(|target| {
415 self.sender()
416 .is_some_and(|sender| sender.received().op >= target)
417 }) {
418 self.catch_up = None;
419 }
420 let (Some(repair), Some(received)) = (self.repair, self.sender().map(Sender::received))
421 else {
422 return;
423 };
424 self.repair = if received.op >= repair.through.op {
425 None
426 } else if received.op > repair.after.op {
427 Some(Repair {
428 after: received,
429 ..repair
430 })
431 } else {
432 Some(repair)
433 };
434 }
435}