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, 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 BufferPooler, 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::{
24 StreamExt,
25 future::{self, Either},
26};
27use rand_core::Rng;
28use std::marker::PhantomData;
29use tracing::{debug, error, trace, warn};
30
31struct Serve<P: PublicKey> {
33 timer: histogram::Timer,
34 peer: P,
35 id: u64,
36 result: Result<Bytes, oneshot::error::RecvError>,
37}
38
39pub struct Engine<E, P, D, B, Key, Con, Pro, NetS, NetR>
41where
42 E: BufferPooler + Clock + Spawner + Rng + Metrics,
43 P: PublicKey,
44 D: Provider<PublicKey = P>,
45 B: Blocker<PublicKey = P>,
46 Key: Span,
47 Con: Consumer<Key = Key, Value = Bytes>,
48 Pro: Producer<Key = Key>,
49 NetS: Sender<PublicKey = P>,
50 NetR: Receiver<PublicKey = P>,
51 Con::Subscriber: Eq,
52{
53 context: ContextCell<E>,
55
56 producer: Pro,
58
59 peer_provider: D,
61
62 blocker: B,
64
65 last_peer_set_id: Option<u64>,
67
68 mailbox: mailbox::Receiver<Message<Key, P, Con::Subscriber>>,
70
71 fetcher: Fetcher<E, P, Key, NetS>,
73
74 inflight: Inflight<Con, P>,
76
77 subscribers: subscribers::Tracker<Key, Con::Subscriber>,
79
80 serves: FuturesPool<'static, Serve<P>>,
85
86 priority_responses: bool,
88
89 metrics: metrics::Metrics,
91
92 _r: PhantomData<NetR>,
94}
95
96impl<E, P, D, B, Key, Con, Pro, NetS, NetR> Engine<E, P, D, B, Key, Con, Pro, NetS, NetR>
97where
98 E: BufferPooler + Clock + Spawner + Rng + Metrics,
99 P: PublicKey,
100 D: Provider<PublicKey = P>,
101 B: Blocker<PublicKey = P>,
102 Key: Span,
103 Con: Consumer<Key = Key, Value = Bytes>,
104 Pro: Producer<Key = Key>,
105 NetS: Sender<PublicKey = P>,
106 NetR: Receiver<PublicKey = P>,
107 Con::Subscriber: Clone + Ord + Send + 'static,
108{
109 pub fn new(
113 context: E,
114 cfg: Config<P, D, B, Key, Con, Pro>,
115 ) -> (Self, Mailbox<Key, P, Con::Subscriber>) {
116 let (sender, receiver) = mailbox::new(context.child("mailbox"), cfg.mailbox_size);
117
118 let metrics = metrics::Metrics::init(&context);
119 let fetcher = Fetcher::new(
120 context.child("fetcher"),
121 FetcherConfig {
122 me: cfg.me,
123 timeout: cfg.timeout,
124 retry_timeout: cfg.fetch_retry_timeout,
125 priority_requests: cfg.priority_requests,
126 },
127 );
128 (
129 Self {
130 context: ContextCell::new(context),
131 producer: cfg.producer,
132 peer_provider: cfg.peer_provider,
133 blocker: cfg.blocker,
134 last_peer_set_id: None,
135 mailbox: receiver,
136 fetcher,
137 inflight: Inflight::new(cfg.consumer),
138 subscribers: subscribers::Tracker::new(),
139 serves: FuturesPool::default(),
140 priority_responses: cfg.priority_responses,
141 metrics,
142 _r: PhantomData,
143 },
144 Mailbox::new(sender),
145 )
146 }
147
148 pub fn start(mut self, network: (NetS, NetR)) -> Handle<()> {
154 spawn_cell!(self.context, self.run(network))
155 }
156
157 async fn run(mut self, network: (NetS, NetR)) {
159 let (mut sender, mut receiver) = wrap(
161 (),
162 self.context.network_buffer_pool().clone(),
163 network.0,
164 network.1,
165 );
166 let mut peer_set_subscription = self.peer_provider.subscribe().await;
167 let mut blocked_subscription = Some(self.blocker.blocked());
168
169 select_loop! {
170 self.context,
171 on_start => {
172 let blocked_update = blocked_subscription.as_mut().map_or_else(
175 || Either::Right(future::pending()),
176 |subscription| Either::Left(subscription.next()),
177 );
178
179 let _ = self
181 .metrics
182 .fetch_pending
183 .try_set(self.fetcher.len_pending());
184 let _ = self.metrics.fetch_active.try_set(self.fetcher.len_active());
185 let _ = self.metrics.serve_processing.try_set(self.serves.len());
186
187 let deadline_pending = match self.fetcher.get_pending_deadline() {
189 Some(deadline) => Either::Left(self.context.sleep_until(deadline)),
190 None => Either::Right(future::pending()),
191 };
192
193 let deadline_active = match self.fetcher.get_active_deadline() {
195 Some(deadline) => Either::Left(self.context.sleep_until(deadline)),
196 None => Either::Right(future::pending()),
197 };
198 },
199 on_stopped => {
200 debug!("shutdown");
201 self.inflight.drain();
202 self.subscribers.clear();
203 self.serves.cancel_all();
204 },
205 Some(update) = peer_set_subscription.recv() else {
207 debug!("peer set subscription closed");
208 return;
209 } => {
210 if self.last_peer_set_id < Some(update.index) {
211 self.last_peer_set_id = Some(update.index);
212 self.fetcher.reconcile(update.latest.primary.as_ref());
213 }
214 },
215 blocked = blocked_update => {
217 match blocked {
218 Some(blocked) => self.fetcher.set_blocked(blocked),
219 None => {
220 debug!("blocked subscription closed");
221 blocked_subscription = None;
222 }
223 }
224 },
225 _ = deadline_active => {
227 if let Some(key) = self.fetcher.pop_active() {
228 debug!(?key, "requester timeout");
229 self.metrics.fetch.inc(Status::Failure);
230 self.fetcher.add_retry(key);
231 }
232 },
233 delivery = self.inflight.next_delivery() => {
238 if let Ok((peer, elapsed, bytes, delivery, result)) = delivery {
241 self.handle_delivery(peer, elapsed, bytes, delivery, result);
242 }
243 },
244 Some(msg) = self.mailbox.recv() else {
246 error!("mailbox closed");
247 return;
248 } => {
249 match msg {
250 Message::Fetch(keys) => {
251 for FetchKey {
252 key,
253 subscribers,
254 metadata: targets,
255 } in keys
256 {
257 trace!(?key, "mailbox: fetch");
258
259 let is_new = !self.inflight.contains(&key);
261 self.subscribers.insert(key.clone(), subscribers);
262
263 match targets {
265 Some(targets) => {
266 if is_new || self.fetcher.has_targets(&key) {
270 self.fetcher.add_targets(key.clone(), targets);
271 }
272 }
273 None => self.fetcher.clear_targets(&key),
274 }
275
276 if is_new {
278 self.inflight.insert(
279 key.clone(),
280 self.metrics.fetch_duration.timer(self.context.as_ref()),
281 );
282 self.fetcher.add_ready(key);
283 } else {
284 trace!(?key, "updated targets for existing fetch");
285 }
286 }
287 }
288 Message::Retain { predicate } => {
289 trace!("mailbox: retain");
290
291 self.subscribers
292 .retain(|key, subscriber| predicate(key, subscriber));
293 let subscribers = &self.subscribers;
294 self.fetcher.retain(|key| subscribers.contains(key));
295 let count = self.inflight.retain(|key| subscribers.contains(key)) as u64;
296 self.record_cancellations(count);
297 }
298 }
299 },
300 _ = deadline_pending => {},
303 serve = self.serves.next_completed() => {
305 let Serve {
306 timer,
307 peer,
308 id,
309 result,
310 } = serve;
311
312 match result {
314 Ok(_) => {
315 timer.observe(self.context.as_ref());
316 self.metrics.serve.inc(Status::Success);
317 }
318 Err(ref err) => {
319 debug!(?err, ?peer, ?id, "serve failed");
320 self.metrics.serve.inc(Status::Failure);
321 }
322 }
323
324 self.handle_serve(&mut sender, peer, id, result, self.priority_responses);
326 },
327 msg = receiver.recv() => {
329 let (peer, msg) = match msg {
331 Ok(msg) => msg,
332 Err(err) => {
333 error!(?err, "receiver closed");
334 return;
335 }
336 };
337
338 match msg {
339 Ok(msg) => match msg.payload {
340 wire::Payload::Request(key) => {
341 self.handle_network_request(peer, msg.id, key)
342 }
343 wire::Payload::Response(response) => {
344 self.handle_network_response(peer, msg.id, response)
345 }
346 wire::Payload::Error => self.handle_network_error_response(peer, msg.id),
347 },
348 Err(err) => {
349 trace!(?err, ?peer, "decode failed");
350 }
351 };
352 },
353 on_end => {
354 if self
357 .fetcher
358 .get_pending_deadline()
359 .is_some_and(|deadline| deadline <= self.context.current())
360 {
361 self.fetcher.fetch(&mut sender);
362 }
363 },
364 }
365 }
366
367 fn record_cancellations(&mut self, count: u64) {
369 if count == 0 {
370 self.metrics.cancel.inc(Status::Dropped);
371 } else {
372 self.metrics.cancel.inc_by(Status::Success, count);
373 }
374 }
375
376 fn handle_serve(
378 &mut self,
379 sender: &mut WrappedSender<NetS, wire::Message<Key>>,
380 peer: P,
381 id: u64,
382 response: Result<Bytes, oneshot::error::RecvError>,
383 priority: bool,
384 ) {
385 let payload: wire::Payload<Key> = response.map_or_else(
387 |_| wire::Payload::Error,
388 |data| wire::Payload::Response(data),
389 );
390 let msg = wire::Message { id, payload };
391
392 let result = sender.send(Recipients::One(peer.clone()), msg, priority);
394
395 if result.is_empty() {
397 warn!(?peer, ?id, "serve send failed");
398 } else {
399 trace!(?peer, ?id, "serve sent");
400 };
401 }
402
403 fn handle_network_request(&mut self, peer: P, id: u64, key: Key) {
405 trace!(?peer, ?id, "peer request");
407 let mut producer = self.producer.clone();
408 let timer = self.metrics.serve_duration.timer(self.context.as_ref());
409 let receiver = producer.produce(key);
410 self.serves.push(async move {
411 let result = receiver.await;
412 Serve {
413 timer,
414 peer,
415 id,
416 result,
417 }
418 });
419 }
420
421 fn handle_network_response(&mut self, peer: P, id: u64, response: Bytes) {
423 trace!(?peer, ?id, "peer response: data");
424
425 let Some((key, elapsed)) = self.fetcher.pop_response(id, &peer) else {
427 return;
429 };
430
431 let Some(subscribers) = self.subscribers.pending(&key) else {
432 warn!(?key, "response for fetch with no subscribers");
433 self.inflight.cancel(&key);
434 return;
435 };
436 let delivery = Delivery { key, subscribers };
437
438 self.inflight.deliver(delivery, peer, elapsed, response);
440 }
441
442 fn handle_delivery(
444 &mut self,
445 peer: P,
446 elapsed: std::time::Duration,
447 bytes: usize,
448 delivery: Delivery<Key, Con::Subscriber>,
449 outcome: Option<Outcome>,
450 ) {
451 let Delivery {
452 key,
453 subscribers: delivered,
454 ..
455 } = delivery;
456 let already_accepted = self.inflight.response_accepted(&key);
457
458 let Some(outcome) = outcome else {
462 let remaining = self
463 .subscribers
464 .remove_delivered(&key, delivered.map_into(|(subscriber, _)| subscriber));
465 if let Some(subscribers) = remaining {
466 self.inflight.redeliver(Delivery { key, subscribers });
467 return;
468 }
469 if !already_accepted {
470 self.metrics.fetch.inc(Status::Dropped);
471 }
472 self.inflight.cancel(&key);
473 self.fetcher.clear_targets(&key);
474 return;
475 };
476
477 if !already_accepted && outcome != Outcome::Ignored {
478 self.fetcher.record_response(&peer, elapsed, bytes);
479 }
480
481 match outcome {
482 Outcome::Complete => {
483 let remaining = self
487 .subscribers
488 .remove_delivered(&key, delivered.map_into(|(subscriber, _)| subscriber));
489
490 if let Some(subscribers) = remaining {
491 if !already_accepted {
492 self.metrics.fetch.inc(Status::Success);
493 self.inflight.accept_response(&key, self.context.as_ref());
494 }
495 self.inflight.redeliver(Delivery { key, subscribers });
496 } else {
497 if !already_accepted {
500 self.metrics.fetch.inc(Status::Success);
501 }
502 self.inflight.complete(self.context.as_ref(), &key);
503 self.fetcher.clear_targets(&key);
504 }
505 }
506 Outcome::Ambiguous => {
507 self.metrics.fetch.inc(Status::Ambiguous);
511 self.inflight.discard_response(&key);
512 self.fetcher.add_retry(key);
513 }
514 Outcome::Invalid => {
515 if already_accepted {
520 warn!(
521 ?key,
522 "previously accepted response was rejected during local redelivery"
523 );
524 self.metrics.fetch.inc(Status::Failure);
525 self.inflight.complete(self.context.as_ref(), &key);
526 self.subscribers.remove(&key);
527 self.fetcher.clear_targets(&key);
528 return;
529 }
530
531 commonware_p2p::block!(self.blocker, peer, "invalid data received");
535 self.metrics.fetch.inc(Status::Failure);
536 self.inflight.discard_response(&key);
537 self.fetcher.add_retry(key);
538 }
539 Outcome::Ignored => {
540 self.metrics.fetch.inc(Status::Dropped);
543 self.inflight.cancel(&key);
544 self.subscribers.remove(&key);
545 self.fetcher.clear_targets(&key);
546 }
547 }
548 }
549
550 fn handle_network_error_response(&mut self, peer: P, id: u64) {
552 trace!(?peer, ?id, "peer response: error");
553
554 let Some(key) = self.fetcher.pop_missing(id, &peer) else {
556 return;
558 };
559
560 self.metrics.fetch.inc(Status::Failure);
562 self.fetcher.add_retry(key);
563 }
564}