1impl<'de> Deserialize<'de> for SessionState {
2 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3 where
4 D: serde::Deserializer<'de>,
5 {
6 let raw = SessionStateSerde::deserialize(deserializer)?;
7 let mut session = Self {
8 sid: raw.sid,
9 roles: raw.roles,
10 role_ids: BTreeMap::new(),
11 local_types: raw.local_types,
12 buffers: raw.buffers,
13 edge_lookup: BTreeMap::new(),
14 handler_ids: BTreeMap::new(),
15 handlers_by_id: Vec::new(),
16 edge_handler_lookup: BTreeMap::new(),
17 default_handler_id: None,
18 label_ids: BTreeMap::new(),
19 labels_by_id: Vec::new(),
20 branch_lookup: BTreeMap::new(),
21 auth_leaves: raw.auth_leaves,
22 auth_trees: raw.auth_trees,
23 auth_roots: raw.auth_roots,
24 edge_handlers: raw.edge_handlers,
25 default_handler: raw.default_handler,
26 edge_traces: raw.edge_traces,
27 status: raw.status,
28 epoch: raw.epoch,
29 ownership: raw.ownership,
30 };
31 session.rebuild_derived_indexes();
32 Ok(session)
33 }
34}
35
36#[derive(Debug, Default, Serialize, Deserialize)]
41pub struct SessionStore {
42 sessions: BTreeMap<SessionId, SessionState>,
43 #[serde(default)]
44 archived_closed: Vec<ClosedSessionSummary>,
45 next_id: SessionId,
46}
47
48impl SessionStore {
49 fn session_mut_or_error(
50 &mut self,
51 sid: SessionId,
52 ) -> Result<&mut SessionState, OwnershipError> {
53 self.sessions
54 .get_mut(&sid)
55 .ok_or(OwnershipError::SessionNotFound { session_id: sid })
56 }
57
58 fn terminal_error(session: &SessionState) -> Option<OwnershipError> {
59 session
60 .ownership
61 .terminal_reason
62 .clone()
63 .map(|reason| OwnershipError::Terminal {
64 session_id: session.sid,
65 reason,
66 })
67 }
68
69 fn ensure_mutable_ownership(session: &SessionState) -> Result<(), OwnershipError> {
70 if let Some(err) = Self::terminal_error(session) {
71 return Err(err);
72 }
73 Ok(())
74 }
75
76 fn validate_current_owner(
77 session: &SessionState,
78 capability: &OwnershipCapability,
79 ) -> Result<(), OwnershipError> {
80 Self::ensure_mutable_ownership(session)?;
81 let Some(current) = session.ownership.current.as_ref() else {
82 return Err(OwnershipError::Unclaimed {
83 session_id: session.sid,
84 });
85 };
86 if current.owner_id != capability.owner_id || current.generation != capability.generation {
87 return Err(OwnershipError::StaleCapability {
88 session_id: session.sid,
89 owner_id: capability.owner_id.clone(),
90 expected_generation: capability.generation,
91 actual_generation: current.generation,
92 });
93 }
94 Ok(())
95 }
96
97 fn require_session_scope(
98 session: &SessionState,
99 capability: &OwnershipCapability,
100 ) -> Result<(), OwnershipError> {
101 Self::validate_current_owner(session, capability)?;
102 let Some(current) = session.ownership.current.as_ref() else {
103 return Err(OwnershipError::Unclaimed {
104 session_id: session.sid,
105 });
106 };
107 if !current.scope.allows_session_mutation() {
108 return Err(OwnershipError::ScopeViolation {
109 session_id: session.sid,
110 owner_id: current.owner_id.clone(),
111 required: OwnershipScope::Session,
112 actual: current.scope.clone(),
113 });
114 }
115 Ok(())
116 }
117
118 fn next_witness_id(session: &mut SessionState) -> AuthorityWitnessId {
119 let witness_id = session.ownership.next_witness_id;
120 session.ownership.next_witness_id = session.ownership.next_witness_id.saturating_add(1);
121 witness_id
122 }
123
124 fn push_authority_audit(
125 session: &mut SessionState,
126 artifact: AuthorityArtifact,
127 event: AuthorityAuditEvent,
128 reason: Option<String>,
129 ) {
130 session.ownership.audit_log.push(AuthorityAuditRecord {
131 tick: None,
132 artifact,
133 event,
134 reason,
135 });
136 }
137
138 #[must_use]
140 pub fn new() -> Self {
141 Self::default()
142 }
143
144 #[allow(clippy::needless_pass_by_value)]
148 pub fn open_with_sid(
149 &mut self,
150 sid: SessionId,
151 roles: Vec<String>,
152 buffer_config: &BufferConfig,
153 initial_types: &BTreeMap<String, LocalTypeR>,
154 ) -> SessionId {
155 let plan = SessionOpenPlan::new(&roles, initial_types);
156 self.open_with_sid_from_plan(sid, &plan, buffer_config)
157 }
158
159 pub fn open_with_sid_from_plan(
161 &mut self,
162 sid: SessionId,
163 plan: &SessionOpenPlan,
164 buffer_config: &BufferConfig,
165 ) -> SessionId {
166 let state = SessionState::from_open_plan(sid, plan, buffer_config);
167 self.sessions.insert(sid, state);
168 self.next_id = self.next_id.max(sid.saturating_add(1));
169 sid
170 }
171
172 #[allow(clippy::needless_pass_by_value)]
176 pub fn open(
177 &mut self,
178 roles: Vec<String>,
179 buffer_config: &BufferConfig,
180 initial_types: &BTreeMap<String, LocalTypeR>,
181 ) -> SessionId {
182 let sid = self.next_id;
183 self.open_with_sid(sid, roles, buffer_config, initial_types)
184 }
185
186 #[must_use]
188 pub fn next_session_id(&self) -> SessionId {
189 self.next_id
190 }
191
192 #[must_use]
198 pub fn lookup_type(&self, ep: &Endpoint) -> Option<&LocalTypeR> {
199 self.sessions
200 .get(&ep.sid)?
201 .local_types
202 .get(ep)
203 .map(|e| &e.current)
204 }
205
206 pub fn update_type(&mut self, ep: &Endpoint, new_type: LocalTypeR) {
210 if let Some(session) = self.sessions.get_mut(&ep.sid) {
211 if let Some(entry) = session.local_types.get_mut(ep) {
212 entry.current = new_type;
213 }
214 session.refresh_endpoint_branch_lookup(ep);
215 }
216 }
217
218 pub fn update_original(&mut self, ep: &Endpoint, new_original: LocalTypeR) {
220 if let Some(session) = self.sessions.get_mut(&ep.sid) {
221 if let Some(entry) = session.local_types.get_mut(ep) {
222 entry.original = new_original;
223 }
224 }
225 }
226
227 #[must_use]
229 pub fn original_type(&self, ep: &Endpoint) -> Option<&LocalTypeR> {
230 self.sessions
231 .get(&ep.sid)?
232 .local_types
233 .get(ep)
234 .map(|e| &e.original)
235 }
236
237 pub fn remove_type(&mut self, ep: &Endpoint) {
239 if let Some(session) = self.sessions.get_mut(&ep.sid) {
240 session.local_types.remove(ep);
241 session.branch_lookup.remove(ep);
242 }
243 }
244
245 #[must_use]
249 pub fn get(&self, sid: SessionId) -> Option<&SessionState> {
250 self.sessions.get(&sid)
251 }
252
253 pub fn get_mut(&mut self, sid: SessionId) -> Option<&mut SessionState> {
255 self.sessions.get_mut(&sid)
256 }
257
258 #[must_use]
260 pub fn current_ownership(&self, sid: SessionId) -> Option<&OwnershipCapability> {
261 self.sessions.get(&sid)?.ownership.current.as_ref()
262 }
263
264 pub fn validate_ownership_capability(
270 &self,
271 capability: &OwnershipCapability,
272 ) -> Result<(), OwnershipError> {
273 let session = self
274 .sessions
275 .get(&capability.session_id)
276 .ok_or(OwnershipError::SessionNotFound {
277 session_id: capability.session_id,
278 })?;
279 Self::validate_current_owner(session, capability)
280 }
281
282 #[must_use]
284 pub fn authority_audit_log(&self, sid: SessionId) -> Option<&[AuthorityAuditRecord]> {
285 Some(self.sessions.get(&sid)?.ownership.audit_log.as_slice())
286 }
287
288 pub fn claim_ownership(
294 &mut self,
295 sid: SessionId,
296 owner_id: impl Into<FragmentOwnerId>,
297 scope: OwnershipScope,
298 ) -> Result<OwnershipCapability, OwnershipError> {
299 let session = self.session_mut_or_error(sid)?;
300 Self::ensure_mutable_ownership(session)?;
301 if let Some(current) = session.ownership.current.as_ref() {
302 return Err(OwnershipError::AlreadyClaimed {
303 session_id: sid,
304 current_owner_id: current.owner_id.clone(),
305 });
306 }
307 if let Some(pending) = session.ownership.pending_transfer.as_ref() {
308 return Err(OwnershipError::TransferPending {
309 session_id: sid,
310 claim_id: pending.receipt.claim_id,
311 });
312 }
313 let capability = OwnershipCapability {
314 session_id: sid,
315 owner_id: owner_id.into(),
316 generation: 0,
317 scope,
318 };
319 session.ownership.current = Some(capability.clone());
320 Ok(capability)
321 }
322
323 pub fn release_ownership(
329 &mut self,
330 capability: &OwnershipCapability,
331 ) -> Result<(), OwnershipError> {
332 let session = self.session_mut_or_error(capability.session_id)?;
333 Self::validate_current_owner(session, capability)?;
334 if let Some(pending) = session.ownership.pending_transfer.as_ref() {
335 return Err(OwnershipError::TransferPending {
336 session_id: capability.session_id,
337 claim_id: pending.receipt.claim_id,
338 });
339 }
340 session.ownership.current = None;
341 Ok(())
342 }
343
344 pub fn begin_ownership_transfer(
350 &mut self,
351 capability: &OwnershipCapability,
352 new_owner_id: impl Into<FragmentOwnerId>,
353 new_scope: OwnershipScope,
354 ) -> Result<OwnershipReceipt, OwnershipError> {
355 let session = self.session_mut_or_error(capability.session_id)?;
356 Self::validate_current_owner(session, capability)?;
357 if let Some(pending) = session.ownership.pending_transfer.as_ref() {
358 return Err(OwnershipError::TransferPending {
359 session_id: capability.session_id,
360 claim_id: pending.receipt.claim_id,
361 });
362 }
363 let claim_id = session.ownership.next_claim_id;
364 session.ownership.next_claim_id = session.ownership.next_claim_id.saturating_add(1);
365 let receipt = OwnershipReceipt {
366 session_id: capability.session_id,
367 claim_id,
368 from_owner_id: capability.owner_id.clone(),
369 from_generation: capability.generation,
370 to_owner_id: new_owner_id.into(),
371 to_generation: capability.generation.saturating_add(1),
372 scope: new_scope,
373 };
374 session.ownership.pending_transfer = Some(PendingOwnershipTransfer {
375 receipt: receipt.clone(),
376 });
377 Ok(receipt)
378 }
379
380 pub fn commit_ownership_transfer(
386 &mut self,
387 receipt: &OwnershipReceipt,
388 ) -> Result<OwnershipCapability, OwnershipError> {
389 let session = self.session_mut_or_error(receipt.session_id)?;
390 Self::ensure_mutable_ownership(session)?;
391 let Some(current) = session.ownership.current.as_ref() else {
392 return Err(OwnershipError::Unclaimed {
393 session_id: receipt.session_id,
394 });
395 };
396 let Some(pending) = session.ownership.pending_transfer.as_ref() else {
397 return Err(OwnershipError::TransferNotPending {
398 session_id: receipt.session_id,
399 });
400 };
401 if pending.receipt != *receipt {
402 return Err(OwnershipError::ReceiptMismatch {
403 session_id: receipt.session_id,
404 claim_id: receipt.claim_id,
405 });
406 }
407 if current.owner_id != receipt.from_owner_id || current.generation != receipt.from_generation
408 {
409 return Err(OwnershipError::StaleCapability {
410 session_id: receipt.session_id,
411 owner_id: receipt.from_owner_id.clone(),
412 expected_generation: receipt.from_generation,
413 actual_generation: current.generation,
414 });
415 }
416 let capability = OwnershipCapability {
417 session_id: receipt.session_id,
418 owner_id: receipt.to_owner_id.clone(),
419 generation: receipt.to_generation,
420 scope: receipt.scope.clone(),
421 };
422 session.ownership.current = Some(capability.clone());
423 session.ownership.pending_transfer = None;
424 Ok(capability)
425 }
426
427 pub fn rollback_ownership_transfer(
433 &mut self,
434 receipt: &OwnershipReceipt,
435 ) -> Result<(), OwnershipError> {
436 let session = self.session_mut_or_error(receipt.session_id)?;
437 Self::ensure_mutable_ownership(session)?;
438 let Some(pending) = session.ownership.pending_transfer.as_ref() else {
439 return Err(OwnershipError::TransferNotPending {
440 session_id: receipt.session_id,
441 });
442 };
443 if pending.receipt != *receipt {
444 return Err(OwnershipError::ReceiptMismatch {
445 session_id: receipt.session_id,
446 claim_id: receipt.claim_id,
447 });
448 }
449 session.ownership.pending_transfer = None;
450 Ok(())
451 }
452
453 pub fn attenuate_ownership_scope(
459 &mut self,
460 capability: &OwnershipCapability,
461 new_scope: OwnershipScope,
462 ) -> Result<OwnershipCapability, OwnershipError> {
463 let session = self.session_mut_or_error(capability.session_id)?;
464 Self::validate_current_owner(session, capability)?;
465 if let Some(pending) = session.ownership.pending_transfer.as_ref() {
466 return Err(OwnershipError::TransferPending {
467 session_id: capability.session_id,
468 claim_id: pending.receipt.claim_id,
469 });
470 }
471 let next = OwnershipCapability {
472 session_id: capability.session_id,
473 owner_id: capability.owner_id.clone(),
474 generation: capability.generation.saturating_add(1),
475 scope: new_scope,
476 };
477 session.ownership.current = Some(next.clone());
478 Ok(next)
479 }
480
481 pub fn apply_owned_session_mutation(
487 &mut self,
488 capability: &OwnershipCapability,
489 mutation: SessionHostMutation,
490 ) -> Result<(), OwnershipError> {
491 let session = self.session_mut_or_error(capability.session_id)?;
492 Self::require_session_scope(session, capability)?;
493 match mutation {
494 SessionHostMutation::SetDefaultHandler { handler } => {
495 let handler_id = session.intern_handler_binding(&handler);
496 session.default_handler = handler;
497 session.default_handler_id = Some(handler_id);
498 }
499 SessionHostMutation::UpdateEdgeHandler { edge, handler } => {
500 let handler_id = session.intern_handler_binding(&handler);
501 if let Some(edge_key) = session.edge_key_for_roles(&edge.sender, &edge.receiver) {
502 session.edge_handler_lookup.insert(edge_key, handler_id);
503 }
504 session.edge_handlers.insert(edge, handler);
505 }
506 SessionHostMutation::UpdateTrace { edge, trace } => {
507 session.edge_traces.insert(edge, trace);
508 }
509 }
510 Ok(())
511 }
512
513 pub fn issue_readiness_witness(
519 &mut self,
520 capability: &OwnershipCapability,
521 predicate_ref: impl Into<String>,
522 ) -> Result<ReadinessWitness, OwnershipError> {
523 let session = self.session_mut_or_error(capability.session_id)?;
524 Self::require_session_scope(session, capability)?;
525 let witness = ReadinessWitness {
526 witness_id: Self::next_witness_id(session),
527 session_id: capability.session_id,
528 owner_id: capability.owner_id.clone(),
529 generation: capability.generation,
530 scope: capability.scope.clone(),
531 predicate_ref: predicate_ref.into(),
532 };
533 session
534 .ownership
535 .issued_readiness
536 .insert(witness.witness_id, witness.clone());
537 Self::push_authority_audit(
538 session,
539 AuthorityArtifact::Readiness(witness.clone()),
540 AuthorityAuditEvent::Issued,
541 None,
542 );
543 Ok(witness)
544 }
545
546 pub fn consume_readiness_witness(
552 &mut self,
553 capability: &OwnershipCapability,
554 witness: &ReadinessWitness,
555 ) -> Result<(), OwnershipError> {
556 let session = self.session_mut_or_error(capability.session_id)?;
557 Self::require_session_scope(session, capability)?;
558 if session.ownership.consumed_witnesses.contains(&witness.witness_id) {
559 Self::push_authority_audit(
560 session,
561 AuthorityArtifact::Readiness(witness.clone()),
562 AuthorityAuditEvent::Rejected,
563 Some("witness already consumed".to_string()),
564 );
565 return Err(OwnershipError::WitnessConsumed {
566 session_id: witness.session_id,
567 witness_id: witness.witness_id,
568 });
569 }
570 let Some(issued) = session.ownership.issued_readiness.get(&witness.witness_id) else {
571 Self::push_authority_audit(
572 session,
573 AuthorityArtifact::Readiness(witness.clone()),
574 AuthorityAuditEvent::Rejected,
575 Some("witness was never issued".to_string()),
576 );
577 return Err(OwnershipError::InvalidWitness {
578 session_id: capability.session_id,
579 witness_id: witness.witness_id,
580 reason: "witness was never issued".to_string(),
581 });
582 };
583 if issued != witness {
584 Self::push_authority_audit(
585 session,
586 AuthorityArtifact::Readiness(witness.clone()),
587 AuthorityAuditEvent::Rejected,
588 Some("witness payload mismatch".to_string()),
589 );
590 return Err(OwnershipError::InvalidWitness {
591 session_id: capability.session_id,
592 witness_id: witness.witness_id,
593 reason: "witness payload mismatch".to_string(),
594 });
595 }
596 if witness.session_id != capability.session_id
597 || witness.owner_id != capability.owner_id
598 || witness.generation != capability.generation
599 || witness.scope != capability.scope
600 {
601 Self::push_authority_audit(
602 session,
603 AuthorityArtifact::Readiness(witness.clone()),
604 AuthorityAuditEvent::Rejected,
605 Some("live ownership no longer matches witness".to_string()),
606 );
607 return Err(OwnershipError::InvalidWitness {
608 session_id: capability.session_id,
609 witness_id: witness.witness_id,
610 reason: "live ownership no longer matches witness".to_string(),
611 });
612 }
613 session.ownership.issued_readiness.remove(&witness.witness_id);
614 session
615 .ownership
616 .consumed_witnesses
617 .insert(witness.witness_id);
618 Self::push_authority_audit(
619 session,
620 AuthorityArtifact::Readiness(witness.clone()),
621 AuthorityAuditEvent::Consumed,
622 None,
623 );
624 Ok(())
625 }
626
627 pub fn mark_owner_died(
633 &mut self,
634 sid: SessionId,
635 owner_id: &str,
636 ) -> Result<CancellationWitness, OwnershipError> {
637 let session = self.session_mut_or_error(sid)?;
638 Self::ensure_mutable_ownership(session)?;
639 let Some(current) = session.ownership.current.as_ref() else {
640 return Err(OwnershipError::Unclaimed { session_id: sid });
641 };
642 if current.owner_id != owner_id {
643 return Err(OwnershipError::StaleCapability {
644 session_id: sid,
645 owner_id: owner_id.to_string(),
646 expected_generation: current.generation,
647 actual_generation: current.generation,
648 });
649 }
650 let generation = current.generation;
651 let reason = OwnershipTerminalReason::OwnerDied {
652 owner_id: owner_id.to_string(),
653 };
654 session.status = SessionStatus::Faulted {
655 reason: format!("ownership owner `{owner_id}` died"),
656 };
657 let witness = CancellationWitness {
658 witness_id: Self::next_witness_id(session),
659 session_id: sid,
660 owner_id: owner_id.to_string(),
661 generation,
662 reason: reason.clone(),
663 };
664 session.ownership.current = None;
665 session.ownership.pending_transfer = None;
666 session.ownership.terminal_reason = Some(reason);
667 Self::push_authority_audit(
668 session,
669 AuthorityArtifact::Cancellation(witness.clone()),
670 AuthorityAuditEvent::Issued,
671 None,
672 );
673 Ok(witness)
674 }
675
676 pub fn cancel_abandoned_transfer(
682 &mut self,
683 receipt: &OwnershipReceipt,
684 ) -> Result<CancellationWitness, OwnershipError> {
685 let session = self.session_mut_or_error(receipt.session_id)?;
686 Self::ensure_mutable_ownership(session)?;
687 let Some(pending) = session.ownership.pending_transfer.as_ref() else {
688 return Err(OwnershipError::TransferNotPending {
689 session_id: receipt.session_id,
690 });
691 };
692 if pending.receipt != *receipt {
693 return Err(OwnershipError::ReceiptMismatch {
694 session_id: receipt.session_id,
695 claim_id: receipt.claim_id,
696 });
697 }
698 let reason = OwnershipTerminalReason::TransferAbandoned {
699 owner_id: receipt.from_owner_id.clone(),
700 claim_id: receipt.claim_id,
701 };
702 session.status = SessionStatus::Cancelled;
703 let witness = CancellationWitness {
704 witness_id: Self::next_witness_id(session),
705 session_id: receipt.session_id,
706 owner_id: receipt.from_owner_id.clone(),
707 generation: receipt.from_generation,
708 reason: reason.clone(),
709 };
710 session.ownership.current = None;
711 session.ownership.pending_transfer = None;
712 session.ownership.terminal_reason = Some(reason);
713 Self::push_authority_audit(
714 session,
715 AuthorityArtifact::Cancellation(witness.clone()),
716 AuthorityAuditEvent::Issued,
717 None,
718 );
719 Ok(witness)
720 }
721
722 pub fn fault_failed_transfer_commit(
728 &mut self,
729 receipt: &OwnershipReceipt,
730 reason: impl Into<String>,
731 ) -> Result<(), OwnershipError> {
732 let session = self.session_mut_or_error(receipt.session_id)?;
733 Self::ensure_mutable_ownership(session)?;
734 let Some(pending) = session.ownership.pending_transfer.as_ref() else {
735 return Err(OwnershipError::TransferNotPending {
736 session_id: receipt.session_id,
737 });
738 };
739 if pending.receipt != *receipt {
740 return Err(OwnershipError::ReceiptMismatch {
741 session_id: receipt.session_id,
742 claim_id: receipt.claim_id,
743 });
744 }
745 let reason = reason.into();
746 let terminal = OwnershipTerminalReason::TransferCommitFailed {
747 owner_id: receipt.from_owner_id.clone(),
748 claim_id: receipt.claim_id,
749 reason: reason.clone(),
750 };
751 session.status = SessionStatus::Faulted {
752 reason: format!("ownership transfer commit failed: {reason}"),
753 };
754 session.ownership.current = None;
755 session.ownership.pending_transfer = None;
756 session.ownership.terminal_reason = Some(terminal);
757 Ok(())
758 }
759
760 pub fn iter(&self) -> impl Iterator<Item = &SessionState> {
762 self.sessions.values()
763 }
764
765 pub fn close(&mut self, sid: SessionId) -> Result<(), String> {
771 let session = self
772 .sessions
773 .get_mut(&sid)
774 .ok_or_else(|| format!("session {sid} not found"))?;
775
776 session.status = SessionStatus::Closed;
777 session.buffers.clear();
778 session.edge_traces.clear();
779 session.epoch = session.epoch.saturating_add(1);
780 Ok(())
781 }
782
783 #[must_use]
785 pub fn closed_session_ids(&self) -> Vec<SessionId> {
786 self.sessions
787 .iter()
788 .filter_map(|(sid, session)| {
789 matches!(
790 session.status,
791 SessionStatus::Closed
792 | SessionStatus::Cancelled
793 | SessionStatus::Faulted { .. }
794 )
795 .then_some(*sid)
796 })
797 .collect()
798 }
799
800 pub fn reap_sessions(&mut self, session_ids: &[SessionId]) -> Vec<ClosedSessionSummary> {
807 let mut reaped = Vec::new();
808 for sid in session_ids {
809 let Some(session) = self.sessions.get(sid) else {
810 continue;
811 };
812 if !matches!(
813 session.status,
814 SessionStatus::Closed | SessionStatus::Cancelled | SessionStatus::Faulted { .. }
815 ) {
816 continue;
817 }
818
819 let session = self
820 .sessions
821 .remove(sid)
822 .expect("session existence checked before removal");
823 let summary = ClosedSessionSummary::from_session(&session);
824 self.archived_closed.push(summary.clone());
825 reaped.push(summary);
826 }
827 reaped
828 }
829
830 pub fn reap_closed(&mut self) -> Vec<ClosedSessionSummary> {
832 let sids = self.closed_session_ids();
833 self.reap_sessions(&sids)
834 }
835
836 #[must_use]
838 pub fn active_count(&self) -> usize {
839 self.sessions
840 .values()
841 .filter(|s| s.status == SessionStatus::Active)
842 .count()
843 }
844
845 #[must_use]
847 pub fn live_count(&self) -> usize {
848 self.sessions.len()
849 }
850
851 #[must_use]
853 pub fn session_ids(&self) -> Vec<SessionId> {
854 self.sessions.keys().copied().collect()
855 }
856
857 #[must_use]
859 pub fn archived_closed(&self) -> &[ClosedSessionSummary] {
860 &self.archived_closed
861 }
862
863 #[must_use]
865 pub fn memory_usage(&self) -> SessionStoreMemoryUsage {
866 let mut usage = SessionStoreMemoryUsage {
867 live_sessions: self.sessions.len(),
868 archived_closed_sessions: self.archived_closed.len(),
869 ..SessionStoreMemoryUsage::default()
870 };
871 usage.retained_bytes.archived_closed = self
872 .archived_closed
873 .iter()
874 .map(ClosedSessionSummary::retained_bytes_estimate)
875 .sum();
876
877 for session in self.sessions.values() {
878 if matches!(
879 session.status,
880 SessionStatus::Closed | SessionStatus::Cancelled | SessionStatus::Faulted { .. }
881 ) {
882 usage.live_closed_sessions += 1;
883 }
884 usage.live_local_type_entries += session.local_types.len();
885 usage.live_buffer_count += session.buffers.len();
886 usage.live_buffered_messages += session
887 .buffers
888 .values()
889 .map(BoundedBuffer::len)
890 .sum::<usize>();
891 usage.live_edge_handler_count += session.edge_handlers.len();
892 usage.live_auth_leaf_count += session.auth_leaves.values().map(Vec::len).sum::<usize>();
893 usage.live_auth_tree_count += session.auth_trees.len();
894 usage.live_auth_root_count += session.auth_roots.len();
895 usage.retained_bytes.live_sessions += session.retained_session_core_bytes();
896 usage.retained_bytes.local_types += session.retained_local_type_bytes();
897 usage.retained_bytes.buffers += session.retained_buffer_bytes();
898 usage.retained_bytes.traces += session.retained_trace_bytes();
899 usage.retained_bytes.auth += session.retained_auth_bytes();
900 usage.retained_bytes.handlers += session.retained_handler_bytes();
901 }
902 usage.retained_bytes.total = usage
903 .retained_bytes
904 .live_sessions
905 .saturating_add(usage.retained_bytes.archived_closed)
906 .saturating_add(usage.retained_bytes.local_types)
907 .saturating_add(usage.retained_bytes.buffers)
908 .saturating_add(usage.retained_bytes.traces)
909 .saturating_add(usage.retained_bytes.auth)
910 .saturating_add(usage.retained_bytes.handlers);
911
912 usage
913 }
914
915 #[must_use]
917 pub fn lookup_handler(&self, edge: &Edge) -> Option<&HandlerId> {
918 self.sessions
919 .get(&edge.sid)?
920 .lookup_handler_for_roles(&edge.sender, &edge.receiver)
921 }
922
923 #[must_use]
925 pub fn default_handler_for_session(&self, sid: SessionId) -> Option<&HandlerId> {
926 self.sessions.get(&sid)?.default_handler_binding()
927 }
928
929 pub(crate) fn set_default_handler_for_session(&mut self, sid: SessionId, handler: HandlerId) {
931 if let Some(session) = self.sessions.get_mut(&sid) {
932 let handler_id = session.intern_handler_binding(&handler);
933 session.default_handler = handler;
934 session.default_handler_id = Some(handler_id);
935 }
936 }
937
938 pub(crate) fn update_handler(&mut self, edge: &Edge, handler: HandlerId) {
940 if let Some(session) = self.sessions.get_mut(&edge.sid) {
941 let handler_id = session.intern_handler_binding(&handler);
942 if let Some(edge_key) = session.edge_key_for_roles(&edge.sender, &edge.receiver) {
943 session.edge_handler_lookup.insert(edge_key, handler_id);
944 }
945 session.edge_handlers.insert(edge.clone(), handler);
946 }
947 }
948
949 #[must_use]
951 pub fn lookup_trace(&self, edge: &Edge) -> Option<&[ValType]> {
952 self.sessions
953 .get(&edge.sid)?
954 .edge_traces
955 .get(edge)
956 .map(Vec::as_slice)
957 }
958
959 #[cfg_attr(not(test), allow(dead_code))]
961 pub(crate) fn update_trace(&mut self, edge: &Edge, trace: Vec<ValType>) {
962 if let Some(session) = self.sessions.get_mut(&edge.sid) {
963 session.edge_traces.insert(edge.clone(), trace);
964 }
965 }
966}
967
968#[must_use]
974pub fn unfold_mu(lt: &LocalTypeR) -> LocalTypeR {
976 match lt {
977 LocalTypeR::Mu { body, .. } => unfold_mu(body),
978 other => other.clone(),
979 }
980}
981
982#[must_use]
987pub fn unfold_if_var(cont: &LocalTypeR, original: &LocalTypeR) -> LocalTypeR {
988 match cont {
989 LocalTypeR::Var(_) => unfold_mu(original),
990 LocalTypeR::Mu { .. } => unfold_mu(cont),
991 other => other.clone(),
992 }
993}
994
995#[must_use]
1001pub fn unfold_if_var_with_scope(
1002 cont: &LocalTypeR,
1003 original: &LocalTypeR,
1004) -> (LocalTypeR, Option<LocalTypeR>) {
1005 match cont {
1006 LocalTypeR::Var(_) => (unfold_mu(original), None),
1007 LocalTypeR::Mu { .. } => (unfold_mu(cont), Some(cont.clone())),
1008 other => (other.clone(), None),
1009 }
1010}