1use std::collections::VecDeque;
5
6use anyhow::{Result, bail};
7use rustc_hash::FxHashMap;
8use uuid::Uuid;
9
10use crate::common::handoff::{HandoffId, HandoffTransferTiming};
11use crate::common::protocols::DirectRequest;
12
13#[allow(dead_code)]
14pub enum SchedulerCommand {
15 Submit(DirectRequest),
16 CancelRequest {
22 request_id: Uuid,
23 },
24 SubmitHandoffPrefill {
25 handoff_id: HandoffId,
26 request: DirectRequest,
27 },
28 ReleaseSource {
29 handoff_id: HandoffId,
30 },
31 CancelSource {
32 handoff_id: HandoffId,
33 },
34 ReserveDestination {
35 handoff_id: HandoffId,
36 request: DirectRequest,
37 },
38 ActivateDestination {
39 handoff_id: HandoffId,
40 },
41 CancelDestination {
42 handoff_id: HandoffId,
43 },
44}
45
46#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47pub enum SchedulerCommandResult {
48 Submitted(Uuid),
49 DestinationAccepted { request_id: Uuid },
50 Applied,
51 Noop,
52}
53
54#[derive(Clone, Copy, Debug, PartialEq)]
55pub enum SchedulerLifecycleEvent {
56 SourceHeld {
57 handoff_id: HandoffId,
58 request_id: Uuid,
59 transfer_timing: HandoffTransferTiming,
60 },
61 DestinationReserved {
62 handoff_id: HandoffId,
63 request_id: Uuid,
64 transferable_prompt_tokens: usize,
65 },
66}
67
68impl SchedulerLifecycleEvent {
69 pub fn handoff_id(&self) -> HandoffId {
70 match *self {
71 Self::SourceHeld { handoff_id, .. } | Self::DestinationReserved { handoff_id, .. } => {
72 handoff_id
73 }
74 }
75 }
76}
77
78#[derive(Debug)]
79pub struct SchedulerCommandEffects {
80 pub result: SchedulerCommandResult,
81 pub lifecycle_events: Vec<SchedulerLifecycleEvent>,
82 pub kv_events: Vec<dynamo_kv_router::protocols::RouterEvent>,
83}
84
85impl SchedulerCommandEffects {
86 pub(crate) fn new(result: SchedulerCommandResult) -> Self {
87 Self {
88 result,
89 lifecycle_events: Vec::new(),
90 kv_events: Vec::new(),
91 }
92 }
93}
94
95pub(crate) enum SourceCompletion<T> {
96 Release(T),
97 Held { handoff_id: HandoffId },
98}
99
100pub(crate) enum RemovedSource<T> {
101 Held(T),
102 Pending { request_id: Uuid },
103 Missing,
104}
105
106pub(crate) struct SourceHolds<T> {
107 pending_by_request: FxHashMap<Uuid, HandoffId>,
108 pending_by_handoff: FxHashMap<HandoffId, Uuid>,
109 held_prefills: FxHashMap<HandoffId, (Uuid, T)>,
110 held_by_request: FxHashMap<Uuid, HandoffId>,
111}
112
113impl<T> Default for SourceHolds<T> {
114 fn default() -> Self {
115 Self {
116 pending_by_request: FxHashMap::default(),
117 pending_by_handoff: FxHashMap::default(),
118 held_prefills: FxHashMap::default(),
119 held_by_request: FxHashMap::default(),
120 }
121 }
122}
123
124impl<T> SourceHolds<T> {
125 #[allow(dead_code)]
126 pub(crate) fn is_empty(&self) -> bool {
127 debug_assert_eq!(self.pending_by_request.len(), self.pending_by_handoff.len());
128 debug_assert_eq!(self.held_prefills.len(), self.held_by_request.len());
129 self.pending_by_request.is_empty()
130 && self.held_prefills.is_empty()
131 && self.held_by_request.is_empty()
132 }
133
134 pub(crate) fn register(&mut self, request_id: Uuid, handoff_id: HandoffId) -> Result<()> {
135 if self.contains_request(request_id) {
136 bail!("source hold already active for request {request_id}");
137 }
138 if self.pending_by_handoff.contains_key(&handoff_id)
139 || self.held_prefills.contains_key(&handoff_id)
140 {
141 bail!("handoff {handoff_id:?} is already active");
142 }
143
144 self.pending_by_request.insert(request_id, handoff_id);
145 self.pending_by_handoff.insert(handoff_id, request_id);
146 Ok(())
147 }
148
149 pub(crate) fn contains_request(&self, request_id: Uuid) -> bool {
150 self.pending_by_request.contains_key(&request_id)
151 || self.held_by_request.contains_key(&request_id)
152 }
153
154 pub(crate) fn complete_source(&mut self, request_id: Uuid, payload: T) -> SourceCompletion<T> {
155 let Some(handoff_id) = self.pending_by_request.remove(&request_id) else {
156 return SourceCompletion::Release(payload);
157 };
158
159 let registered_request = self
160 .pending_by_handoff
161 .remove(&handoff_id)
162 .expect("validated source-hold registration must be bidirectional");
163 debug_assert_eq!(registered_request, request_id);
164
165 let previous = self.held_prefills.insert(handoff_id, (request_id, payload));
166 debug_assert!(previous.is_none());
167 let previous = self.held_by_request.insert(request_id, handoff_id);
168 debug_assert!(previous.is_none());
169 SourceCompletion::Held { handoff_id }
170 }
171
172 pub(crate) fn remove(&mut self, handoff_id: HandoffId) -> RemovedSource<T> {
173 if let Some((request_id, payload)) = self.held_prefills.remove(&handoff_id) {
174 let removed = self.held_by_request.remove(&request_id);
175 debug_assert_eq!(removed, Some(handoff_id));
176 return RemovedSource::Held(payload);
177 }
178
179 let Some(request_id) = self.pending_by_handoff.remove(&handoff_id) else {
180 return RemovedSource::Missing;
181 };
182 let removed = self.pending_by_request.remove(&request_id);
183 debug_assert_eq!(removed, Some(handoff_id));
184 RemovedSource::Pending { request_id }
185 }
186
187 pub(crate) fn remove_request(&mut self, request_id: Uuid) {
188 let Some(handoff_id) = self.pending_by_request.remove(&request_id) else {
189 return;
190 };
191 let removed = self.pending_by_handoff.remove(&handoff_id);
192 debug_assert_eq!(removed, Some(request_id));
193 }
194
195 #[cfg(test)]
196 pub(crate) fn is_held(&self, handoff_id: HandoffId) -> bool {
197 self.held_prefills.contains_key(&handoff_id)
198 }
199
200 #[cfg(test)]
201 pub(crate) fn is_registered(&self, handoff_id: HandoffId) -> bool {
202 self.pending_by_handoff.contains_key(&handoff_id)
203 }
204}
205
206pub(crate) struct DestinationHolds<T> {
207 by_handoff: FxHashMap<HandoffId, (Uuid, T)>,
208 by_request: FxHashMap<Uuid, HandoffId>,
209}
210
211#[derive(Default)]
212pub(crate) struct ActiveHandoffRequests {
213 by_handoff: FxHashMap<HandoffId, Uuid>,
214 by_request: FxHashMap<Uuid, HandoffId>,
215}
216
217impl ActiveHandoffRequests {
218 pub(crate) fn insert(&mut self, handoff_id: HandoffId, request_id: Uuid) {
219 let previous = self.by_handoff.insert(handoff_id, request_id);
220 debug_assert!(previous.is_none());
221 let previous = self.by_request.insert(request_id, handoff_id);
222 debug_assert!(previous.is_none());
223 }
224
225 pub(crate) fn remove_handoff(&mut self, handoff_id: HandoffId) -> Option<Uuid> {
226 let request_id = self.by_handoff.remove(&handoff_id)?;
227 let removed = self.by_request.remove(&request_id);
228 debug_assert_eq!(removed, Some(handoff_id));
229 Some(request_id)
230 }
231
232 pub(crate) fn remove_request(&mut self, request_id: Uuid) -> Option<HandoffId> {
233 let handoff_id = self.by_request.remove(&request_id)?;
234 let removed = self.by_handoff.remove(&handoff_id);
235 debug_assert_eq!(removed, Some(request_id));
236 Some(handoff_id)
237 }
238
239 pub(crate) fn contains_request(&self, request_id: Uuid) -> bool {
240 self.by_request.contains_key(&request_id)
241 }
242
243 pub(crate) fn contains_handoff(&self, handoff_id: HandoffId) -> bool {
244 self.by_handoff.contains_key(&handoff_id)
245 }
246
247 pub(crate) fn is_empty(&self) -> bool {
248 debug_assert_eq!(self.by_handoff.len(), self.by_request.len());
249 self.by_handoff.is_empty()
250 }
251}
252
253pub(crate) struct PendingDestinations<T> {
254 fifo: VecDeque<HandoffId>,
255 by_handoff: FxHashMap<HandoffId, PendingDestination<T>>,
256 by_request: FxHashMap<Uuid, HandoffId>,
257}
258
259struct PendingDestination<T> {
260 request_id: Uuid,
261 payload: T,
262 last_attempt_generation: Option<u64>,
263}
264
265impl<T> Default for PendingDestinations<T> {
266 fn default() -> Self {
267 Self {
268 fifo: VecDeque::new(),
269 by_handoff: FxHashMap::default(),
270 by_request: FxHashMap::default(),
271 }
272 }
273}
274
275impl<T> PendingDestinations<T> {
276 pub(crate) fn validate(&self, request_id: Uuid, handoff_id: HandoffId) -> Result<()> {
277 if self.by_request.contains_key(&request_id) {
278 bail!("destination request {request_id} is already pending");
279 }
280 if self.by_handoff.contains_key(&handoff_id) {
281 bail!("destination handoff {handoff_id:?} is already pending");
282 }
283 Ok(())
284 }
285
286 pub(crate) fn insert(&mut self, request_id: Uuid, handoff_id: HandoffId, payload: T) {
287 debug_assert!(self.validate(request_id, handoff_id).is_ok());
288 self.fifo.push_back(handoff_id);
289 let previous = self.by_handoff.insert(
290 handoff_id,
291 PendingDestination {
292 request_id,
293 payload,
294 last_attempt_generation: None,
295 },
296 );
297 debug_assert!(previous.is_none());
298 let previous = self.by_request.insert(request_id, handoff_id);
299 debug_assert!(previous.is_none());
300 }
301
302 pub(crate) fn front_due(&mut self, generation: u64) -> Option<(HandoffId, Uuid, &T)> {
303 self.normalize_front();
304 let handoff_id = *self.fifo.front()?;
305 let pending = self
306 .by_handoff
307 .get(&handoff_id)
308 .expect("normalized pending destination must exist");
309 if pending.last_attempt_generation == Some(generation) {
310 return None;
311 }
312 Some((handoff_id, pending.request_id, &pending.payload))
313 }
314
315 pub(crate) fn front_due_mut(&mut self, generation: u64) -> Option<(HandoffId, Uuid, &mut T)> {
316 self.normalize_front();
317 let handoff_id = *self.fifo.front()?;
318 let pending = self
319 .by_handoff
320 .get_mut(&handoff_id)
321 .expect("normalized pending destination must exist");
322 if pending.last_attempt_generation == Some(generation) {
323 return None;
324 }
325 Some((handoff_id, pending.request_id, &mut pending.payload))
326 }
327
328 pub(crate) fn mark_front_attempted(&mut self, generation: u64) {
329 self.normalize_front();
330 let Some(handoff_id) = self.fifo.front() else {
331 return;
332 };
333 let pending = self
334 .by_handoff
335 .get_mut(handoff_id)
336 .expect("normalized pending destination must exist");
337 pending.last_attempt_generation = Some(generation);
338 }
339
340 pub(crate) fn pop_front(&mut self) -> Option<(HandoffId, Uuid, T)> {
341 self.normalize_front();
342 let handoff_id = self.fifo.pop_front()?;
343 let pending = self
344 .by_handoff
345 .remove(&handoff_id)
346 .expect("normalized pending destination must exist");
347 let removed = self.by_request.remove(&pending.request_id);
348 debug_assert_eq!(removed, Some(handoff_id));
349 self.compact_if_sparse();
350 Some((handoff_id, pending.request_id, pending.payload))
351 }
352
353 pub(crate) fn remove(&mut self, handoff_id: HandoffId) -> Option<(Uuid, T)> {
354 let pending = self.by_handoff.remove(&handoff_id)?;
355 let removed = self.by_request.remove(&pending.request_id);
356 debug_assert_eq!(removed, Some(handoff_id));
357 self.compact_if_sparse();
358 Some((pending.request_id, pending.payload))
359 }
360
361 pub(crate) fn contains_request(&self, request_id: Uuid) -> bool {
362 self.by_request.contains_key(&request_id)
363 }
364
365 #[cfg(test)]
366 pub(crate) fn contains_handoff(&self, handoff_id: HandoffId) -> bool {
367 self.by_handoff.contains_key(&handoff_id)
368 }
369
370 pub(crate) fn is_empty(&self) -> bool {
371 debug_assert_eq!(self.by_handoff.len(), self.by_request.len());
372 self.by_handoff.is_empty()
373 }
374
375 pub(crate) fn has_pending(&self) -> bool {
376 !self.by_handoff.is_empty()
377 }
378
379 pub(crate) fn len(&self) -> usize {
380 self.by_handoff.len()
381 }
382
383 pub(crate) fn payloads(&self) -> impl Iterator<Item = &T> {
384 self.by_handoff.values().map(|pending| &pending.payload)
385 }
386
387 fn normalize_front(&mut self) {
388 while self
389 .fifo
390 .front()
391 .is_some_and(|handoff_id| !self.by_handoff.contains_key(handoff_id))
392 {
393 self.fifo.pop_front();
394 }
395 }
396
397 fn compact_if_sparse(&mut self) {
398 if self.fifo.len() <= self.by_handoff.len().saturating_mul(2) {
399 return;
400 }
401 self.fifo
402 .retain(|handoff_id| self.by_handoff.contains_key(handoff_id));
403 }
404}
405
406impl<T> Default for DestinationHolds<T> {
407 fn default() -> Self {
408 Self {
409 by_handoff: FxHashMap::default(),
410 by_request: FxHashMap::default(),
411 }
412 }
413}
414
415impl<T> DestinationHolds<T> {
416 pub(crate) fn validate(&self, request_id: Uuid, handoff_id: HandoffId) -> Result<()> {
417 if self.by_request.contains_key(&request_id) {
418 bail!("destination reservation already exists for request {request_id}");
419 }
420 if self.by_handoff.contains_key(&handoff_id) {
421 bail!("destination handoff {handoff_id:?} is already active");
422 }
423 Ok(())
424 }
425
426 pub(crate) fn contains_request(&self, request_id: Uuid) -> bool {
427 self.by_request.contains_key(&request_id)
428 }
429
430 pub(crate) fn insert(&mut self, request_id: Uuid, handoff_id: HandoffId, payload: T) {
431 debug_assert!(self.validate(request_id, handoff_id).is_ok());
432 let previous = self.by_handoff.insert(handoff_id, (request_id, payload));
433 debug_assert!(previous.is_none());
434 let previous = self.by_request.insert(request_id, handoff_id);
435 debug_assert!(previous.is_none());
436 }
437
438 pub(crate) fn remove(&mut self, handoff_id: HandoffId) -> Option<(Uuid, T)> {
439 let (request_id, payload) = self.by_handoff.remove(&handoff_id)?;
440 let removed = self.by_request.remove(&request_id);
441 debug_assert_eq!(removed, Some(handoff_id));
442 Some((request_id, payload))
443 }
444
445 #[allow(dead_code)]
446 pub(crate) fn is_empty(&self) -> bool {
447 debug_assert_eq!(self.by_handoff.len(), self.by_request.len());
448 self.by_handoff.is_empty()
449 }
450
451 pub(crate) fn len(&self) -> usize {
452 self.by_handoff.len()
453 }
454
455 pub(crate) fn payloads(&self) -> impl Iterator<Item = &T> {
456 self.by_handoff.values().map(|(_, payload)| payload)
457 }
458
459 #[cfg(test)]
460 pub(crate) fn contains(&self, handoff_id: HandoffId) -> bool {
461 self.by_handoff.contains_key(&handoff_id)
462 }
463
464 #[cfg(test)]
465 pub(crate) fn get(&self, handoff_id: HandoffId) -> Option<&T> {
466 self.by_handoff.get(&handoff_id).map(|(_, payload)| payload)
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473
474 #[test]
475 fn completion_atomically_moves_registered_payload_to_held() {
476 let request_id = Uuid::from_u128(1);
477 let handoff_id = HandoffId::from(Uuid::from_u128(2));
478 let mut holds = SourceHolds::default();
479
480 holds.register(request_id, handoff_id).unwrap();
481 assert!(holds.is_registered(handoff_id));
482
483 let completion = holds.complete_source(request_id, "payload");
484 assert!(matches!(
485 completion,
486 SourceCompletion::Held { handoff_id: held } if held == handoff_id
487 ));
488 assert!(!holds.is_registered(handoff_id));
489 assert!(holds.is_held(handoff_id));
490 assert!(
491 holds
492 .register(request_id, HandoffId::from(Uuid::from_u128(7)))
493 .is_err()
494 );
495 assert!(matches!(
496 holds.remove(handoff_id),
497 RemovedSource::Held("payload")
498 ));
499 assert!(matches!(holds.remove(handoff_id), RemovedSource::Missing));
500 }
501
502 #[test]
503 fn active_ids_are_rejected_but_released_ids_can_be_reused() {
504 let request_id = Uuid::from_u128(3);
505 let handoff_id = HandoffId::from(Uuid::from_u128(4));
506 let mut holds = SourceHolds::<()>::default();
507
508 holds.register(request_id, handoff_id).unwrap();
509 assert!(holds.register(Uuid::from_u128(5), handoff_id).is_err());
510 assert!(holds.register(request_id, HandoffId::new()).is_err());
511 assert!(matches!(
512 holds.remove(handoff_id),
513 RemovedSource::Pending { request_id: pending } if pending == request_id
514 ));
515
516 holds.register(Uuid::from_u128(5), handoff_id).unwrap();
517 }
518
519 #[test]
520 fn unregistered_completion_releases_payload() {
521 let mut holds = SourceHolds::default();
522 let payload = String::from("payload");
523
524 assert!(matches!(
525 holds.complete_source(Uuid::from_u128(6), payload),
526 SourceCompletion::Release(value) if value == "payload"
527 ));
528 }
529
530 #[test]
531 fn pending_destination_preserves_fifo_after_head_cancellation() {
532 let first = HandoffId::from(Uuid::from_u128(10));
533 let second = HandoffId::from(Uuid::from_u128(11));
534 let mut pending = PendingDestinations::default();
535 pending.insert(Uuid::from_u128(20), first, "first");
536 pending.insert(Uuid::from_u128(21), second, "second");
537
538 assert_eq!(pending.remove(first), Some((Uuid::from_u128(20), "first")));
539 let (handoff_id, request_id, payload) = pending.front_due(0).unwrap();
540 assert_eq!(handoff_id, second);
541 assert_eq!(request_id, Uuid::from_u128(21));
542 assert_eq!(*payload, "second");
543
544 pending.mark_front_attempted(0);
545 assert!(pending.front_due(0).is_none());
546 assert!(pending.front_due(1).is_some());
547 }
548
549 #[test]
550 fn pending_destination_indexes_reject_conflicts_and_compact_tombstones() {
551 let mut pending = PendingDestinations::default();
552 let handoffs = (0..8)
553 .map(|index| HandoffId::from(Uuid::from_u128(100 + index)))
554 .collect::<Vec<_>>();
555 for (index, handoff_id) in handoffs.iter().copied().enumerate() {
556 pending.insert(Uuid::from_u128(200 + index as u128), handoff_id, index);
557 }
558 assert!(
559 pending
560 .validate(Uuid::from_u128(200), HandoffId::new())
561 .is_err()
562 );
563 assert!(pending.validate(Uuid::new_v4(), handoffs[0]).is_err());
564
565 for handoff_id in handoffs.iter().take(6) {
566 assert!(pending.remove(*handoff_id).is_some());
567 }
568 assert!(pending.fifo.len() <= pending.by_handoff.len() * 2);
569 let (handoff_id, request_id, payload) = pending.front_due(0).unwrap();
570 assert_eq!(handoff_id, handoffs[6]);
571 assert_eq!(request_id, Uuid::from_u128(206));
572 assert_eq!(*payload, 6);
573 assert_eq!(pending.pop_front(), Some((handoffs[6], request_id, 6)));
574 assert_eq!(pending.pop_front().map(|entry| entry.0), Some(handoffs[7]));
575 assert!(pending.is_empty());
576 }
577}