1use std::{
2 fmt::Display,
3 num::NonZeroUsize,
4 sync::{Arc, RwLock},
5 task::Waker,
6 time::Duration,
7};
8
9use closest::ClosestPeersIter;
10use fnv::FnvHashMap;
11use futures::{
12 channel::mpsc::{self},
13 task::Poll,
14};
15use tracing::{debug, instrument, trace, warn};
16use wasm_timer::Instant;
17
18use crate::{
19 IdBytes, PeerId,
20 commit::{Commit, CommitEvent},
21 constants::DEFAULT_COMMIT_CHANNEL_SIZE,
22 io::InResponse,
23 kbucket::{ALPHA_VALUE, K_VALUE},
24};
25
26mod closest;
27mod peers;
28pub mod table;
29
30use self::{
31 peers::PeersIterState,
32 table::{PeerState, QueryTable},
33};
34use super::{Command, Peer, message::ReplyMsgData};
35
36#[derive(Debug)]
39pub struct QueryPool {
40 local_id: IdBytes,
41 queries: FnvHashMap<QueryId, Arc<RwLock<Query>>>,
42 config: QueryConfig,
43 next_id: usize,
44 waker: Option<Waker>,
45}
46
47#[derive(Debug, Clone)]
49pub struct QueryConfig {
50 pub timeout: Duration,
52
53 pub replication_factor: NonZeroUsize,
55
56 pub parallelism: NonZeroUsize,
58}
59
60impl Default for QueryConfig {
61 fn default() -> Self {
62 QueryConfig {
63 timeout: Duration::from_secs(60),
64 replication_factor: NonZeroUsize::new(K_VALUE.get()).expect("K_VALUE > 0"),
65 parallelism: ALPHA_VALUE,
66 }
67 }
68}
69
70impl QueryPool {
71 pub fn new(local_id: IdBytes, config: QueryConfig) -> Self {
73 Self {
74 local_id,
75 next_id: 0,
76 config,
77 queries: Default::default(),
78 waker: Default::default(),
79 }
80 }
81
82 pub fn len(&self) -> usize {
84 self.queries.len()
85 }
86
87 pub fn is_empty(&self) -> bool {
88 self.queries.is_empty()
89 }
90
91 pub(crate) fn next_query_id(&mut self) -> QueryId {
92 let id = QueryId(self.next_id);
93 self.next_id = self.next_id.wrapping_add(1);
94 id
95 }
96
97 pub fn bootstrap(
98 &mut self,
99 target: IdBytes,
100 peers: Vec<PeerId>,
101 bootstrap: Vec<Peer>,
102 ) -> QueryId {
103 self.add_stream(
104 Command::Internal(crate::InternalCommand::FindNode),
105 peers,
106 target,
107 None,
108 bootstrap,
109 Commit::No,
110 )
111 }
112
113 pub fn add_stream(
115 &mut self,
116 cmd: Command,
117 peers: Vec<PeerId>,
118 target: IdBytes,
119 value: Option<Vec<u8>>,
120 bootstrap: Vec<Peer>,
121 commit: Commit,
122 ) -> QueryId {
123 let id = self.next_query_id();
124 debug!(
125 id = id.0,
126 cmd = tracing::field::display(cmd),
127 n_peers = peers.len(),
128 n_bootstrap = bootstrap.len(),
129 "new Query"
130 );
131 let query = Query::new(
132 id,
133 cmd,
134 self.config.parallelism,
135 self.local_id,
136 target,
137 value,
138 peers,
139 bootstrap,
140 commit,
141 );
142 self.queries.insert(id, Arc::new(RwLock::new(query)));
143 if let Some(waker) = &self.waker {
144 waker.wake_by_ref();
145 }
146 id
147 }
148
149 pub fn get(&self, id: &QueryId) -> Option<Arc<RwLock<Query>>> {
157 self.queries.get(id).cloned()
158 }
159
160 pub fn poll(&mut self, now: Instant, waker: Waker) -> QueryPoolEvent {
162 let mut finished = None;
163 let mut timeout = None;
164 let mut waiting = None;
165
166 _ = self.waker.insert(waker);
167
168 for (&query_id, query) in self.queries.iter() {
169 let mut w_query = query.write().unwrap();
170 w_query.stats.start = w_query.stats.start.or(Some(now));
171 match w_query.poll(now) {
172 Poll::Ready(Some(ev)) => {
173 match ev {
174 QueryEvent::Commit(cev) => {
175 return QueryPoolEvent::Commit((query.clone(), cev));
176 }
177 qev => waiting = Some((qev, query_id)),
178 }
179 break;
180 }
181 Poll::Ready(None) => {
182 finished = Some(query_id);
183 break;
184 }
185 Poll::Pending => {
186 let elapsed = now - w_query.stats.start.unwrap_or(now);
187 if elapsed >= self.config.timeout {
188 timeout = Some(query_id);
189 break;
190 }
191 }
192 }
193 }
194
195 if let Some((event, query_id)) = waiting {
196 let query = self.queries.get(&query_id).expect("s.a.");
197 return QueryPoolEvent::Waiting(Some((query.clone(), event)));
198 }
199
200 if let Some(query_id) = finished {
201 let query = self.queries.remove(&query_id).expect("s.a.");
202 query.write().unwrap().stats.end = Some(now);
203 return QueryPoolEvent::Finished(query.clone());
204 }
205
206 if let Some(query_id) = timeout {
207 let query = self.queries.remove(&query_id).expect("s.a.");
208 query.write().unwrap().stats.end = Some(now);
209 return QueryPoolEvent::Timeout(query);
210 }
211
212 if self.queries.is_empty() {
213 QueryPoolEvent::Idle
214 } else {
215 QueryPoolEvent::Waiting(None)
216 }
217 }
218}
219
220#[derive(Debug)]
222pub enum QueryPoolEvent {
223 Idle,
225 Waiting(Option<(Arc<RwLock<Query>>, QueryEvent)>),
228 Commit((Arc<RwLock<Query>>, CommitEvent)),
230 Finished(Arc<RwLock<Query>>),
232 Timeout(Arc<RwLock<Query>>),
234}
235
236#[derive(Debug)]
237pub struct Query {
238 pub id: QueryId,
240 #[expect(unused)] parallelism: NonZeroUsize,
243 pub(crate) peer_iter: ClosestPeersIter,
245 pub cmd: Command,
247 stats: QueryStats,
249 pub value: Option<Vec<u8>>,
251 inner: QueryTable,
253 pub commit: Commit,
255 pub closest_replies: Vec<Arc<InResponse>>,
257}
258
259impl Query {
260 #[expect(clippy::too_many_arguments)]
262 pub fn new(
263 id: QueryId,
264 cmd: Command,
265 parallelism: NonZeroUsize,
266 local_id: IdBytes,
267 target: IdBytes,
268 value: Option<Vec<u8>>,
269 peers: Vec<PeerId>,
270 bootstrap: Vec<Peer>,
271 commit: Commit,
272 ) -> Self {
273 Self {
274 id,
275 parallelism,
276 peer_iter: ClosestPeersIter::new(target, bootstrap),
277 cmd,
278 stats: QueryStats::empty(),
279 value,
280 inner: QueryTable::new(local_id, target, peers),
281 commit,
282 closest_replies: vec![],
283 }
284 }
285
286 #[instrument(skip_all)]
289 fn maybe_update_closest_replies(&mut self, data: &Arc<InResponse>) -> Option<usize> {
290 let reply_distance = self.peer_iter.target.distance(data.response.id?);
291 let replace = self.closest_replies.len() >= K_VALUE.into();
292
293 if self.closest_replies.is_empty() {
294 self.closest_replies.insert(0, data.clone());
295 return Some(0);
296 }
297 for (i, cur) in self.closest_replies.iter().enumerate() {
298 if reply_distance
299 < self
300 .peer_iter
301 .target
302 .distance(cur.response.id.expect("TODO this should be a PeerId"))
303 {
304 if replace {
305 self.closest_replies[i] = data.clone();
306 } else {
307 self.closest_replies.insert(i, data.clone());
308 }
309 trace!(
310 closest_replies.len = self.closest_replies.len(),
311 "Inerted response at [{i}]"
312 );
313 return Some(i);
314 }
315 }
316 trace!("Response farther than all current replies");
317 None
318 }
319 pub fn command(&self) -> Command {
320 self.cmd
321 }
322
323 pub fn target(&self) -> &IdBytes {
324 self.inner.target()
325 }
326 pub fn value(&self) -> Option<&Vec<u8>> {
327 self.value.as_ref()
328 }
329
330 pub fn id(&self) -> QueryId {
331 self.id
332 }
333
334 pub(crate) fn _on_timeout(&mut self, peer: &Peer) {
336 self.stats.failure += 1;
337 for (p, state) in self.inner.peers_mut() {
338 if p.addr == peer.addr {
339 *state = PeerState::Failed;
340 break;
341 }
342 }
343 self.peer_iter.on_failure(peer);
344 }
345
346 #[instrument(skip(self, data))]
348 pub(crate) fn inject_response(&mut self, data: Arc<InResponse>) -> Option<Arc<InResponse>> {
349 use crate::Progress as P;
350 use Commit as C;
351
352 match &mut self.commit {
353 C::Auto(prog @ (P::AwaitingReplies(_) | P::Sending(_)))
355 | C::Custom(prog @ (P::AwaitingReplies(_) | P::Sending(_))) => {
356 warn!("recieved a response! for tid {}", data.response.tid);
357 prog.recieved_tid(data.response.tid);
358 }
359 _ => {
361 self.maybe_update_closest_replies(&data);
362 let remote = data
363 .response
364 .id
365 .map(|id| PeerId::new(data.peer.addr, IdBytes::from(id)));
366
367 if data.response.is_error() {
368 warn!(error = data.response.error, "Error in peer response");
369 self.stats.failure += 1;
370 self.peer_iter.on_failure(&data.peer);
371 if let Some(ref remote) = remote
372 && let Some(state) = self.inner.peers_mut().get_mut(remote)
373 {
374 *state = PeerState::Failed;
375 }
376 return None;
377 }
378
379 self.stats.success += 1;
380 self.peer_iter
381 .on_success(&data.peer, &data.response.closer_nodes);
382
383 if let Some(token) = &data.response.token
384 && let Some(remote) = remote
385 {
386 self.inner
387 .add_verified(remote, token.to_vec(), Some(data.response.to.addr));
388 }
389 }
390 }
391
392 Some(data)
393 }
394
395 fn send(&self, peer: Peer) -> QueryEvent {
396 QueryEvent::Query {
397 command: self.cmd,
398 target: *self.target(),
399 value: self.value.clone(),
400 peer,
401 }
402 }
403
404 #[instrument(skip_all)]
405 fn poll(&mut self, now: Instant) -> Poll<Option<QueryEvent>> {
406 let pis = self.peer_iter.next(now);
407 match pis {
408 PeersIterState::Waiting(peer) => {
409 return if let Some(peer) = peer {
410 Poll::Ready(Some(self.send(peer)))
411 } else {
412 Poll::Pending
413 };
414 }
415 PeersIterState::WaitingAtCapacity => return Poll::Pending,
416 PeersIterState::Finished => {}
417 };
418
419 let out = match poll_commit(&mut self.commit, self.id) {
420 Poll::Ready(op) => Poll::Ready(op.map(QueryEvent::Commit)),
421 Poll::Pending => Poll::Pending,
422 };
423
424 trace!("Query::poll result {out:?}");
425 out
426 }
427
428 pub fn get_result(&self) -> QueryResult {
430 QueryResult {
431 peers: self.inner.peers_iter(),
432 query_id: self.id,
433 stats: self.stats.clone(),
434 cmd: self.cmd,
435 closest_replies: self.closest_replies.clone(),
436 }
437 }
438}
439
440#[instrument(skip_all)]
442fn poll_commit(commit: &mut Commit, query_id: QueryId) -> Poll<Option<CommitEvent>> {
443 use crate::Progress as P;
444 use Commit as C;
445 trace!("polling commit: {commit:?}");
446 match commit {
447 C::No | C::Auto(P::Done) | C::Custom(P::Done) => Poll::Ready(None),
448 C::Auto(P::BeforeStart) => {
449 let (tx, rx) = mpsc::channel(DEFAULT_COMMIT_CHANNEL_SIZE);
450 *commit = C::Auto(P::Sending((rx, Default::default())));
451 Poll::Ready(Some(CommitEvent::AutoStart((tx, query_id))))
453 }
454 C::Custom(P::BeforeStart) => {
455 let (tx, rx) = mpsc::channel(DEFAULT_COMMIT_CHANNEL_SIZE);
456 *commit = C::Custom(P::Sending((rx, Default::default())));
457 Poll::Ready(Some(CommitEvent::CustomStart((tx, query_id))))
458 }
459 C::Auto(P::Sending(_)) => Poll::Pending,
460 C::Custom(P::Sending((rx, _tids))) => {
461 let mut out = vec![];
462 while let Some(e) = rx.try_next().ok().flatten() {
463 out.push(e)
464 }
465 if !out.is_empty() {
466 let sending_done = CommitEvent::SendRequests((out, query_id));
467 return Poll::Ready(Some(sending_done));
468 }
469 Poll::Pending
470 }
471 C::Custom(P::AwaitingReplies(ids_waiting_on)) => {
472 debug!("Custom still waiting on [{}] replies", ids_waiting_on.len());
473 Poll::Pending
474 }
475 C::Auto(P::AwaitingReplies(ids_waiting_on)) => {
476 debug!("Auto still waiting on [{}] replies", ids_waiting_on.len());
477 Poll::Pending
478 }
479 }
480}
481
482#[derive(Debug)]
483pub enum QueryEvent {
484 Commit(CommitEvent),
485 Query {
486 peer: Peer,
487 command: Command,
488 target: IdBytes,
489 value: Option<Vec<u8>>,
490 },
491 RemoveNode {
492 id: IdBytes,
493 },
494 MissingRoundtripToken {
495 peer: Peer,
496 },
497}
498
499#[derive(Debug, Clone)]
501pub struct CommandQuery {
502 pub tid: u16,
504 pub command: usize,
506 pub peer: Peer,
508 pub target: IdBytes,
510 pub value: Option<Vec<u8>>,
512}
513
514impl CommandQuery {
515 pub fn into_response_with_error(self, err: impl Into<usize>) -> CommandQueryResponse {
516 let mut resp = CommandQueryResponse::from(self);
517 resp.msg.error = err.into();
518 resp
519 }
520}
521
522#[derive(Debug, Clone)]
524pub struct CommandQueryResponse {
525 pub msg: ReplyMsgData,
526 pub peer: Peer,
527 pub target: IdBytes,
528}
529
530impl From<CommandQuery> for CommandQueryResponse {
531 fn from(q: CommandQuery) -> Self {
532 let msg = ReplyMsgData {
533 tid: q.tid,
534 to: q.peer.clone(),
535 id: None,
536 token: None,
537 closer_nodes: vec![],
538 value: q.value,
539 error: 0,
540 };
541
542 Self {
543 msg,
544 peer: q.peer,
545 target: q.target,
546 }
547 }
548}
549#[derive(Debug)]
551pub struct QueryResult {
552 pub query_id: QueryId,
554 pub cmd: Command,
556 pub stats: QueryStats,
558 pub peers: Vec<(PeerId, PeerState)>,
560 pub closest_replies: Vec<Arc<InResponse>>,
562}
563
564#[derive(Clone, Debug, PartialEq, Eq)]
566pub struct QueryStats {
567 requests: u32,
568 success: u32,
569 failure: u32,
570 start: Option<Instant>,
571 end: Option<Instant>,
572}
573
574impl QueryStats {
575 pub fn empty() -> Self {
576 QueryStats {
577 requests: 0,
578 success: 0,
579 failure: 0,
580 start: None,
581 end: None,
582 }
583 }
584
585 pub fn num_requests(&self) -> u32 {
587 self.requests
588 }
589
590 pub fn num_successes(&self) -> u32 {
592 self.success
593 }
594
595 pub fn num_failures(&self) -> u32 {
597 self.failure
598 }
599
600 pub fn num_pending(&self) -> u32 {
605 self.requests - (self.success + self.failure)
606 }
607
608 pub fn duration(&self) -> Option<Duration> {
616 if let Some(s) = self.start {
617 if let Some(e) = self.end {
618 Some(e - s)
619 } else {
620 Some(Instant::now() - s)
621 }
622 } else {
623 None
624 }
625 }
626
627 pub fn merge(self, other: &QueryStats) -> Self {
634 QueryStats {
635 requests: self.requests + other.requests,
636 success: self.success + other.success,
637 failure: self.failure + other.failure,
638 start: match (self.start, other.start) {
639 (Some(a), Some(b)) => Some(std::cmp::min(a, b)),
640 (a, b) => a.or(b),
641 },
642 end: std::cmp::max(self.end, other.end),
643 }
644 }
645}
646#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
648pub struct QueryId(pub usize);
649
650impl Display for QueryId {
651 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
652 write!(f, "QueryId({})", self.0)
653 }
654}