1use super::{
2 Producer,
3 config::Config,
4 fetcher::{Config as FetcherConfig, Fetcher},
5 inflight::Inflight,
6 ingress::{FetchKey, Mailbox, Message},
7 metrics, wire,
8};
9use crate::{Consumer, Delivery, Outcome, Resolution, delivery::Completion, subscribers};
10use bytes::Bytes;
11use commonware_actor::mailbox;
12use commonware_cryptography::PublicKey;
13use commonware_macros::select_loop;
14use commonware_p2p::{
15 Blocker, Provider, Receiver, Recipients, Sender,
16 utils::codec::{WrappedSender, wrap},
17};
18use commonware_runtime::{
19 Clock, ContextCell, Handle, Metrics, Spawner, spawn_cell,
20 telemetry::metrics::{GaugeExt, histogram, status::Status},
21};
22use commonware_utils::{Span, channel::oneshot, futures::Pool as FuturesPool};
23use futures::future::{self, Either};
24use rand_core::Rng;
25use tracing::{debug, error, trace, warn};
26
27struct Serve<P: PublicKey> {
29 timer: histogram::Timer,
30 peer: P,
31 id: u64,
32 result: Result<Bytes, oneshot::error::RecvError>,
33}
34
35pub struct Engine<E, P, D, B, Key, Con, Pro>
37where
38 E: Clock + Spawner + Rng + Metrics,
39 P: PublicKey,
40 D: Provider<PublicKey = P>,
41 B: Blocker<PublicKey = P>,
42 Key: Span,
43 Con: Consumer<Key = Key, Value = Bytes>,
44 Pro: Producer<Key = Key>,
45{
46 context: ContextCell<E>,
48
49 producer: Pro,
51
52 peer_provider: D,
54
55 blocker: B,
57
58 last_peer_set_id: Option<u64>,
60
61 mailbox: mailbox::Receiver<Message<Key, P, Con::Subscriber>>,
63
64 fetcher: Fetcher<E, P, Key>,
66
67 inflight: Inflight<Con, P>,
69
70 subscribers: subscribers::Tracker<Key, Con::Subscriber>,
72
73 serves: FuturesPool<'static, Serve<P>>,
78
79 priority_responses: bool,
81
82 metrics: metrics::Metrics,
84}
85
86impl<E, P, D, B, Key, Con, Pro> Engine<E, P, D, B, Key, Con, Pro>
87where
88 E: Clock + Spawner + Rng + Metrics,
89 P: PublicKey,
90 D: Provider<PublicKey = P>,
91 B: Blocker<PublicKey = P>,
92 Key: Span,
93 Con: Consumer<Key = Key, Value = Bytes>,
94 Pro: Producer<Key = Key>,
95 Con::Subscriber: Ord,
96{
97 pub fn new(
101 context: E,
102 cfg: Config<P, D, B, Key, Con, Pro>,
103 ) -> (Self, Mailbox<Key, P, Con::Subscriber>) {
104 let (sender, receiver) = mailbox::new(context.child("mailbox"), cfg.mailbox_size);
105
106 let metrics = metrics::Metrics::init(&context);
107 let fetcher = Fetcher::new(
108 context.child("fetcher"),
109 FetcherConfig {
110 me: cfg.me,
111 fetch_timeout: cfg.fetch_timeout,
112 retry_delay: cfg.fetch_retry_delay,
113 priority_requests: cfg.priority_requests,
114 },
115 );
116 (
117 Self {
118 context: ContextCell::new(context),
119 producer: cfg.producer,
120 peer_provider: cfg.peer_provider,
121 blocker: cfg.blocker,
122 last_peer_set_id: None,
123 mailbox: receiver,
124 fetcher,
125 inflight: Inflight::new(cfg.consumer),
126 subscribers: subscribers::Tracker::new(),
127 serves: FuturesPool::default(),
128 priority_responses: cfg.priority_responses,
129 metrics,
130 },
131 Mailbox::new(sender),
132 )
133 }
134
135 pub fn start<NetS: Sender<PublicKey = P>, NetR: Receiver<PublicKey = P>>(
141 mut self,
142 network: (NetS, NetR),
143 ) -> Handle<()> {
144 spawn_cell!(self.context, self.run(network))
145 }
146
147 async fn run<NetS: Sender<PublicKey = P>, NetR: Receiver<PublicKey = P>>(
149 mut self,
150 network: (NetS, NetR),
151 ) {
152 let (mut sender, mut receiver) = wrap((), network.0, network.1);
154 let mut peer_set_subscription = self.peer_provider.subscribe().await;
155 let mut blocked_subscription = Some(self.blocker.blocked());
156
157 select_loop! {
158 self.context,
159 on_start => {
160 let blocked_update = blocked_subscription.as_mut().map_or_else(
163 || Either::Right(future::pending()),
164 |subscription| Either::Left(subscription.recv()),
165 );
166
167 let _ = self
169 .metrics
170 .fetch_pending
171 .try_set(self.fetcher.len_pending());
172 let _ = self.metrics.fetch_active.try_set(self.fetcher.len_active());
173 let _ = self.metrics.serve_processing.try_set(self.serves.len());
174
175 let deadline_pending = match self.fetcher.get_pending_deadline() {
177 Some(deadline) => Either::Left(self.context.sleep_until(deadline)),
178 None => Either::Right(future::pending()),
179 };
180
181 let deadline_active = match self.fetcher.get_active_deadline() {
183 Some(deadline) => Either::Left(self.context.sleep_until(deadline)),
184 None => Either::Right(future::pending()),
185 };
186 },
187 on_stopped => {
188 debug!("shutdown");
189 self.inflight.drain();
190 self.subscribers.clear();
191 self.serves.cancel_all();
192 },
193 Some(update) = peer_set_subscription.recv() else {
195 debug!("peer set subscription closed");
196 return;
197 } => {
198 if self.last_peer_set_id < Some(update.index) {
199 self.last_peer_set_id = Some(update.index);
200 self.fetcher.reconcile(update.latest.union().as_ref());
201 }
202 },
203 blocked = blocked_update => {
205 match blocked {
206 Some(blocked) => self.fetcher.set_blocked(blocked),
207 None => {
208 debug!("blocked subscription closed");
209 blocked_subscription = None;
210 }
211 }
212 },
213 _ = deadline_active => {
215 if let Some(key) = self.fetcher.pop_active() {
216 debug!(?key, "requester timeout");
217 self.metrics.fetch.inc(Status::Failure);
218 self.fetcher.add_retry(key);
219 }
220 },
221 delivery = self.inflight.next_delivery() => {
224 if let Ok(completed) = delivery {
225 self.handle_delivery(completed);
226 }
227 },
228 Some(msg) = self.mailbox.recv() else {
230 error!("mailbox closed");
231 return;
232 } => {
233 match msg {
234 Message::Fetch(keys) => {
235 for FetchKey {
236 key,
237 subscribers,
238 metadata: targets,
239 } in keys
240 {
241 trace!(?key, "mailbox: fetch");
242
243 let is_new = !self.inflight.contains(&key);
245 self.subscribers.insert(key.clone(), subscribers);
246
247 match targets {
249 Some(targets) => {
250 if is_new || self.fetcher.has_targets(&key) {
254 self.fetcher.add_targets(key.clone(), targets);
255 }
256 }
257 None => self.fetcher.clear_targets(&key),
258 }
259
260 if is_new {
262 self.inflight.insert(
263 key.clone(),
264 self.metrics.fetch_duration.timer(self.context.as_ref()),
265 );
266 self.fetcher.add_ready(key);
267 } else {
268 trace!(?key, "updated targets for existing fetch");
269 }
270 }
271 }
272 Message::Retain { predicate } => {
273 trace!("mailbox: retain");
274
275 self.subscribers
276 .retain(|key, subscriber| predicate(key, subscriber));
277 let subscribers = &self.subscribers;
278 self.fetcher.retain(|key| subscribers.contains(key));
279 let count = self.inflight.retain(|key| subscribers.contains(key)) as u64;
280 self.record_cancellations(count);
281 }
282 }
283 },
284 _ = deadline_pending => {},
287 serve = self.serves.next_completed() => {
289 let Serve {
290 timer,
291 peer,
292 id,
293 result,
294 } = serve;
295
296 match result {
298 Ok(_) => {
299 timer.observe(self.context.as_ref());
300 self.metrics.serve.inc(Status::Success);
301 }
302 Err(ref err) => {
303 debug!(?err, ?peer, ?id, "serve failed");
304 self.metrics.serve.inc(Status::Failure);
305 }
306 }
307
308 self.handle_serve(&mut sender, peer, id, result, self.priority_responses);
310 },
311 msg = receiver.recv() => {
313 let (peer, msg) = match msg {
315 Ok(msg) => msg,
316 Err(err) => {
317 error!(?err, "receiver closed");
318 return;
319 }
320 };
321
322 match msg {
323 Ok(msg) => match msg.payload {
324 wire::Payload::Request(key) => {
325 self.handle_network_request(peer, msg.id, key)
326 }
327 wire::Payload::Response(response) => {
328 self.handle_network_response(peer, msg.id, response)
329 }
330 wire::Payload::Error => self.handle_network_error_response(peer, msg.id),
331 },
332 Err(err) => {
333 trace!(?err, ?peer, "decode failed");
334 }
335 };
336 },
337 on_end => {
338 if self
341 .fetcher
342 .get_pending_deadline()
343 .is_some_and(|deadline| deadline <= self.context.current())
344 {
345 self.fetcher.fetch(&mut sender);
346 }
347 },
348 }
349 }
350
351 fn record_cancellations(&mut self, count: u64) {
353 if count == 0 {
354 self.metrics.cancel.inc(Status::Dropped);
355 } else {
356 self.metrics.cancel.inc_by(Status::Success, count);
357 }
358 }
359
360 fn handle_serve<NetS: Sender<PublicKey = P>>(
362 &mut self,
363 sender: &mut WrappedSender<NetS, wire::Message<Key>>,
364 peer: P,
365 id: u64,
366 response: Result<Bytes, oneshot::error::RecvError>,
367 priority: bool,
368 ) {
369 let payload: wire::Payload<Key> = response.map_or_else(
371 |_| wire::Payload::Error,
372 |data| wire::Payload::Response(data),
373 );
374 let msg = wire::Message { id, payload };
375
376 let result = sender.send(Recipients::One(peer.clone()), msg, priority);
378
379 if result.is_empty() {
381 warn!(?peer, ?id, "serve send failed");
382 } else {
383 trace!(?peer, ?id, "serve sent");
384 };
385 }
386
387 fn handle_network_request(&mut self, peer: P, id: u64, key: Key) {
389 trace!(?peer, ?id, "peer request");
391 let mut producer = self.producer.clone();
392 let timer = self.metrics.serve_duration.timer(self.context.as_ref());
393 let receiver = producer.produce(key);
394 self.serves.push(async move {
395 let result = receiver.await;
396 Serve {
397 timer,
398 peer,
399 id,
400 result,
401 }
402 });
403 }
404
405 fn handle_network_response(&mut self, peer: P, id: u64, response: Bytes) {
407 trace!(?peer, ?id, "peer response: data");
408
409 let Some((key, elapsed)) = self.fetcher.pop_response(id, &peer) else {
411 return;
413 };
414
415 let Some(subscribers) = self.subscribers.pending(&key) else {
416 warn!(?key, "response for fetch with no subscribers");
417 self.inflight.cancel(&key);
418 return;
419 };
420 let delivery = Delivery { key, subscribers };
421
422 self.inflight.deliver(delivery, peer, elapsed, response);
424 }
425
426 fn handle_delivery(
428 &mut self,
429 completed: Completion<Key, Con::Subscriber, (P, std::time::Duration, usize)>,
430 ) {
431 let Completion {
432 context: (peer, elapsed, bytes),
433 delivery:
434 Delivery {
435 key,
436 subscribers: delivered,
437 },
438 outcome,
439 canceled,
440 previously_accepted: already_accepted,
441 } = completed;
442
443 match outcome {
446 Some(Outcome::Resolved(_)) if !already_accepted => {
447 self.fetcher.record_response(&peer, elapsed, bytes);
448 self.metrics.fetch.inc(Status::Success);
449 }
450 Some(Outcome::Unresolved) if !already_accepted => {
451 self.metrics.fetch.inc(Status::Ambiguous);
452 }
453 Some(Outcome::Invalid) => {
454 if already_accepted {
457 warn!(
458 ?key,
459 "previously accepted response was rejected during local redelivery"
460 );
461 } else {
462 commonware_p2p::block!(self.blocker, peer, "invalid data received");
463 }
464 self.metrics.fetch.inc(Status::Failure);
465 }
466 _ => {}
467 }
468
469 if canceled {
471 return;
472 }
473
474 let Some(outcome) = outcome else {
478 let remaining = self
479 .subscribers
480 .remove_delivered(&key, delivered.map_into(|(subscriber, _)| subscriber));
481 if let Some(subscribers) = remaining {
482 self.inflight.redeliver(Delivery { key, subscribers });
483 return;
484 }
485 if !already_accepted {
486 self.metrics.fetch.inc(Status::Dropped);
487 }
488 self.inflight.cancel(&key);
489 self.fetcher.clear(&key);
490 return;
491 };
492
493 match outcome {
496 Outcome::Resolved(Resolution::Complete) => {
497 let remaining = self
501 .subscribers
502 .remove_delivered(&key, delivered.map_into(|(subscriber, _)| subscriber));
503
504 if let Some(subscribers) = remaining {
505 if !already_accepted {
506 self.inflight.accept_response(&key, self.context.as_ref());
507 }
508 self.inflight.redeliver(Delivery { key, subscribers });
509 } else {
510 self.inflight.complete(self.context.as_ref(), &key);
513 self.fetcher.clear(&key);
514 }
515 }
516 Outcome::Resolved(Resolution::Partial) | Outcome::Unresolved => {
517 self.inflight.discard_response(&key);
520 self.fetcher.add_retry(key);
521 }
522 Outcome::Invalid => {
523 if already_accepted {
526 self.inflight.complete(self.context.as_ref(), &key);
527 self.subscribers.remove(&key);
528 self.fetcher.clear(&key);
529 return;
530 }
531
532 self.inflight.discard_response(&key);
535 self.fetcher.add_retry(key);
536 }
537 Outcome::Ignored => {
538 self.metrics.fetch.inc(Status::Dropped);
541 self.inflight.cancel(&key);
542 self.subscribers.remove(&key);
543 self.fetcher.clear(&key);
544 }
545 }
546 }
547
548 fn handle_network_error_response(&mut self, peer: P, id: u64) {
550 trace!(?peer, ?id, "peer response: error");
551
552 let Some(key) = self.fetcher.pop_missing(id, &peer) else {
554 return;
556 };
557
558 self.metrics.fetch.inc(Status::Failure);
560 self.fetcher.add_retry(key);
561 }
562}