1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
23use std::panic::AssertUnwindSafe;
24use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
25use std::sync::{Arc, Mutex, MutexGuard, RwLock};
26use std::time::Duration;
27
28use futures::FutureExt;
29use oxrdf::vocab::xsd;
30use oxrdf::{GraphName, Literal, NamedNode, Quad, Term, Triple};
31use vertep_core::datamodel::{DatamodelRegistry, Environment, Event, EventType, Value};
32use vertep_core::ir::Chart;
33use vertep_core::session::{InvokeRequest, SCXML_EVENT_PROCESSOR, SCXML_INVOKE_TYPE};
34use vertep_rdf::ChartVersion;
35use vertep_store::{AppendError, AppendMeta, FencingToken, Position, StateDelta, StoreError};
36
37use crate::actor::{
38 Actor, ActorFactory, ActorFailure, ActorFn, ActorInput, ActorStart, ChartActor, FnActor,
39 OutTarget, Outgoing, START_EVENT, Turn, TurnOutput, platform_event,
40};
41use crate::address::{Address, hex};
42use crate::chart::{
43 ChartInfo, ChartRegistry, Migration, RegisterError, RestartMode, RestartPolicy,
44 UnhandledPolicy, migrated_session, static_invoke_sources, unmapped_configurations,
45};
46use crate::journal::{
47 DecodeError, EntryBody, EntryData, InputRecord, SendRecord, SnapshotData, TimerRecord,
48};
49use crate::machine::{Ctx, Machine, MachineError};
50use crate::mailbox::{
51 Envelope, Mailbox, MailboxError, MailboxFactory, MemoryMailbox, MemoryMailboxes,
52};
53use crate::placement::{Placement, Route};
54use crate::shell::Shell;
55use crate::storage::{Storage, select, text};
56use crate::transport::{Directory, Resolver, Transport, Transports};
57use crate::vocab::{self as vt, node};
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62pub enum CrashPoint {
63 BeforeAppend,
65 AfterAppend,
67}
68
69#[derive(Clone, Debug)]
71pub struct NodeConfig {
72 pub idle_timeout_ms: i64,
74 pub snapshot_interval: u64,
77 pub fencing: Option<FencingToken>,
79 pub failure_backoff_ms: i64,
81 pub crash_at: Option<CrashPoint>,
83}
84
85impl Default for NodeConfig {
86 fn default() -> Self {
87 NodeConfig {
88 idle_timeout_ms: 60_000,
89 snapshot_interval: 100,
90 fencing: None,
91 failure_backoff_ms: 1_000,
92 crash_at: None,
93 }
94 }
95}
96
97#[derive(Clone, Debug, PartialEq)]
99pub enum ActivationError {
100 ReplayDivergence {
102 actor: Address,
104 position: u64,
106 },
107 MissingVersion {
109 actor: Address,
111 version: String,
113 },
114 NoChart(Address),
116 Decode(DecodeError),
118 Store(StoreError),
120 Machine(String),
122 Append(AppendError),
124}
125
126impl core::fmt::Display for ActivationError {
127 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
128 match self {
129 ActivationError::ReplayDivergence { actor, position } => {
130 write!(f, "replay divergence for {actor} at position {position}")
131 }
132 ActivationError::MissingVersion { actor, version } => {
133 write!(f, "chart version {version} of {actor} is not registered")
134 }
135 ActivationError::NoChart(a) => write!(f, "no chart type is registered for {a}"),
136 ActivationError::Decode(e) => write!(f, "{e}"),
137 ActivationError::Store(e) => write!(f, "{e}"),
138 ActivationError::Machine(e) => write!(f, "{e}"),
139 ActivationError::Append(e) => write!(f, "{e}"),
140 }
141 }
142}
143
144impl std::error::Error for ActivationError {}
145
146#[derive(Clone, Debug, PartialEq)]
148pub enum MigrateError {
149 UnknownMigration(String),
151 WrongVersion {
153 actual: String,
155 expected: String,
157 },
158 Unmapped(Vec<BTreeSet<String>>),
160 TooManyConfigurations(usize),
162 TransformFailed(String),
164 Activation(ActivationError),
166 NotStarted,
168 Exists(Address),
170 Append(AppendError),
172}
173
174impl core::fmt::Display for MigrateError {
175 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
176 match self {
177 MigrateError::UnknownMigration(m) => write!(f, "unknown migration {m}"),
178 MigrateError::WrongVersion { actual, expected } => {
179 write!(f, "actor runs {actual}, migration starts from {expected}")
180 }
181 MigrateError::Unmapped(c) => {
182 let list: Vec<String> = c
183 .iter()
184 .map(|s| format!("{{{}}}", s.iter().cloned().collect::<Vec<_>>().join(", ")))
185 .collect();
186 write!(f, "unmapped configurations: {}", list.join(" "))
187 }
188 MigrateError::TooManyConfigurations(n) => {
189 write!(f, "more than {n} reachable configurations")
190 }
191 MigrateError::TransformFailed(e) => write!(f, "datamodel transform failed: {e}"),
192 MigrateError::Activation(e) => write!(f, "{e}"),
193 MigrateError::NotStarted => f.write_str("the actor has no state yet"),
194 MigrateError::Exists(a) => write!(f, "{a} already has a journal"),
195 MigrateError::Append(e) => write!(f, "{e}"),
196 }
197 }
198}
199
200impl std::error::Error for MigrateError {}
201
202#[derive(Clone, Debug, PartialEq, Eq)]
204pub enum RemoveError {
205 InUse(Vec<Address>),
207 Store(String),
209}
210
211impl core::fmt::Display for RemoveError {
212 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
213 match self {
214 RemoveError::InUse(a) => write!(
215 f,
216 "version in use by {}",
217 a.iter().map(Address::as_str).collect::<Vec<_>>().join(", ")
218 ),
219 RemoveError::Store(e) => write!(f, "{e}"),
220 }
221 }
222}
223
224impl std::error::Error for RemoveError {}
225
226#[derive(Clone, Debug, PartialEq, Eq)]
228pub enum DeliveryError {
229 Unresolvable(String),
231 Transport(String),
233}
234
235impl core::fmt::Display for DeliveryError {
236 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
237 match self {
238 DeliveryError::Unresolvable(t) => write!(f, "cannot resolve {t}"),
239 DeliveryError::Transport(e) => write!(f, "{e}"),
240 }
241 }
242}
243
244impl std::error::Error for DeliveryError {}
245
246#[derive(Clone, Debug, PartialEq, Eq)]
248pub enum AskError {
249 Timeout,
251 Delivery(DeliveryError),
253}
254
255impl core::fmt::Display for AskError {
256 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
257 match self {
258 AskError::Timeout => f.write_str("ask timed out"),
259 AskError::Delivery(e) => write!(f, "{e}"),
260 }
261 }
262}
263
264impl std::error::Error for AskError {}
265
266#[derive(Clone, Debug, PartialEq)]
268pub struct ActorState {
269 pub position: u64,
271 pub version: String,
273 pub configuration: Vec<String>,
275 pub datamodel: Value,
277 pub terminated: bool,
279}
280
281#[derive(Clone, Debug, PartialEq, Eq)]
283pub struct DeadLetter {
284 pub iri: String,
286 pub receiver: String,
288 pub sender: Option<String>,
290 pub event: String,
292 pub payload: String,
294 pub reason: String,
296 pub recorded_at: i64,
298}
299
300#[derive(Clone, Debug, Default)]
302pub struct NodeStats {
303 pub activations: BTreeMap<Address, u64>,
305 pub deactivations: u64,
307 pub turns: u64,
309 pub replayed: u64,
311 pub worker_concurrency: BTreeMap<Address, usize>,
313 pub restarts: Vec<(Address, i64)>,
315}
316
317#[derive(Clone)]
319struct WorkerSpec {
320 pool: usize,
321 handler: ActorFn,
322}
323
324struct AskMailbox {
326 tx: Mutex<Option<tokio::sync::oneshot::Sender<Event>>>,
327}
328
329impl Mailbox for AskMailbox {
330 fn enqueue(&self, env: Envelope) -> Result<(), MailboxError> {
331 if let Some(tx) = self.tx.lock().ok().and_then(|mut g| g.take()) {
332 let _ = tx.send(env.event);
333 }
334 Ok(())
335 }
336 fn receive(&self, _: &[String]) -> Option<Envelope> {
337 None
338 }
339 fn ack(&self, _: &str) -> Result<(), MailboxError> {
340 Ok(())
341 }
342 fn release(&self, _: &str) {}
343 fn release_all(&self) {}
344 fn pending(&self) -> Vec<Envelope> {
345 Vec::new()
346 }
347 fn pending_deliverable(&self, _: &[String]) -> usize {
348 0
349 }
350}
351
352struct DropMailbox;
354
355impl Mailbox for DropMailbox {
356 fn enqueue(&self, _: Envelope) -> Result<(), MailboxError> {
357 Ok(())
358 }
359 fn receive(&self, _: &[String]) -> Option<Envelope> {
360 None
361 }
362 fn ack(&self, _: &str) -> Result<(), MailboxError> {
363 Ok(())
364 }
365 fn release(&self, _: &str) {}
366 fn release_all(&self) {}
367 fn pending(&self) -> Vec<Envelope> {
368 Vec::new()
369 }
370 fn pending_deliverable(&self, _: &[String]) -> usize {
371 0
372 }
373}
374
375const ASK_PREFIX: &str = "urn:vertep:ask:";
376
377#[derive(Default)]
378struct Hosted {
379 workers: BTreeMap<Address, WorkerSpec>,
380 children: BTreeSet<Address>,
381 asks: BTreeMap<Address, Arc<AskMailbox>>,
382}
383
384struct Completion {
385 address: Address,
386 generation: u64,
387 message_id: Option<String>,
388 origin: Option<String>,
389 correlation: Option<String>,
390 result: Result<TurnOutput, ActorFailure>,
391}
392
393#[derive(Default)]
394struct Ready {
395 queue: VecDeque<Address>,
396 set: HashSet<Address>,
397}
398
399struct Shared {
401 storage: Arc<dyn Storage>,
402 shell: Shell,
403 directory: Directory,
404 transports: Transports,
405 default_mailboxes: Arc<dyn MailboxFactory>,
406 scheme_mailboxes: BTreeMap<String, Arc<dyn MailboxFactory>>,
407 mailboxes: Mutex<HashMap<Address, Arc<dyn Mailbox>>>,
408 charts: RwLock<ChartRegistry>,
409 hosted: RwLock<Hosted>,
410 ready: Mutex<Ready>,
411 completions: Mutex<VecDeque<Completion>>,
412 jobs: AtomicUsize,
413 wake: tokio::sync::Notify,
414 counter: AtomicU64,
415 nonce: u64,
416 shutdown: AtomicBool,
417 placement: Option<Arc<dyn Placement>>,
419}
420
421fn lock<T>(m: &Mutex<T>) -> MutexGuard<'_, T> {
422 m.lock().unwrap_or_else(std::sync::PoisonError::into_inner)
423}
424
425#[derive(Clone, Copy, PartialEq, Eq)]
426enum Kind {
427 Ask,
428 Worker,
429 Child,
430 Virtual,
431}
432
433impl Shared {
434 fn charts(&self) -> std::sync::RwLockReadGuard<'_, ChartRegistry> {
435 self.charts
436 .read()
437 .unwrap_or_else(std::sync::PoisonError::into_inner)
438 }
439
440 fn charts_mut(&self) -> std::sync::RwLockWriteGuard<'_, ChartRegistry> {
441 self.charts
442 .write()
443 .unwrap_or_else(std::sync::PoisonError::into_inner)
444 }
445
446 fn hosted(&self) -> std::sync::RwLockReadGuard<'_, Hosted> {
447 self.hosted
448 .read()
449 .unwrap_or_else(std::sync::PoisonError::into_inner)
450 }
451
452 fn hosted_mut(&self) -> std::sync::RwLockWriteGuard<'_, Hosted> {
453 self.hosted
454 .write()
455 .unwrap_or_else(std::sync::PoisonError::into_inner)
456 }
457
458 fn kind(&self, a: &Address) -> Option<Kind> {
459 {
460 let h = self.hosted();
461 if h.asks.contains_key(a) || a.as_str().starts_with(ASK_PREFIX) {
462 return Some(Kind::Ask);
463 }
464 if h.workers.contains_key(a) {
465 return Some(Kind::Worker);
466 }
467 if h.children.contains(a) {
468 return Some(Kind::Child);
469 }
470 }
471 if self.charts().type_of(a.as_str()).is_some() {
472 return Some(Kind::Virtual);
473 }
474 if self
475 .storage
476 .journal()
477 .head(a.node())
478 .is_ok_and(|h| h.get() > 0)
479 {
480 return Some(Kind::Virtual);
481 }
482 None
483 }
484
485 fn local_mailbox(&self, a: &Address) -> Option<(Arc<dyn Mailbox>, Kind)> {
487 let kind = self.kind(a)?;
488 if kind == Kind::Ask {
489 let m: Arc<dyn Mailbox> = match self.hosted().asks.get(a) {
490 Some(m) => m.clone(),
491 None => Arc::new(DropMailbox),
492 };
493 return Some((m, kind));
494 }
495 let mut g = lock(&self.mailboxes);
496 if let Some(m) = g.get(a) {
497 return Some((m.clone(), kind));
498 }
499 let m = match kind {
500 Kind::Child | Kind::Worker => Arc::new(MemoryMailbox::default()) as Arc<dyn Mailbox>,
501 _ => self
502 .scheme_mailboxes
503 .get(a.scheme())
504 .unwrap_or(&self.default_mailboxes)
505 .create(a),
506 };
507 g.insert(a.clone(), m.clone());
508 Some((m, kind))
509 }
510
511 fn mark_ready(&self, a: &Address) {
512 let mut r = lock(&self.ready);
513 if r.set.insert(a.clone()) {
514 r.queue.push_back(a.clone());
515 }
516 drop(r);
517 self.wake.notify_one();
518 }
519
520 fn deliver(&self, env: Envelope, type_: &str) -> Result<(), DeliveryError> {
522 let to =
523 Address::parse(&env.to).map_err(|_| DeliveryError::Unresolvable(env.to.clone()))?;
524 let transport: Arc<dyn Transport> = self
525 .transports
526 .get(type_)
527 .or_else(|| self.transports.get(SCXML_EVENT_PROCESSOR))
528 .cloned()
529 .ok_or_else(|| DeliveryError::Transport(format!("no transport for {type_}")))?;
530 if let Some(m) = self.placed_elsewhere(&to, &env) {
531 return transport
532 .deliver(m.as_ref(), env)
533 .map_err(|e| DeliveryError::Transport(e.0));
534 }
535 self.deliver_with(env, &to, transport.as_ref())
536 }
537
538 fn placed_elsewhere(&self, to: &Address, env: &Envelope) -> Option<Arc<dyn Mailbox>> {
541 let p = self.placement.as_ref()?;
542 let kind = self.kind(to);
543 if matches!(kind, Some(Kind::Ask | Kind::Child)) {
544 return None;
545 }
546 match p.route(to, kind == Some(Kind::Virtual), Some(env)) {
547 Route::Remote(m) => Some(m),
548 Route::Local | Route::Hold => None,
549 }
550 }
551
552 fn deliver_with(
555 &self,
556 env: Envelope,
557 to: &Address,
558 transport: &dyn Transport,
559 ) -> Result<(), DeliveryError> {
560 let to = to.clone();
561 if let Some((m, kind)) = self.local_mailbox(&to) {
562 transport
563 .deliver(m.as_ref(), env)
564 .map_err(|e| DeliveryError::Transport(e.0))?;
565 if kind != Kind::Ask {
566 self.mark_ready(&to);
567 }
568 return Ok(());
569 }
570 let m = self
571 .directory
572 .resolve(&to)
573 .map_err(|_| DeliveryError::Unresolvable(to.as_str().to_owned()))?;
574 transport
575 .deliver(m.as_ref(), env)
576 .map_err(|e| DeliveryError::Transport(e.0))
577 }
578
579 fn fresh_id(&self, prefix: &str) -> String {
580 let n = self.counter.fetch_add(1, Ordering::Relaxed);
581 format!(
582 "{prefix}{}-{:016x}-{n}",
583 self.shell.clock.now_ms(),
584 self.nonce
585 )
586 }
587
588 fn complete(&self, c: Completion) {
589 lock(&self.completions).push_back(c);
590 self.jobs.fetch_sub(1, Ordering::SeqCst);
591 self.wake.notify_one();
592 }
593}
594
595#[derive(Clone)]
598pub struct NodeHandle {
599 shared: Arc<Shared>,
600}
601
602impl std::fmt::Debug for NodeHandle {
603 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
604 f.debug_struct("NodeHandle").finish_non_exhaustive()
605 }
606}
607
608impl NodeHandle {
609 pub fn send(&self, to: &Address, event: Event) -> Result<String, DeliveryError> {
615 let id = self.shared.fresh_id("urn:vertep:message:");
616 self.send_envelope(Envelope::new(id.clone(), to, event))?;
617 Ok(id)
618 }
619
620 pub fn send_envelope(&self, env: Envelope) -> Result<(), DeliveryError> {
626 self.shared.deliver(env, SCXML_EVENT_PROCESSOR)
627 }
628
629 pub async fn ask(
638 &self,
639 to: &Address,
640 event: Event,
641 timeout: Duration,
642 ) -> Result<Event, AskError> {
643 let me = Address::parse(&self.shared.fresh_id(ASK_PREFIX))
644 .map_err(|e| AskError::Delivery(DeliveryError::Unresolvable(e.text)))?;
645 let (tx, rx) = tokio::sync::oneshot::channel();
646 self.shared.hosted_mut().asks.insert(
647 me.clone(),
648 Arc::new(AskMailbox {
649 tx: Mutex::new(Some(tx)),
650 }),
651 );
652 let mut ev = event;
653 ev.origin = Some(me.as_str().to_owned());
654 ev.origintype = Some(SCXML_EVENT_PROCESSOR.to_owned());
655 ev.originverified = false;
656 let mut env = Envelope::new(format!("{me}/message"), to, ev);
657 env.correlation = Some(format!("{me}/request"));
658 if let Err(e) = self.send_envelope(env) {
659 self.shared.hosted_mut().asks.remove(&me);
660 return Err(AskError::Delivery(e));
661 }
662 let deadline = self
663 .shared
664 .shell
665 .clock
666 .now_ms()
667 .saturating_add(i64::try_from(timeout.as_millis()).unwrap_or(i64::MAX));
668 let sleep = self.shared.shell.sleep.sleep_until(deadline);
669 let r = futures::future::select(rx, sleep).await;
670 self.shared.hosted_mut().asks.remove(&me);
671 match r {
672 futures::future::Either::Left((Ok(ev), _)) => Ok(ev),
673 _ => Err(AskError::Timeout),
674 }
675 }
676
677 pub fn register_resolver(&self, scheme: &str, resolver: Arc<dyn Resolver>) {
679 self.shared.directory.register(scheme, resolver);
680 }
681
682 pub fn bind_address(&self, address: &Address, chart_type: &str, key: &str) {
686 self.shared
687 .charts_mut()
688 .bind(address.as_str(), chart_type, key);
689 }
690
691 pub fn notify(&self, a: &Address) -> bool {
695 match self.shared.local_mailbox(a) {
696 Some((_, kind)) if kind != Kind::Ask => {
697 self.shared.mark_ready(a);
698 true
699 }
700 _ => false,
701 }
702 }
703
704 pub fn shutdown(&self) {
706 self.shared.shutdown.store(true, Ordering::SeqCst);
707 self.shared.wake.notify_one();
708 }
709
710 pub fn now_ms(&self) -> i64 {
712 self.shared.shell.clock.now_ms()
713 }
714
715 pub fn wakeup(&self) -> crate::shell::BoxFuture<()> {
719 let shared = self.shared.clone();
720 Box::pin(async move { shared.wake.notified().await })
721 }
722
723 pub fn deliver_here(&self, env: Envelope) -> Result<(), DeliveryError> {
730 let to =
731 Address::parse(&env.to).map_err(|_| DeliveryError::Unresolvable(env.to.clone()))?;
732 let transport = self
733 .shared
734 .transports
735 .get(SCXML_EVENT_PROCESSOR)
736 .cloned()
737 .ok_or_else(|| DeliveryError::Transport("no local transport".into()))?;
738 self.shared.deliver_with(env, &to, transport.as_ref())
739 }
740
741 pub fn record_dead_letter(&self, receiver: &Address, env: &Envelope, reason: &str) {
743 self.shared
744 .dead_letter(receiver, env, reason, self.shared.shell.clock.now_ms());
745 }
746
747 pub fn pending(&self, a: &Address) -> usize {
750 lock(&self.shared.mailboxes).get(a).map_or(0, |m| m.len())
751 }
752}
753
754pub struct NodeBuilder {
756 storage: Arc<dyn Storage>,
757 shell: Shell,
758 config: NodeConfig,
759 registry: Arc<dyn DatamodelRegistry + Send + Sync>,
760 mailboxes: Arc<dyn MailboxFactory>,
761 scheme_mailboxes: BTreeMap<String, Arc<dyn MailboxFactory>>,
762 transports: Transports,
763 placement: Option<Arc<dyn Placement>>,
764}
765
766impl std::fmt::Debug for NodeBuilder {
767 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
768 f.debug_struct("NodeBuilder")
769 .field("config", &self.config)
770 .finish_non_exhaustive()
771 }
772}
773
774impl NodeBuilder {
775 pub fn datamodels(mut self, r: Arc<dyn DatamodelRegistry + Send + Sync>) -> Self {
777 self.registry = r;
778 self
779 }
780
781 pub fn mailboxes(mut self, f: Arc<dyn MailboxFactory>) -> Self {
783 self.mailboxes = f;
784 self
785 }
786
787 pub fn mailboxes_for_scheme(mut self, scheme: &str, f: Arc<dyn MailboxFactory>) -> Self {
789 self.scheme_mailboxes.insert(scheme.to_owned(), f);
790 self
791 }
792
793 pub fn transport(mut self, t: Arc<dyn Transport>) -> Self {
795 self.transports.register(t);
796 self
797 }
798
799 pub fn config(mut self, c: NodeConfig) -> Self {
801 self.config = c;
802 self
803 }
804
805 pub fn placement(mut self, p: Arc<dyn Placement>) -> Self {
808 self.placement = Some(p);
809 self
810 }
811
812 pub fn build(self) -> Node {
816 let nonce = {
817 use std::hash::{BuildHasher, Hasher};
818 let mut h = std::collections::hash_map::RandomState::new().build_hasher();
819 h.write_u64(self.shell.clock.now_ms() as u64);
820 h.finish()
821 };
822 let mut factories = vec![self.mailboxes.clone()];
823 factories.extend(self.scheme_mailboxes.values().cloned());
824 let shared = Arc::new(Shared {
825 storage: self.storage,
826 shell: self.shell,
827 directory: Directory::default(),
828 transports: self.transports,
829 default_mailboxes: self.mailboxes,
830 scheme_mailboxes: self.scheme_mailboxes,
831 mailboxes: Mutex::new(HashMap::new()),
832 charts: RwLock::new(ChartRegistry::default()),
833 hosted: RwLock::new(Hosted::default()),
834 ready: Mutex::new(Ready::default()),
835 completions: Mutex::new(VecDeque::new()),
836 jobs: AtomicUsize::new(0),
837 wake: tokio::sync::Notify::new(),
838 counter: AtomicU64::new(0),
839 nonce,
840 shutdown: AtomicBool::new(false),
841 placement: self.placement,
842 });
843
844 Node {
845 shared,
846 config: self.config,
847 registry: self.registry,
848 kinds: BTreeMap::new(),
849 actors: BTreeMap::new(),
850 timers: BTreeMap::new(),
851 timer_keys: HashMap::new(),
852 volatile: BTreeMap::new(),
853 parked: BTreeMap::new(),
854 errors: BTreeMap::new(),
855 migrations: BTreeMap::new(),
856 seq: 0,
857 crashed: false,
858 recovered: false,
859 stats: NodeStats::default(),
860 factories,
861 outbox: HashMap::new(),
862 }
863 }
864}
865
866struct Armed {
867 rec: TimerRecord,
868 occurrence: u64,
869}
870
871enum Volatile {
872 Deliver(Box<Envelope>),
873 Resend(Address, u64, Box<SendRecord>),
876 Restart(Address, u64),
877}
878
879struct VirtualActivation {
880 machine: Option<Machine>,
881 first: Arc<ChartInfo>,
882 chart_type: Option<(String, String)>,
883 head: u64,
884 seen: HashSet<String>,
885 state: HashSet<Triple>,
886 last_active: i64,
887}
888
889impl VirtualActivation {
890 fn deferred(&self) -> Vec<String> {
891 self.machine
892 .as_ref()
893 .map(|m| m.info.deferred(&m.session))
894 .unwrap_or_default()
895 }
896
897 fn info(&self) -> &Arc<ChartInfo> {
898 self.machine.as_ref().map_or(&self.first, |m| &m.info)
899 }
900}
901
902struct ChildActivation {
903 parent: Address,
904 invokeid: String,
905 make: Arc<dyn Fn() -> Result<Box<dyn Actor>, ActorFailure> + Send + Sync>,
906 actor: Box<dyn Actor>,
907 busy: bool,
908 generation: u64,
909 policy: RestartPolicy,
910 failures: Vec<i64>,
911 out_seq: u64,
912 suppress: bool,
913}
914
915struct WorkerPool {
916 spec: WorkerSpec,
917 busy: usize,
918}
919
920enum Activation {
921 Virtual(Box<VirtualActivation>),
922 Child(Box<ChildActivation>),
923 Worker(WorkerPool),
924}
925
926pub struct Node {
928 shared: Arc<Shared>,
929 config: NodeConfig,
930 registry: Arc<dyn DatamodelRegistry + Send + Sync>,
931 kinds: BTreeMap<String, ActorFactory>,
932 actors: BTreeMap<Address, Activation>,
933 timers: BTreeMap<(i64, String), Armed>,
934 timer_keys: HashMap<String, (i64, String)>,
935 volatile: BTreeMap<(i64, u64), Volatile>,
936 parked: BTreeMap<Address, i64>,
937 errors: BTreeMap<Address, ActivationError>,
938 migrations: BTreeMap<Address, VecDeque<(String, String)>>,
939 seq: u64,
940 crashed: bool,
941 recovered: bool,
942 stats: NodeStats,
943 factories: Vec<Arc<dyn MailboxFactory>>,
944 outbox: HashMap<Address, Outbox>,
945}
946
947#[derive(Default)]
951struct Outbox {
952 tip: u64,
954 owed: BTreeMap<u64, usize>,
956}
957
958impl std::fmt::Debug for Node {
959 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
960 f.debug_struct("Node")
961 .field("active", &self.actors.keys().collect::<Vec<_>>())
962 .field("timers", &self.timers.len())
963 .finish_non_exhaustive()
964 }
965}
966
967struct Loaded {
969 machine: Option<Machine>,
970 head: u64,
971 seen: HashSet<String>,
972 entries: Vec<EntryData>,
973}
974
975fn is_scxml_invoke(t: &str) -> bool {
976 t == SCXML_INVOKE_TYPE || t == "http://www.w3.org/TR/scxml" || t == "scxml"
977}
978
979impl Node {
980 pub fn builder(storage: Arc<dyn Storage>, shell: Shell) -> NodeBuilder {
982 NodeBuilder {
983 storage,
984 shell,
985 config: NodeConfig::default(),
986 registry: Arc::new(vertep_core::null::NullRegistry),
987 mailboxes: Arc::new(MemoryMailboxes),
988 scheme_mailboxes: BTreeMap::new(),
989 transports: Transports::local(),
990 placement: None,
991 }
992 }
993
994 pub fn handle(&self) -> NodeHandle {
996 NodeHandle {
997 shared: self.shared.clone(),
998 }
999 }
1000
1001 pub fn stats(&self) -> &NodeStats {
1003 &self.stats
1004 }
1005
1006 pub fn storage(&self) -> &Arc<dyn Storage> {
1008 &self.shared.storage
1009 }
1010
1011 pub fn now_ms(&self) -> i64 {
1013 self.shared.shell.clock.now_ms()
1014 }
1015
1016 pub fn is_active(&self, a: &Address) -> bool {
1018 self.actors.contains_key(a)
1019 }
1020
1021 pub fn activate_actor(&mut self, a: &Address) -> Result<(), ActivationError> {
1027 if self.actors.contains_key(a) {
1028 return Ok(());
1029 }
1030 let now = self.now_ms();
1031 self.activate(a, now)
1032 }
1033
1034 pub fn last_error(&self, a: &Address) -> Option<&ActivationError> {
1036 self.errors.get(a)
1037 }
1038
1039 pub fn chart(&self, iri: &str) -> Option<Arc<ChartInfo>> {
1041 self.shared.charts().get(iri)
1042 }
1043
1044 pub fn latest(&self, ty: &str) -> Option<Arc<ChartInfo>> {
1046 self.shared.charts().latest(ty)
1047 }
1048
1049 pub fn mailbox(&self, a: &Address) -> Vec<Envelope> {
1051 self.shared
1052 .local_mailbox(a)
1053 .map(|(m, _)| m.pending())
1054 .unwrap_or_default()
1055 }
1056
1057 pub fn register_type(
1069 &mut self,
1070 chart_type: &str,
1071 chart: Chart,
1072 ) -> Result<ChartVersion, RegisterError> {
1073 Address::parse(chart_type).map_err(|_| RegisterError::BadType(chart_type.to_owned()))?;
1074 let mut srcs = Vec::new();
1075 static_invoke_sources(&chart.states, &mut srcs);
1076 {
1077 let charts = self.shared.charts();
1078 for (state, src) in srcs {
1079 let own = src
1080 .strip_prefix(chart_type)
1081 .is_some_and(|r| r.starts_with('/'));
1082 if own || charts.type_of(&src).is_some() {
1083 return Err(RegisterError::InvokesVirtualActor { state, src });
1084 }
1085 }
1086 }
1087 let info = ChartInfo::new(chart, Some(chart_type.to_owned()))?;
1088 self.admit_chart(&info)?;
1089 Ok(self.shared.charts_mut().insert(info).version.clone())
1090 }
1091
1092 pub fn register_chart(&mut self, chart: Chart) -> Result<ChartVersion, RegisterError> {
1098 let info = ChartInfo::new(chart, None)?;
1099 self.admit_chart(&info)?;
1100 Ok(self.shared.charts_mut().insert(info).version.clone())
1101 }
1102
1103 fn admit_chart(&self, info: &ChartInfo) -> Result<(), RegisterError> {
1108 let chart = info.compiled.chart();
1109 self.registry
1110 .check(chart)
1111 .map_err(|problems| RegisterError::Refused {
1112 datamodel: chart.datamodel.clone(),
1113 problems,
1114 })
1115 }
1116
1117 pub fn register_migration(&mut self, m: Migration) {
1119 self.shared.charts_mut().add_migration(m);
1120 }
1121
1122 pub fn register_actor_kind(&mut self, invoke_type: &str, factory: ActorFactory) {
1124 self.kinds.insert(invoke_type.to_owned(), factory);
1125 }
1126
1127 pub fn register_worker(&mut self, address: Address, pool: usize, handler: ActorFn) {
1130 self.shared.hosted_mut().workers.insert(
1131 address,
1132 WorkerSpec {
1133 pool: pool.max(1),
1134 handler,
1135 },
1136 );
1137 }
1138
1139 pub fn send(&self, to: &Address, event: Event) -> Result<String, DeliveryError> {
1145 self.handle().send(to, event)
1146 }
1147
1148 pub fn send_envelope(&self, env: Envelope) -> Result<(), DeliveryError> {
1154 self.handle().send_envelope(env)
1155 }
1156
1157 pub fn remove_version(&mut self, iri: &str) -> Result<(), RemoveError> {
1163 let j = self.shared.storage.journal();
1164 let mut users = BTreeSet::new();
1165 for (a, act) in &self.actors {
1166 if let Activation::Virtual(v) = act
1167 && let Some(m) = &v.machine
1168 && m.info.version.as_str() == iri
1169 && !m.session.is_terminated()
1170 {
1171 users.insert(a.clone());
1172 }
1173 }
1174 for a in j.actors().map_err(|e| RemoveError::Store(e.to_string()))? {
1175 let head = j.head(&a).map_err(|e| RemoveError::Store(e.to_string()))?;
1176 let recs = j
1177 .read(&a, head)
1178 .map_err(|e| RemoveError::Store(e.to_string()))?;
1179 let Ok(addr) = Address::parse(a.as_str()) else {
1180 continue;
1181 };
1182 if let Some(r) = recs.last()
1183 && let Ok(e) = EntryData::from_record(&addr, r)
1184 && e.version == iri
1185 && !e.effects().is_some_and(|x| x.terminated)
1186 {
1187 users.insert(addr);
1188 }
1189 }
1190 if !users.is_empty() {
1191 return Err(RemoveError::InUse(users.into_iter().collect()));
1192 }
1193 self.shared.charts_mut().remove(iri);
1194 Ok(())
1195 }
1196
1197 pub fn pump(&mut self) -> bool {
1201 if self.crashed {
1202 return false;
1203 }
1204 if !self.recovered {
1205 self.recovered = true;
1206 self.recover();
1207 }
1208 let now = self.now_ms();
1209 let unparked: Vec<Address> = self
1210 .parked
1211 .iter()
1212 .filter(|(_, t)| **t <= now)
1213 .map(|(a, _)| a.clone())
1214 .collect();
1215 for a in unparked {
1216 self.parked.remove(&a);
1217 self.shared.mark_ready(&a);
1218 }
1219 let mut progress = self.drain_completions(now);
1220 progress |= self.fire_timers(now);
1221 progress |= self.fire_volatile(now);
1222 progress |= self.process_ready(now);
1223 progress |= self.deactivate_idle(now);
1224 progress
1225 }
1226
1227 pub fn run_until_idle(&mut self) -> usize {
1230 let mut n = 0;
1231 while n < 1_000_000 && self.pump() {
1232 n += 1;
1233 }
1234 n
1235 }
1236
1237 pub async fn settle(&mut self) {
1239 loop {
1240 self.run_until_idle();
1241 if self.crashed
1242 || (self.shared.jobs.load(Ordering::SeqCst) == 0
1243 && lock(&self.shared.completions).is_empty())
1244 {
1245 return;
1246 }
1247 self.shared.wake.notified().await;
1248 }
1249 }
1250
1251 pub async fn run(&mut self) {
1254 loop {
1255 self.run_until_idle();
1256 if self.crashed || self.shared.shutdown.load(Ordering::SeqCst) {
1257 return;
1258 }
1259 let notified = self.shared.wake.notified();
1260 match self.next_deadline() {
1261 Some(t) => {
1262 let sleep = self.shared.shell.sleep.sleep_until(t);
1263 futures::future::select(Box::pin(notified), sleep).await;
1264 }
1265 None => notified.await,
1266 }
1267 }
1268 }
1269
1270 pub fn next_deadline(&self) -> Option<i64> {
1273 let mut d: Vec<i64> = Vec::new();
1274 d.extend(self.timers.keys().next().map(|k| k.0));
1275 d.extend(self.volatile.keys().next().map(|k| k.0));
1276 d.extend(self.parked.values().min().copied());
1277 for a in self.actors.values() {
1278 if let Activation::Virtual(v) = a {
1279 d.push(v.last_active.saturating_add(self.config.idle_timeout_ms));
1280 }
1281 }
1282 d.into_iter().min()
1283 }
1284
1285 fn ctx(&self, a: &Address) -> (Vec<String>, Vec<String>, Value) {
1286 let mut invoke_types: Vec<String> = self.kinds.keys().cloned().collect();
1287 invoke_types.push(SCXML_INVOKE_TYPE.to_owned());
1288 (
1289 self.shared.transports.send_types(),
1290 invoke_types,
1291 self.shared.transports.ioprocessors(a),
1292 )
1293 }
1294
1295 fn park(&mut self, a: &Address, now: i64) {
1296 self.parked.insert(
1297 a.clone(),
1298 now.saturating_add(self.config.failure_backoff_ms),
1299 );
1300 }
1301
1302 fn crash(&mut self) {
1303 self.crashed = true;
1304 }
1305
1306 fn process_ready(&mut self, now: i64) -> bool {
1307 let batch: Vec<Address> = {
1308 let mut r = lock(&self.shared.ready);
1309 r.set.clear();
1310 r.queue.drain(..).collect()
1311 };
1312 let mut progress = false;
1313 for a in batch {
1314 if self.crashed {
1315 break;
1316 }
1317 if self.parked.contains_key(&a) {
1318 continue;
1319 }
1320 let Some(kind) = self.shared.kind(&a) else {
1321 continue;
1322 };
1323 let did = match kind {
1324 Kind::Ask => false,
1325 Kind::Worker => self.worker_turns(&a, now),
1326 Kind::Child => self.child_next(&a, now),
1327 Kind::Virtual => self.virtual_turn(&a, now),
1328 };
1329 progress |= did;
1330 if did && self.has_work(&a, kind) {
1331 self.shared.mark_ready(&a);
1332 }
1333 }
1334 progress
1335 }
1336
1337 fn has_work(&self, a: &Address, kind: Kind) -> bool {
1338 if self.migrations.get(a).is_some_and(|q| !q.is_empty()) {
1339 return true;
1340 }
1341 let Some((m, _)) = self.shared.local_mailbox(a) else {
1342 return false;
1343 };
1344 let deferred = match self.actors.get(a) {
1345 Some(Activation::Virtual(v)) => v.deferred(),
1346 Some(Activation::Child(c)) if c.busy => return false,
1347 Some(Activation::Worker(w)) if w.busy >= w.spec.pool => return false,
1348 _ => Vec::new(),
1349 };
1350 let _ = kind;
1351 m.has_deliverable(&deferred)
1352 }
1353
1354 fn load(&mut self, a: &Address, upto: Option<u64>) -> Result<Loaded, ActivationError> {
1360 let j = self.shared.storage.journal();
1361 let recs = j
1362 .read(a.node(), Position::ZERO)
1363 .map_err(ActivationError::Store)?;
1364 let mut entries = Vec::with_capacity(recs.len());
1365 for r in &recs {
1366 if upto.is_some_and(|u| r.position.get() > u) {
1367 break;
1368 }
1369 entries.push(EntryData::from_record(a, r).map_err(ActivationError::Decode)?);
1370 }
1371 let head = entries.len() as u64;
1372 let seen: HashSet<String> = entries.iter().map(|e| e.message_id.clone()).collect();
1373 let (sends, invokes, ioproc) = self.ctx(a);
1374 let registry = self.registry.clone();
1375 let ctx = Ctx {
1376 registry: registry.as_ref(),
1377 ioprocessors: ioproc,
1378 send_types: sends,
1379 invoke_types: invokes,
1380 };
1381 let shared = self.shared.clone();
1382 let charts = |v: &str| shared.charts().get(v);
1383 let mut replayed = 0u64;
1384 let missing = |v: &str| ActivationError::MissingVersion {
1385 actor: a.clone(),
1386 version: v.to_owned(),
1387 };
1388 let mut machine: Option<Machine> = None;
1390 let mut from = 1u64;
1391 for s in self.snapshots(a) {
1392 if s.position > head || s.position == 0 {
1393 continue;
1394 }
1395 let e = &entries[(s.position - 1) as usize];
1396 if e.version != s.version || e.digest != s.digest {
1397 continue;
1398 }
1399 let Some(info) = charts(&s.version) else {
1400 continue;
1401 };
1402 if let Ok(m) = Machine::from_snapshot(a, info, &ctx, &s) {
1403 machine = Some(m);
1404 from = s.position + 1;
1405 break;
1406 }
1407 }
1408 for e in entries.iter().skip((from - 1) as usize) {
1409 match &e.body {
1410 EntryBody::Fork { snapshot, .. } => {
1411 let info =
1412 charts(&snapshot.version).ok_or_else(|| missing(&snapshot.version))?;
1413 let mut m = Machine::from_snapshot(a, info, &ctx, snapshot)
1414 .map_err(|x| ActivationError::Machine(x.to_string()))?;
1415 m.session.sessionid = a.as_str().to_owned();
1416 machine = Some(m);
1417 }
1418 EntryBody::Migration { after, .. } => {
1419 let info = charts(&after.version).ok_or_else(|| missing(&after.version))?;
1420 machine = Some(
1421 Machine::from_snapshot(a, info, &ctx, after)
1422 .map_err(|x| ActivationError::Machine(x.to_string()))?,
1423 );
1424 }
1425 EntryBody::Input { input, effects } => {
1426 let info = charts(&e.version).ok_or_else(|| missing(&e.version))?;
1427 let env = Environment {
1428 now_ms: e.timestamp_ms,
1429 seed: crate::address::seed_of(&e.iri()),
1430 };
1431 let (mut m, start, trace) = match machine.take() {
1432 Some(m) => {
1433 if m.info.version.as_str() != e.version {
1434 return Err(ActivationError::ReplayDivergence {
1435 actor: a.clone(),
1436 position: e.position,
1437 });
1438 }
1439 (m, Vec::new(), None)
1440 }
1441 None => {
1442 let (m, eff, tr) = Machine::start(a, info.clone(), &ctx, env)
1443 .map_err(|x| ActivationError::Machine(x.to_string()))?;
1444 (m, eff, Some(tr))
1445 }
1446 };
1447 let policy = info.unhandled.unwrap_or(UnhandledPolicy::DeadLetter);
1448 let applied = m
1449 .apply(
1450 input,
1451 e.position,
1452 e.timestamp_ms,
1453 policy,
1454 start,
1455 trace.as_ref(),
1456 )
1457 .map_err(|x| ActivationError::Machine(x.to_string()))?;
1458 replayed += 1;
1459 let same_sends = applied.effects.sends.len() == effects.sends.len()
1460 && applied
1461 .effects
1462 .sends
1463 .iter()
1464 .zip(&effects.sends)
1465 .all(|(x, y)| x.send_id == y.send_id && x.event.name == y.event.name);
1466 if m.digest() != e.digest || !same_sends {
1467 return Err(ActivationError::ReplayDivergence {
1468 actor: a.clone(),
1469 position: e.position,
1470 });
1471 }
1472 machine = Some(m);
1473 }
1474 }
1475 }
1476 self.stats.replayed += replayed;
1477 Ok(Loaded {
1478 machine,
1479 head,
1480 seen,
1481 entries,
1482 })
1483 }
1484
1485 fn snapshots(&self, a: &Address) -> Vec<SnapshotData> {
1487 let q = format!(
1488 "SELECT ?s ?n ?v ?d ?ss ?dm ?rt WHERE {{ GRAPH ?s {{ ?s a <{snap}> ; <{actor}> {a} ; \
1489 <{pos}> ?n ; <{ver}> ?v ; <{dig}> ?d ; <{ses}> ?ss ; <{dms}> ?dm ; <{rts}> ?rt }} }}",
1490 snap = vt::SNAPSHOT,
1491 actor = vt::SNAPSHOT_OF,
1492 a = a.node(),
1493 pos = vt::POSITION,
1494 ver = vt::CHART_VERSION,
1495 dig = vt::CONFIGURATION_DIGEST,
1496 ses = vt::SESSION_STATE,
1497 dms = vt::DATAMODEL_STATE,
1498 rts = vt::RUNTIME_STATE,
1499 );
1500 let Ok(sol) = select(self.shared.storage.graph(a.node()), &q) else {
1501 return Vec::new();
1502 };
1503 let mut out = Vec::new();
1504 for i in 0..sol.len() {
1505 let g = |v: &str| sol.get(i, v).and_then(text);
1506 if let (Some(s), Some(n), Some(v), Some(d), Some(ss), Some(dm), Some(rt)) = (
1507 g("s"),
1508 g("n").and_then(|n| n.parse::<u64>().ok()),
1509 g("v"),
1510 g("d"),
1511 g("ss"),
1512 g("dm"),
1513 g("rt"),
1514 ) {
1515 out.push(SnapshotData {
1516 iri: s,
1517 actor: a.as_str().to_owned(),
1518 position: n,
1519 version: v,
1520 digest: d,
1521 session: ss,
1522 datamodel: dm,
1523 runtime: rt,
1524 });
1525 }
1526 }
1527 out.sort_by_key(|x| std::cmp::Reverse(x.position));
1528 out
1529 }
1530
1531 fn outbox_marks(&self, a: &Address) -> (u64, HashSet<String>) {
1532 let q = format!(
1533 "SELECT ?s ?p ?o WHERE {{ GRAPH {g} {{ ?s ?p ?o }} }}",
1534 g = a.outbox_graph()
1535 );
1536 let mut through = 0;
1537 let mut fired = HashSet::new();
1538 if let Ok(sol) = select(self.shared.storage.graph(a.node()), &q) {
1539 for i in 0..sol.len() {
1540 let p = sol.get(i, "p").and_then(text).unwrap_or_default();
1541 if p == vt::DISPATCHED_THROUGH {
1542 through = through.max(
1543 sol.get(i, "o")
1544 .and_then(text)
1545 .and_then(|n| n.parse().ok())
1546 .unwrap_or(0),
1547 );
1548 } else if p == vt::DISPATCHED
1549 && let Some(s) = sol.get(i, "s").and_then(text)
1550 {
1551 fired.insert(s);
1552 }
1553 }
1554 }
1555 (through, fired)
1556 }
1557
1558 fn activate(&mut self, a: &Address, now: i64) -> Result<(), ActivationError> {
1562 let loaded = self.load(a, None)?;
1563 let chart_type = self.shared.charts().type_of(a.as_str());
1564 let first = match &loaded.machine {
1565 Some(m) => m.info.clone(),
1566 None => chart_type
1567 .as_ref()
1568 .and_then(|(t, _)| self.shared.charts().latest(t))
1569 .ok_or_else(|| ActivationError::NoChart(a.clone()))?,
1570 };
1571 let (through, fired) = self.outbox_marks(a);
1572 let mut machine = loaded.machine;
1573 if let Some(m) = &mut machine {
1574 m.rt.timers.retain(|k, _| !fired.contains(k));
1575 }
1576 let ct = chart_type.as_ref().map(|(t, k)| (t.as_str(), k.as_str()));
1577 let state = machine
1578 .as_ref()
1579 .map(|m| m.state_triples(ct))
1580 .unwrap_or_default();
1581 if let Some(m) = &machine {
1583 for t in m.rt.timers.values() {
1584 if !self.timer_keys.contains_key(&t.iri) {
1585 self.arm(t.clone(), now, true);
1586 }
1587 }
1588 }
1589 let invocations: Vec<_> = machine
1590 .as_ref()
1591 .map(|m| m.rt.invocations.values().cloned().collect())
1592 .unwrap_or_default();
1593 *self.stats.activations.entry(a.clone()).or_default() += 1;
1594 self.errors.remove(a);
1595 self.actors.insert(
1596 a.clone(),
1597 Activation::Virtual(Box::new(VirtualActivation {
1598 machine,
1599 first,
1600 chart_type,
1601 head: loaded.head,
1602 seen: loaded.seen,
1603 state,
1604 last_active: now,
1605 })),
1606 );
1607 for e in loaded.entries.iter().filter(|e| e.position > through) {
1609 if let Some(eff) = e.effects() {
1610 self.dispatch_sends(a, e.position, &eff.sends, now);
1611 }
1612 }
1613 if loaded.head > through {
1614 self.advance_outbox(a, loaded.head);
1615 }
1616 for log in invocations {
1618 self.start_child(a, &log.request, &log.events, now);
1619 }
1620 Ok(())
1621 }
1622
1623 fn mark_through(&self, a: &Address, n: u64) {
1626 let g = self.shared.storage.graph(a.node());
1627 let graph = GraphName::NamedNode(a.outbox_graph());
1628 let p = node(vt::DISPATCHED_THROUGH);
1629 let old: Vec<Quad> = crate::storage::graph_quads(g, &a.outbox_graph())
1630 .unwrap_or_default()
1631 .into_iter()
1632 .filter(|q| q.predicate == p)
1633 .collect();
1634 let current = old
1635 .iter()
1636 .filter_map(|q| match &q.object {
1637 Term::Literal(l) => l.value().parse::<u64>().ok(),
1638 _ => None,
1639 })
1640 .max()
1641 .unwrap_or(0);
1642 if current >= n {
1643 return;
1644 }
1645 let _ = g.remove_quads(&old);
1646 let _ = g.insert_quads(&[Quad::new(
1647 a.node().clone(),
1648 p,
1649 Literal::new_typed_literal(n.to_string(), xsd::INTEGER),
1650 graph,
1651 )]);
1652 }
1653
1654 fn mark_fired(&self, scheduler: &str, timer: &str) {
1655 let Ok(a) = Address::parse(scheduler) else {
1656 return;
1657 };
1658 let _ = self
1659 .shared
1660 .storage
1661 .graph(a.node())
1662 .insert_quads(&[Quad::new(
1663 NamedNode::new_unchecked(timer),
1664 node(vt::DISPATCHED),
1665 Literal::new_typed_literal("true", xsd::BOOLEAN),
1666 a.outbox_graph(),
1667 )]);
1668 }
1669
1670 fn virtual_turn(&mut self, a: &Address, now: i64) -> bool {
1678 if let Some(progress) = self.placement_turn(a, now) {
1679 return progress;
1680 }
1681 if !self.actors.contains_key(a)
1682 && let Err(e) = self.activate(a, now)
1683 {
1684 self.errors.insert(a.clone(), e);
1685 self.park(a, now);
1686 return false;
1687 }
1688 if let Some(q) = self.migrations.get_mut(a)
1689 && let Some((to, m)) = q.pop_front()
1690 {
1691 self.self_migrate(a, &to, &m, now);
1692 return true;
1693 }
1694 let Some((mailbox, _)) = self.shared.local_mailbox(a) else {
1695 return false;
1696 };
1697 let Some(Activation::Virtual(mut va)) = self.actors.remove(a) else {
1698 return false;
1699 };
1700 let deferred = va.deferred();
1701 let Some(env) = mailbox.receive(&deferred) else {
1702 self.actors.insert(a.clone(), Activation::Virtual(va));
1703 return false;
1704 };
1705 va.last_active = now;
1706 let drop_reason = self.drop_reason(&va, &env);
1708 if let Some(reason) = drop_reason {
1709 if let Some(dl) = reason {
1710 self.dead_letter(a, &env, dl, now);
1711 }
1712 let _ = mailbox.ack(&env.message_id);
1713 self.actors.insert(a.clone(), Activation::Virtual(va));
1714 return true;
1715 }
1716 let position = va.head + 1;
1717 let entry_iri = a.entry_iri(position);
1718 let input = InputRecord {
1719 event: Event {
1720 type_: EventType::External,
1721 ..env.event.clone()
1722 },
1723 message_id: env.message_id.clone(),
1724 correlation: env.correlation.clone(),
1725 timer: env.timer.clone(),
1726 };
1727 let (sends, invokes, ioproc) = self.ctx(a);
1728 let registry = self.registry.clone();
1729 let info = va.info().clone();
1730 let policy = info.unhandled.unwrap_or(UnhandledPolicy::DeadLetter);
1731 let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
1732 let ctx = Ctx {
1733 registry: registry.as_ref(),
1734 ioprocessors: ioproc,
1735 send_types: sends,
1736 invoke_types: invokes,
1737 };
1738 let (mut m, start, trace) = match va.machine.take() {
1739 Some(m) => (m, Vec::new(), None),
1740 None => {
1741 let env = Environment {
1742 now_ms: now,
1743 seed: crate::address::seed_of(&entry_iri),
1744 };
1745 let (m, eff, tr) = Machine::start(a, info.clone(), &ctx, env)?;
1746 (m, eff, Some(tr))
1747 }
1748 };
1749 let applied = m.apply(&input, position, now, policy, start, trace.as_ref());
1750 Ok::<_, MachineError>((m, applied))
1751 }));
1752 let (machine, applied) = match result {
1753 Ok(Ok((m, Ok(applied)))) => (m, applied),
1754 Ok(Ok((_, Err(MachineError::Terminated)))) => {
1755 self.dead_letter(a, &env, "terminated", now);
1756 let _ = mailbox.ack(&env.message_id);
1757 self.stats.deactivations += 1;
1759 return true;
1760 }
1761 _ => {
1762 mailbox.release(&env.message_id);
1764 self.stats.deactivations += 1;
1765 self.park(a, now);
1766 return true;
1767 }
1768 };
1769 let ct = va
1770 .chart_type
1771 .as_ref()
1772 .map(|(t, k)| (t.as_str(), k.as_str()));
1773 let new_state = machine.state_triples(ct);
1774 let delta = state_delta(&va.state, &new_state);
1775 let data = EntryData {
1776 actor: a.clone(),
1777 position,
1778 version: machine.info.version.as_str().to_owned(),
1779 message_id: env.message_id.clone(),
1780 timestamp_ms: now,
1781 digest: machine.digest(),
1782 body: EntryBody::Input {
1783 input,
1784 effects: applied.effects.clone(),
1785 },
1786 };
1787 if self.config.crash_at == Some(CrashPoint::BeforeAppend) {
1788 self.crash();
1789 return true;
1790 }
1791 if let Err(e) = self.append(a, va.head, &data, delta.clone()) {
1792 mailbox.release(&env.message_id);
1794 self.stats.deactivations += 1;
1795 if !(e.is_conflict() || e.is_fenced()) {
1796 self.errors.insert(a.clone(), ActivationError::Append(e));
1797 self.park(a, now);
1798 }
1799 return true;
1800 }
1801 self.stats.turns += 1;
1802 if self.config.crash_at == Some(CrashPoint::AfterAppend) {
1803 self.crash();
1804 return true;
1805 }
1806 let _ = mailbox.ack(&env.message_id);
1807 va.head = position;
1808 va.seen.insert(env.message_id.clone());
1809 va.state = new_state;
1810 let snapshot = (self.config.snapshot_interval > 0
1811 && position % self.config.snapshot_interval == 0)
1812 .then(|| machine.snapshot(&a.snapshot_iri(position), position));
1813 va.machine = Some(machine);
1814 self.actors.insert(a.clone(), Activation::Virtual(va));
1815 if let Some(s) = snapshot {
1816 self.write_snapshot(a, &s);
1817 }
1818 self.after_append(
1819 a,
1820 &data,
1821 applied.invokes,
1822 applied.cancel_invokes,
1823 applied.migrations,
1824 &env,
1825 now,
1826 );
1827 true
1828 }
1829
1830 fn drop_reason(&self, va: &VirtualActivation, env: &Envelope) -> Option<Option<&'static str>> {
1833 if va.seen.contains(&env.message_id) {
1834 return Some(None);
1835 }
1836 let rt = va.machine.as_ref().map(|m| &m.rt);
1837 if let Some(t) = &env.timer
1838 && rt.is_some_and(|rt| rt.cancelled.contains(t))
1839 {
1840 return Some(None);
1841 }
1842 if let Some(c) = &env.correlation {
1843 let answered = rt.is_some_and(|rt| rt.answered.contains(c));
1844 let open = rt.is_some_and(|rt| rt.requests.contains_key(c));
1845 if env.event.name == vt::REPLY_EVENT && !open {
1846 return Some(if answered { None } else { Some("late-reply") });
1847 }
1848 if env.event.name == vt::TIMEOUT_EVENT && !open {
1849 return Some(None);
1850 }
1851 }
1852 if va
1853 .machine
1854 .as_ref()
1855 .is_some_and(|m| m.session.is_terminated())
1856 {
1857 return Some(Some("terminated"));
1858 }
1859 None
1860 }
1861
1862 fn append(
1863 &self,
1864 a: &Address,
1865 expected: u64,
1866 data: &EntryData,
1867 delta: StateDelta,
1868 ) -> Result<(), AppendError> {
1869 let s = &self.shared.storage;
1870 let caps = s.journal().capabilities();
1871 let entry = data.to_entry();
1872 let mut meta = AppendMeta::none();
1873 if let Some(f) = &self.config.fencing {
1874 meta = meta.with_fencing(f.clone());
1875 }
1876 if caps.state_in_append && !delta.is_empty() {
1877 meta = meta.with_state(delta.clone());
1878 }
1879 s.journal()
1880 .append(a.node(), Position::new(expected), &[entry], &meta)?;
1881 let notes = data.annotations();
1882 if !notes.is_empty() {
1883 let _ = vertep_store::reification::write_annotations(s.graph(a.node()), ¬es);
1884 }
1885 if !caps.state_in_append && !delta.is_empty() {
1886 let g = s.graph(a.node());
1887 let gn = GraphName::NamedNode(a.node().clone());
1888 let r: Vec<Quad> = delta
1889 .retract
1890 .iter()
1891 .map(|t| t.clone().in_graph(gn.clone()))
1892 .collect();
1893 let i: Vec<Quad> = delta
1894 .assert
1895 .iter()
1896 .map(|t| t.clone().in_graph(gn.clone()))
1897 .collect();
1898 let _ = g.remove_quads(&r);
1899 let _ = g.insert_quads(&i);
1900 }
1901 Ok(())
1902 }
1903
1904 fn write_snapshot(&self, a: &Address, s: &SnapshotData) {
1905 let g = NamedNode::new_unchecked(&s.iri);
1906 let quads: Vec<Quad> = crate::journal::snapshot_triples(s)
1907 .into_iter()
1908 .map(|t| t.in_graph(g.clone()))
1909 .collect();
1910 let _ = self.shared.storage.graph(a.node()).insert_quads(&quads);
1911 }
1912
1913 #[allow(clippy::too_many_arguments)]
1916 fn after_append(
1917 &mut self,
1918 a: &Address,
1919 data: &EntryData,
1920 invokes: Vec<InvokeRequest>,
1921 cancel_invokes: Vec<String>,
1922 migrations: Vec<(String, String)>,
1923 env: &Envelope,
1924 now: i64,
1925 ) {
1926 let Some(eff) = data.effects() else { return };
1927 self.dispatch_sends(a, data.position, &eff.sends, now);
1928 if !eff.sends.is_empty() {
1929 self.advance_outbox(a, data.position);
1930 }
1931 for c in &eff.cancels {
1932 self.disarm(c);
1933 }
1934 for t in &eff.timers {
1935 self.arm(t.clone(), now, false);
1936 }
1937 if eff.unhandled == Some(UnhandledPolicy::DeadLetter) {
1938 self.dead_letter(a, env, "unhandled", now);
1939 }
1940 for id in cancel_invokes {
1941 self.cancel_child(&Address::invoked(a, &id));
1942 }
1943 for req in invokes {
1944 self.start_child(a, &req, &[], now);
1945 }
1946 if !migrations.is_empty() {
1947 self.migrations
1948 .entry(a.clone())
1949 .or_default()
1950 .extend(migrations);
1951 self.shared.mark_ready(a);
1952 }
1953 }
1954
1955 fn communication_error(&self, to: &Address, about: &str, send_id: &str) {
1956 let mut ev = platform_event("error.communication", Value::Undefined);
1957 ev.sendid = Some(send_id.to_owned());
1958 let env = Envelope::new(format!("{about}/error"), to, ev);
1959 let _ = self.shared.deliver(env, SCXML_EVENT_PROCESSOR);
1960 }
1961
1962 fn dispatch_sends(&mut self, from: &Address, position: u64, sends: &[SendRecord], now: i64) {
1965 let refused = sends
1966 .iter()
1967 .filter(|s| !self.dispatch_send(from, position, s, now))
1968 .count();
1969 let o = self.outbox.entry(from.clone()).or_default();
1970 o.tip = o.tip.max(position);
1971 if refused > 0 {
1972 *o.owed.entry(position).or_default() += refused;
1973 }
1974 }
1975
1976 fn advance_outbox(&mut self, a: &Address, upto: u64) {
1980 let o = self.outbox.entry(a.clone()).or_default();
1981 o.tip = o.tip.max(upto);
1982 let through = match o.owed.keys().next() {
1983 Some(&p) => p.saturating_sub(1).min(o.tip),
1984 None => o.tip,
1985 };
1986 if through > 0 {
1987 self.mark_through(a, through);
1988 }
1989 }
1990
1991 fn settle_send(&mut self, from: &Address, position: u64) {
1993 let Some(o) = self.outbox.get_mut(from) else {
1994 return;
1995 };
1996 if let Some(n) = o.owed.get_mut(&position) {
1997 *n = n.saturating_sub(1);
1998 if *n == 0 {
1999 o.owed.remove(&position);
2000 }
2001 }
2002 let tip = o.tip;
2003 self.advance_outbox(from, tip);
2004 }
2005
2006 fn dispatch_send(&mut self, from: &Address, position: u64, s: &SendRecord, now: i64) -> bool {
2011 let env = Envelope {
2012 message_id: s.iri.clone(),
2013 to: s.recipient.clone().unwrap_or_default(),
2014 event: s.event.clone(),
2015 correlation: s.correlation.clone(),
2016 timer: None,
2017 };
2018 let type_ = if s.type_.starts_with(vt::REQUEST_SEND_TYPE) {
2019 SCXML_EVENT_PROCESSOR
2020 } else {
2021 s.type_.as_str()
2022 };
2023 match self.shared.deliver(env.clone(), type_) {
2024 Ok(()) => {}
2025 Err(DeliveryError::Unresolvable(t)) => {
2026 self.communication_error(from, &s.iri, &s.send_id);
2027 let receiver = Address::parse(&t).unwrap_or_else(|_| from.clone());
2028 self.dead_letter(&receiver, &env, "unresolvable", now);
2029 }
2030 Err(DeliveryError::Transport(_)) => {
2031 self.seq += 1;
2033 self.volatile.insert(
2034 (now.saturating_add(self.config.failure_backoff_ms), self.seq),
2035 Volatile::Resend(from.clone(), position, Box::new(s.clone())),
2036 );
2037 return false;
2038 }
2039 }
2040 true
2041 }
2042
2043 fn dead_letter(&self, receiver: &Address, env: &Envelope, reason: &str, now: i64) {
2046 self.shared.dead_letter(receiver, env, reason, now);
2047 }
2048}
2049
2050impl Shared {
2051 fn dead_letter(&self, receiver: &Address, env: &Envelope, reason: &str, now: i64) {
2053 let g = receiver.dead_letter_graph();
2054 let d = receiver.dead_letter_iri(&env.message_id);
2055 let q = |p: &str, o: Term| Quad::new(d.clone(), node(p), o, g.clone());
2056 let mut quads = vec![
2057 q(vt::RDF_TYPE, node(vt::DEAD_LETTER).into()),
2058 q(vt::RECEIVER, receiver.node().clone().into()),
2059 q(
2060 vt::EVENT_NAME,
2061 Literal::new_simple_literal(&env.event.name).into(),
2062 ),
2063 q(
2064 vt::PAYLOAD,
2065 Literal::new_simple_literal(crate::json::to_text(&env.event.data)).into(),
2066 ),
2067 q(vt::REASON, Literal::new_simple_literal(reason).into()),
2068 q(
2069 vt::RECORDED_AT,
2070 Literal::new_typed_literal(now.to_string(), xsd::INTEGER).into(),
2071 ),
2072 q(
2073 vt::MESSAGE_ID,
2074 Literal::new_simple_literal(&env.message_id).into(),
2075 ),
2076 ];
2077 if let Some(o) = &env.event.origin {
2078 quads.push(q(vt::SENDER, Literal::new_simple_literal(o).into()));
2079 }
2080 let _ = self.storage.graph(receiver.node()).insert_quads(&quads);
2081 }
2082}
2083
2084impl Node {
2085 pub fn dead_letters(&self, receiver: &Address) -> Result<Vec<DeadLetter>, StoreError> {
2091 let q = format!(
2092 "SELECT ?d ?sender ?name ?payload ?reason ?at WHERE {{ GRAPH {g} {{ ?d a <{dl}> ; \
2093 <{rcv}> {r} ; <{en}> ?name ; <{pl}> ?payload ; <{rs}> ?reason ; <{at}> ?at . \
2094 OPTIONAL {{ ?d <{snd}> ?sender }} }} }} ORDER BY ?at ?d",
2095 g = receiver.dead_letter_graph(),
2096 dl = vt::DEAD_LETTER,
2097 rcv = vt::RECEIVER,
2098 r = receiver.node(),
2099 en = vt::EVENT_NAME,
2100 pl = vt::PAYLOAD,
2101 rs = vt::REASON,
2102 at = vt::RECORDED_AT,
2103 snd = vt::SENDER,
2104 );
2105 let sol = select(self.shared.storage.graph(receiver.node()), &q)?;
2106 let mut out = Vec::new();
2107 for i in 0..sol.len() {
2108 let g = |v: &str| sol.get(i, v).and_then(text);
2109 out.push(DeadLetter {
2110 iri: g("d").unwrap_or_default(),
2111 receiver: receiver.as_str().to_owned(),
2112 sender: g("sender"),
2113 event: g("name").unwrap_or_default(),
2114 payload: g("payload").unwrap_or_default(),
2115 reason: g("reason").unwrap_or_default(),
2116 recorded_at: g("at").and_then(|x| x.parse().ok()).unwrap_or_default(),
2117 });
2118 }
2119 Ok(out)
2120 }
2121
2122 fn deactivate_idle(&mut self, now: i64) -> bool {
2123 let idle: Vec<Address> = self
2124 .actors
2125 .iter()
2126 .filter_map(|(a, act)| match act {
2127 Activation::Virtual(v)
2128 if now.saturating_sub(v.last_active) >= self.config.idle_timeout_ms =>
2129 {
2130 Some((a, v))
2131 }
2132 _ => None,
2133 })
2134 .filter(|(a, v)| {
2135 let busy = self
2136 .shared
2137 .local_mailbox(a)
2138 .is_some_and(|(m, _)| m.has_deliverable(&v.deferred()));
2139 let children = v
2140 .machine
2141 .as_ref()
2142 .is_some_and(|m| !m.rt.invocations.is_empty());
2143 !busy && !children && self.migrations.get(*a).is_none_or(|q| q.is_empty())
2144 })
2145 .map(|(a, _)| a.clone())
2146 .collect();
2147 for a in &idle {
2148 self.deactivate(a);
2149 }
2150 !idle.is_empty()
2151 }
2152
2153 pub fn deactivate(&mut self, a: &Address) {
2156 if self.actors.remove(a).is_some() {
2157 self.stats.deactivations += 1;
2158 if let Some((m, _)) = self.shared.local_mailbox(a) {
2159 m.release_all();
2160 }
2161 }
2162 }
2163
2164 pub fn active_actors(&self) -> Vec<Address> {
2166 self.actors
2167 .iter()
2168 .filter(|(_, act)| matches!(act, Activation::Virtual(_)))
2169 .map(|(a, _)| a.clone())
2170 .collect()
2171 }
2172
2173 pub fn drain(&mut self, which: &dyn Fn(&Address) -> bool) -> Vec<Address> {
2178 self.run_until_idle();
2179 let drained: Vec<Address> = self
2180 .active_actors()
2181 .into_iter()
2182 .filter(|a| which(a))
2183 .collect();
2184 for a in &drained {
2185 self.deactivate(a);
2186 }
2187 drained
2188 }
2189
2190 fn placement_turn(&mut self, a: &Address, now: i64) -> Option<bool> {
2194 let p = self.shared.placement.clone()?;
2195 match p.route(a, true, None) {
2196 Route::Local => None,
2197 Route::Hold => {
2198 self.deactivate(a);
2199 self.park(a, now);
2200 Some(false)
2201 }
2202 Route::Remote(remote) => {
2203 self.deactivate(a);
2204 let Some((m, _)) = self.shared.local_mailbox(a) else {
2205 return Some(false);
2206 };
2207 let mut moved = false;
2208 for env in m.pending() {
2209 let id = env.message_id.clone();
2210 if remote.enqueue(env).is_ok() {
2211 let _ = m.ack(&id);
2212 moved = true;
2213 } else {
2214 self.park(a, now);
2215 break;
2216 }
2217 }
2218 Some(moved)
2219 }
2220 }
2221 }
2222
2223 fn arm(&mut self, rec: TimerRecord, now: i64, catch_up: bool) {
2228 self.disarm(&rec.iri);
2229 let mut occurrence = 0;
2230 if catch_up
2231 && let Some(r) = &rec.reminder
2232 && rec.due_ms <= now
2233 && r.period_ms > 0
2234 {
2235 occurrence = u64::try_from((now - rec.due_ms) / r.period_ms).unwrap_or(0);
2236 }
2237 let key = (rec.due_of(occurrence), rec.iri.clone());
2238 self.timer_keys.insert(rec.iri.clone(), key.clone());
2239 self.timers.insert(key, Armed { rec, occurrence });
2240 }
2241
2242 fn disarm(&mut self, iri: &str) {
2243 if let Some(k) = self.timer_keys.remove(iri) {
2244 self.timers.remove(&k);
2245 }
2246 }
2247
2248 fn fire_timers(&mut self, now: i64) -> bool {
2253 let mut progress = false;
2254 while let Some(((due, _), _)) = self.timers.first_key_value() {
2255 if *due > now {
2256 break;
2257 }
2258 let Some((_, armed)) = self.timers.pop_first() else {
2259 break;
2260 };
2261 progress = true;
2262 let rec = armed.rec;
2263 self.timer_keys.remove(&rec.iri);
2264 let env = Envelope {
2265 message_id: rec.message_id(armed.occurrence),
2266 to: rec.recipient.clone(),
2267 event: rec.event.clone(),
2268 correlation: rec.correlation.clone(),
2269 timer: Some(rec.iri.clone()),
2270 };
2271 match self.shared.deliver(env.clone(), SCXML_EVENT_PROCESSOR) {
2272 Ok(()) => {}
2273 Err(DeliveryError::Unresolvable(t)) => {
2274 if let Ok(s) = Address::parse(&rec.actor) {
2275 self.communication_error(&s, &rec.iri, &rec.send_id);
2276 let receiver = Address::parse(&t).unwrap_or_else(|_| s.clone());
2277 self.dead_letter(&receiver, &env, "unresolvable", now);
2278 }
2279 }
2280 Err(DeliveryError::Transport(_)) => {
2281 let key = (
2283 now.saturating_add(self.config.failure_backoff_ms),
2284 rec.iri.clone(),
2285 );
2286 self.timer_keys.insert(rec.iri.clone(), key.clone());
2287 self.timers.insert(
2288 key,
2289 Armed {
2290 rec,
2291 occurrence: armed.occurrence,
2292 },
2293 );
2294 continue;
2295 }
2296 }
2297 if let Some(r) = &rec.reminder {
2298 let mut next = armed.occurrence + 1;
2300 if r.period_ms > 0 && rec.due_of(next) <= now {
2301 next = u64::try_from((now - rec.due_ms) / r.period_ms).unwrap_or(0) + 1;
2302 }
2303 let key = (rec.due_of(next), rec.iri.clone());
2304 self.timer_keys.insert(rec.iri.clone(), key.clone());
2305 self.timers.insert(
2306 key,
2307 Armed {
2308 rec,
2309 occurrence: next,
2310 },
2311 );
2312 } else {
2313 self.mark_fired(&rec.actor, &rec.iri);
2314 if let Ok(s) = Address::parse(&rec.actor)
2315 && s.as_str() != rec.recipient
2316 && let Some(Activation::Virtual(v)) = self.actors.get_mut(&s)
2317 && let Some(m) = &mut v.machine
2318 {
2319 m.rt.timers.remove(&rec.iri);
2320 }
2321 }
2322 }
2323 progress
2324 }
2325
2326 fn fire_volatile(&mut self, now: i64) -> bool {
2327 let mut progress = false;
2328 while let Some(((due, _), _)) = self.volatile.first_key_value() {
2329 if *due > now {
2330 break;
2331 }
2332 let Some((_, v)) = self.volatile.pop_first() else {
2333 break;
2334 };
2335 progress = true;
2336 match v {
2337 Volatile::Deliver(env) => {
2338 if self
2339 .shared
2340 .deliver((*env).clone(), SCXML_EVENT_PROCESSOR)
2341 .is_err()
2342 {
2343 self.seq += 1;
2344 self.volatile.insert(
2345 (now.saturating_add(self.config.failure_backoff_ms), self.seq),
2346 Volatile::Deliver(env),
2347 );
2348 }
2349 }
2350 Volatile::Resend(from, position, s) => {
2351 if self.dispatch_send(&from, position, &s, now) {
2352 self.settle_send(&from, position);
2353 }
2354 }
2355 Volatile::Restart(a, generation) => self.restart_child(&a, generation, now),
2356 }
2357 }
2358 progress
2359 }
2360
2361 fn recover(&mut self) {
2366 let now = self.now_ms();
2367 let storage = self.shared.storage.clone();
2368 let q_timers = format!(
2369 "SELECT ?env WHERE {{ GRAPH ?a {{ ?t <{actor}> ?a ; <{env}> ?env }} }}",
2370 actor = vt::ACTOR,
2371 env = vt::ENVELOPE
2372 );
2373 let q_fired = format!(
2374 "SELECT ?t WHERE {{ GRAPH ?g {{ ?t <{d}> ?x }} }}",
2375 d = vt::DISPATCHED
2376 );
2377 let mut fired = HashSet::new();
2378 let mut pending = Vec::new();
2379 for g in storage.graphs() {
2380 if let Ok(sol) = select(g, &q_fired) {
2381 fired.extend(sol.values("t").iter().filter_map(text));
2382 }
2383 if let Ok(sol) = select(g, &q_timers) {
2384 for t in sol.values("env") {
2385 if let Some(rec) =
2386 text(&t).and_then(|j| serde_json::from_str::<TimerRecord>(&j).ok())
2387 {
2388 pending.push(rec);
2389 }
2390 }
2391 }
2392 }
2393 for rec in pending {
2394 if !fired.contains(&rec.iri) {
2395 self.arm(rec, now, true);
2396 }
2397 }
2398 if let Ok(actors) = storage.journal().actors() {
2400 for n in actors {
2401 let Ok(a) = Address::parse(n.as_str()) else {
2402 continue;
2403 };
2404 let head = storage.journal().head(&n).map(|h| h.get()).unwrap_or(0);
2405 let (through, _) = self.outbox_marks(&a);
2406 if head <= through {
2407 continue;
2408 }
2409 let Ok(recs) = storage.journal().read(&n, Position::new(through + 1)) else {
2410 continue;
2411 };
2412 for r in recs {
2413 if let Ok(e) = EntryData::from_record(&a, &r)
2414 && let Some(eff) = e.effects()
2415 {
2416 self.dispatch_sends(&a, e.position, &eff.sends, now);
2417 }
2418 }
2419 self.advance_outbox(&a, head);
2420 }
2421 }
2422 for f in self.factories.clone() {
2423 for a in f.recover() {
2424 self.shared.mark_ready(&a);
2425 }
2426 }
2427 }
2428
2429 fn is_virtual(&self, src: &str) -> bool {
2432 self.shared.charts().type_of(src).is_some()
2433 }
2434
2435 fn start_child(
2439 &mut self,
2440 parent: &Address,
2441 req: &InvokeRequest,
2442 events: &[(Event, i64)],
2443 now: i64,
2444 ) {
2445 let child = Address::invoked(parent, &req.invokeid);
2446 let fail = |node: &mut Node, cause: String| {
2447 let ev = platform_event(
2448 "error.execution",
2449 Value::Object(vec![
2450 ("invokeid".into(), Value::String(req.invokeid.clone())),
2451 ("cause".into(), Value::String(cause)),
2452 ]),
2453 );
2454 let env = Envelope::new(format!("{child}/start-error"), parent, ev);
2455 let _ = node.shared.deliver(env, SCXML_EVENT_PROCESSOR);
2456 };
2457 if let Some(src) = &req.src
2458 && self.is_virtual(src)
2459 {
2460 fail(
2461 self,
2462 format!("{src} is a virtual actor and cannot be invoked"),
2463 );
2464 return;
2465 }
2466 let start = ActorStart {
2467 address: child.clone(),
2468 parent: parent.clone(),
2469 invokeid: req.invokeid.clone(),
2470 src: req.src.clone(),
2471 content: req.content.clone(),
2472 params: req.params.clone(),
2473 };
2474 let make: Arc<dyn Fn() -> Result<Box<dyn Actor>, ActorFailure> + Send + Sync> =
2475 if is_scxml_invoke(&req.type_) {
2476 let info = match self.child_chart(req) {
2477 Ok(i) => i,
2478 Err(e) => {
2479 fail(self, e);
2480 return;
2481 }
2482 };
2483 let registry = self.registry.clone();
2484 let io = self.shared.transports.ioprocessors(&child);
2485 let data: Vec<(String, Value)> = match &req.params {
2486 Value::Object(kv) => kv.clone(),
2487 _ => Vec::new(),
2488 };
2489 let addr = child.clone();
2490 Arc::new(move || {
2491 Ok(Box::new(ChartActor::new(
2492 addr.clone(),
2493 info.clone(),
2494 registry.clone(),
2495 data.clone(),
2496 io.clone(),
2497 )) as Box<dyn Actor>)
2498 })
2499 } else if let Some(f) = self.kinds.get(&req.type_).cloned() {
2500 let start = start.clone();
2501 Arc::new(move || f(&start))
2502 } else {
2503 fail(self, format!("no actor kind handles {}", req.type_));
2504 return;
2505 };
2506 let actor = match make() {
2507 Ok(a) => a,
2508 Err(e) => {
2509 fail(self, e.0);
2510 return;
2511 }
2512 };
2513 let policy = self
2514 .actors
2515 .get(parent)
2516 .and_then(|p| match p {
2517 Activation::Virtual(v) => {
2518 Some(v.info().restart_policy(&req.state, index_of(v, req)))
2519 }
2520 _ => None,
2521 })
2522 .unwrap_or_default();
2523 self.shared.hosted_mut().children.insert(child.clone());
2524 self.seq += 1;
2525 let generation = self.seq;
2526 self.actors.insert(
2527 child.clone(),
2528 Activation::Child(Box::new(ChildActivation {
2529 parent: parent.clone(),
2530 invokeid: req.invokeid.clone(),
2531 make,
2532 actor,
2533 busy: false,
2534 generation,
2535 policy,
2536 failures: Vec::new(),
2537 out_seq: 0,
2538 suppress: !events.is_empty(),
2539 })),
2540 );
2541 let mut start_ev = Event::named(START_EVENT, EventType::External);
2542 start_ev.data = req.params.clone();
2543 self.child_input(&child, start_ev, None, None, now);
2544 for (e, ts) in events {
2545 self.child_input(&child, e.clone(), None, None, *ts);
2546 }
2547 if let Some(Activation::Child(c)) = self.actors.get_mut(&child) {
2548 c.suppress = false;
2549 }
2550 }
2551
2552 fn child_chart(&mut self, req: &InvokeRequest) -> Result<Arc<ChartInfo>, String> {
2553 if let Some(src) = &req.src {
2554 let charts = self.shared.charts();
2555 return charts
2556 .latest(src)
2557 .or_else(|| charts.get(src))
2558 .ok_or_else(|| format!("no chart registered for {src}"));
2559 }
2560 let Some(Value::String(xml)) = &req.content else {
2561 return Err("an scxml invoke needs src or inline content".into());
2562 };
2563 let opts = vertep_scxml::ReadOptions::new().with_datamodels(&vertep_scxml::AnyDatamodel);
2564 let parsed = vertep_scxml::read(xml, &opts).map_err(|e| format!("{e:?}"))?;
2565 let info = ChartInfo::new(parsed.chart, None).map_err(|e| e.to_string())?;
2566 Ok(self.shared.charts_mut().insert(info))
2567 }
2568
2569 fn cancel_child(&mut self, child: &Address) {
2570 if self.actors.remove(child).is_some() {
2571 self.shared.hosted_mut().children.remove(child);
2572 lock(&self.shared.mailboxes).remove(child);
2573 }
2574 }
2575
2576 fn child_input(
2578 &mut self,
2579 child: &Address,
2580 event: Event,
2581 message_id: Option<String>,
2582 origin_correlation: Option<(Option<String>, Option<String>)>,
2583 now: i64,
2584 ) {
2585 let Some(Activation::Child(c)) = self.actors.get_mut(child) else {
2586 return;
2587 };
2588 let input = ActorInput {
2589 event: event.clone(),
2590 message_id: message_id.clone().unwrap_or_default(),
2591 received_at_ms: now,
2592 };
2593 let generation = c.generation;
2594 let turn = std::panic::catch_unwind(AssertUnwindSafe(|| c.actor.turn(input)))
2595 .unwrap_or_else(|_| Turn::Ready(Err(ActorFailure("panic".into()))));
2596 let (origin, correlation) = origin_correlation.unwrap_or((event.origin.clone(), None));
2597 match turn {
2598 Turn::Ready(result) => {
2599 self.child_done(
2600 Completion {
2601 address: child.clone(),
2602 generation,
2603 message_id,
2604 origin,
2605 correlation,
2606 result,
2607 },
2608 now,
2609 );
2610 }
2611 Turn::Pending(fut) => {
2612 c.busy = true;
2613 let shared = self.shared.clone();
2614 let address = child.clone();
2615 shared.jobs.fetch_add(1, Ordering::SeqCst);
2616 let job = async move {
2617 let result = AssertUnwindSafe(fut)
2618 .catch_unwind()
2619 .await
2620 .unwrap_or_else(|_| Err(ActorFailure("panic".into())));
2621 shared.complete(Completion {
2622 address,
2623 generation,
2624 message_id,
2625 origin,
2626 correlation,
2627 result,
2628 });
2629 };
2630 self.shared.shell.spawn.spawn(Box::pin(job));
2631 }
2632 }
2633 }
2634
2635 fn child_next(&mut self, child: &Address, now: i64) -> bool {
2636 let Some(Activation::Child(c)) = self.actors.get(child) else {
2637 return false;
2638 };
2639 if c.busy {
2640 return false;
2641 }
2642 let Some((m, _)) = self.shared.local_mailbox(child) else {
2643 return false;
2644 };
2645 let Some(env) = m.receive(&[]) else {
2646 return false;
2647 };
2648 let origin = env.event.origin.clone();
2649 self.child_input(
2650 child,
2651 env.event,
2652 Some(env.message_id),
2653 Some((origin, env.correlation)),
2654 now,
2655 );
2656 true
2657 }
2658
2659 fn drain_completions(&mut self, now: i64) -> bool {
2660 let done: Vec<Completion> = lock(&self.shared.completions).drain(..).collect();
2661 let progress = !done.is_empty();
2662 for c in done {
2663 match self.actors.get(&c.address) {
2664 Some(Activation::Child(_)) => self.child_done(c, now),
2665 Some(Activation::Worker(_)) => self.worker_done(c, now),
2666 _ => {}
2667 }
2668 }
2669 progress
2670 }
2671
2672 fn child_done(&mut self, c: Completion, now: i64) {
2673 let Some(Activation::Child(ca)) = self.actors.get_mut(&c.address) else {
2674 return;
2675 };
2676 if ca.generation != c.generation {
2677 return;
2678 }
2679 ca.busy = false;
2680 if let Some(id) = &c.message_id
2681 && let Some((m, _)) = self.shared.local_mailbox(&c.address)
2682 {
2683 let _ = m.ack(id);
2684 }
2685 let parent = ca.parent.clone();
2686 let invokeid = ca.invokeid.clone();
2687 let suppress = ca.suppress;
2688 match c.result {
2689 Ok(out) => {
2690 if !suppress {
2691 for o in out.sends {
2692 self.child_send(
2693 &c.address,
2694 &parent,
2695 &invokeid,
2696 o,
2697 c.origin.as_deref(),
2698 c.correlation.as_deref(),
2699 now,
2700 );
2701 }
2702 }
2703 if let Some(data) = out.done {
2704 let restart = matches!(
2705 self.actors.get(&c.address),
2706 Some(Activation::Child(x)) if x.policy.mode == RestartMode::Always
2707 );
2708 if restart {
2709 self.child_failed(&c.address, "completed".into(), now);
2710 } else {
2711 if !suppress {
2712 let mut ev = Event::named(
2713 format!("done.invoke.{invokeid}"),
2714 EventType::External,
2715 );
2716 ev.data = data;
2717 ev.invokeid = Some(invokeid.clone());
2718 ev.origin = Some(c.address.as_str().to_owned());
2719 ev.origintype = Some(SCXML_EVENT_PROCESSOR.to_owned());
2720 ev.originverified = false;
2721 let env = Envelope::new(
2722 format!("{}/{}/done", c.address, c.generation),
2723 &parent,
2724 ev,
2725 );
2726 let _ = self.shared.deliver(env, SCXML_EVENT_PROCESSOR);
2727 }
2728 self.cancel_child(&c.address);
2729 }
2730 } else {
2731 self.shared.mark_ready(&c.address);
2732 }
2733 }
2734 Err(f) => self.child_failed(&c.address, f.0, now),
2735 }
2736 }
2737
2738 #[allow(clippy::too_many_arguments)]
2739 fn child_send(
2740 &mut self,
2741 child: &Address,
2742 parent: &Address,
2743 invokeid: &str,
2744 o: Outgoing,
2745 origin: Option<&str>,
2746 correlation: Option<&str>,
2747 now: i64,
2748 ) {
2749 let mut ev = o.event;
2750 ev.origin = Some(child.as_str().to_owned());
2751 ev.origintype = Some(SCXML_EVENT_PROCESSOR.to_owned());
2752 ev.originverified = false;
2753 let to = match o.target {
2754 OutTarget::Parent => {
2755 ev.invokeid = Some(invokeid.to_owned());
2756 Some(parent.clone())
2757 }
2758 OutTarget::Origin => origin.and_then(|x| Address::parse(x).ok()),
2759 OutTarget::Itself => Some(child.clone()),
2760 OutTarget::Address(a) => Some(a),
2761 OutTarget::Invalid(_) => None,
2762 };
2763 let generation = match self.actors.get_mut(child) {
2764 Some(Activation::Child(c)) => {
2765 c.out_seq += 1;
2766 format!("{}/{}", c.generation, c.out_seq)
2767 }
2768 _ => {
2769 self.seq += 1;
2770 format!("w/{}", self.seq)
2771 }
2772 };
2773 let Some(to) = to else {
2774 self.communication_error(
2775 child,
2776 &format!("{child}/{generation}"),
2777 ev.sendid.as_deref().unwrap_or(""),
2778 );
2779 return;
2780 };
2781 let mut env = Envelope::new(
2782 format!("{child}/{}-{generation}", self.shared.nonce),
2783 &to,
2784 ev,
2785 );
2786 if env.event.name == vt::REPLY_EVENT {
2787 env.correlation = correlation.map(str::to_owned);
2788 }
2789 if o.delay_ms > 0 {
2790 self.seq += 1;
2791 self.volatile.insert(
2792 (
2793 now.saturating_add(i64::try_from(o.delay_ms).unwrap_or(i64::MAX)),
2794 self.seq,
2795 ),
2796 Volatile::Deliver(Box::new(env)),
2797 );
2798 } else if let Err(DeliveryError::Unresolvable(_)) =
2799 self.shared.deliver(env.clone(), SCXML_EVENT_PROCESSOR)
2800 {
2801 self.dead_letter(&to, &env, "unresolvable", now);
2802 }
2803 }
2804
2805 fn child_failed(&mut self, child: &Address, cause: String, now: i64) {
2807 let Some(Activation::Child(ca)) = self.actors.get_mut(child) else {
2808 return;
2809 };
2810 let p = ca.policy;
2811 let give_up = if p.mode == RestartMode::Never {
2812 true
2813 } else {
2814 ca.failures.retain(|t| *t > now.saturating_sub(p.window_ms));
2815 ca.failures.push(now);
2816 ca.failures.len() as u32 > p.limit
2817 };
2818 let parent = ca.parent.clone();
2819 let invokeid = ca.invokeid.clone();
2820 let generation = ca.generation;
2821 if give_up {
2822 let ev = platform_event(
2823 "error.execution",
2824 Value::Object(vec![
2825 ("invokeid".into(), Value::String(invokeid.clone())),
2826 ("cause".into(), Value::String(cause)),
2827 ]),
2828 );
2829 let mut ev = ev;
2830 ev.invokeid = Some(invokeid);
2831 let env = Envelope::new(format!("{child}/{generation}/failed"), &parent, ev);
2832 let _ = self.shared.deliver(env, SCXML_EVENT_PROCESSOR);
2833 self.cancel_child(child);
2834 } else {
2835 let n = ca.failures.len() as u32;
2836 ca.busy = true;
2837 let at = now.saturating_add(p.backoff(n));
2838 self.seq += 1;
2839 self.volatile
2840 .insert((at, self.seq), Volatile::Restart(child.clone(), generation));
2841 self.stats.restarts.push((child.clone(), at));
2842 }
2843 }
2844
2845 fn restart_child(&mut self, child: &Address, generation: u64, now: i64) {
2846 let Some(Activation::Child(ca)) = self.actors.get_mut(child) else {
2847 return;
2848 };
2849 if ca.generation != generation {
2850 return;
2851 }
2852 match (ca.make)() {
2853 Ok(actor) => {
2854 self.seq += 1;
2855 ca.actor = actor;
2856 ca.generation = self.seq;
2857 ca.busy = false;
2858 let ev = Event::named(START_EVENT, EventType::External);
2859 self.child_input(child, ev, None, None, now);
2860 }
2861 Err(e) => self.child_failed(child, e.0, now),
2862 }
2863 }
2864
2865 fn worker_turns(&mut self, a: &Address, now: i64) -> bool {
2868 if !self.actors.contains_key(a) {
2869 let Some(spec) = self.shared.hosted().workers.get(a).cloned() else {
2870 return false;
2871 };
2872 *self.stats.activations.entry(a.clone()).or_default() += 1;
2873 self.actors
2874 .insert(a.clone(), Activation::Worker(WorkerPool { spec, busy: 0 }));
2875 }
2876 let Some((m, _)) = self.shared.local_mailbox(a) else {
2877 return false;
2878 };
2879 let mut progress = false;
2880 loop {
2881 let Some(Activation::Worker(pool)) = self.actors.get_mut(a) else {
2882 return progress;
2883 };
2884 if pool.busy >= pool.spec.pool {
2885 return progress;
2886 }
2887 let Some(env) = m.receive(&[]) else {
2888 return progress;
2889 };
2890 pool.busy += 1;
2891 let busy = pool.busy;
2892 let c = self.stats.worker_concurrency.entry(a.clone()).or_default();
2893 *c = (*c).max(busy);
2894 let mut actor = FnActor::new(a.clone(), pool.spec.handler.clone());
2895 let input = ActorInput {
2896 event: env.event.clone(),
2897 message_id: env.message_id.clone(),
2898 received_at_ms: now,
2899 };
2900 let shared = self.shared.clone();
2901 let completion = |result| Completion {
2902 address: a.clone(),
2903 generation: 0,
2904 message_id: Some(env.message_id.clone()),
2905 origin: env.event.origin.clone(),
2906 correlation: env.correlation.clone(),
2907 result,
2908 };
2909 match actor.turn(input) {
2910 Turn::Ready(r) => {
2911 shared.jobs.fetch_add(1, Ordering::SeqCst);
2912 shared.complete(completion(r));
2913 }
2914 Turn::Pending(fut) => {
2915 shared.jobs.fetch_add(1, Ordering::SeqCst);
2916 let mut c = completion(Ok(TurnOutput::default()));
2917 let s2 = shared.clone();
2918 self.shared.shell.spawn.spawn(Box::pin(async move {
2919 c.result = AssertUnwindSafe(fut)
2920 .catch_unwind()
2921 .await
2922 .unwrap_or_else(|_| Err(ActorFailure("panic".into())));
2923 s2.complete(c);
2924 }));
2925 }
2926 }
2927 progress = true;
2928 }
2929 }
2930
2931 fn worker_done(&mut self, c: Completion, now: i64) {
2932 if let Some(Activation::Worker(pool)) = self.actors.get_mut(&c.address) {
2933 pool.busy = pool.busy.saturating_sub(1);
2934 }
2935 if let (Some(id), Some((m, _))) = (&c.message_id, self.shared.local_mailbox(&c.address)) {
2936 let _ = m.ack(id);
2937 }
2938 if let Ok(out) = c.result {
2939 for o in out.sends {
2940 let mut ev = o.event;
2941 ev.origin = Some(c.address.as_str().to_owned());
2942 ev.origintype = Some(SCXML_EVENT_PROCESSOR.to_owned());
2943 ev.originverified = false;
2944 let to = match o.target {
2945 OutTarget::Origin => c.origin.as_deref().and_then(|x| Address::parse(x).ok()),
2946 OutTarget::Address(a) => Some(a),
2947 _ => None,
2948 };
2949 let Some(to) = to else { continue };
2950 self.seq += 1;
2951 let mut env = Envelope::new(
2952 format!("{}/{}-{}", c.address, self.shared.nonce, self.seq),
2953 &to,
2954 ev,
2955 );
2956 if env.event.name == vt::REPLY_EVENT {
2957 env.correlation = c.correlation.clone();
2958 }
2959 if let Err(DeliveryError::Unresolvable(_)) =
2960 self.shared.deliver(env.clone(), SCXML_EVENT_PROCESSOR)
2961 {
2962 self.dead_letter(&to, &env, "unresolvable", now);
2963 }
2964 }
2965 }
2966 self.shared.mark_ready(&c.address);
2967 }
2968
2969 fn self_migrate(&mut self, a: &Address, to: &str, migration: &str, now: i64) {
2972 let current = match self.actors.get(a) {
2973 Some(Activation::Virtual(v)) => v.info().version.as_str().to_owned(),
2974 _ => return,
2975 };
2976 if current == to {
2977 return;
2978 }
2979 if let Err(e) = self.migrate_at(a, migration, now) {
2980 let ev = platform_event(
2981 "error.execution",
2982 Value::Object(vec![
2983 ("migration".into(), Value::String(migration.to_owned())),
2984 ("cause".into(), Value::String(e.to_string())),
2985 ]),
2986 );
2987 let env = Envelope::new(format!("{a}/migration-refused/{}", self.seq), a, ev);
2988 self.seq += 1;
2989 let _ = self.shared.deliver(env, SCXML_EVENT_PROCESSOR);
2990 }
2991 }
2992
2993 pub fn migrate(&mut self, a: &Address, migration: &str) -> Result<u64, MigrateError> {
3004 let now = self.now_ms();
3005 self.migrate_at(a, migration, now)
3006 }
3007
3008 fn migrate_at(&mut self, a: &Address, migration: &str, now: i64) -> Result<u64, MigrateError> {
3009 if !self.actors.contains_key(a) {
3010 self.activate(a, now).map_err(MigrateError::Activation)?;
3011 }
3012 let m = self
3013 .shared
3014 .charts()
3015 .migration(migration)
3016 .ok_or_else(|| MigrateError::UnknownMigration(migration.to_owned()))?;
3017 let to = self.shared.charts().get(m.to.as_str()).ok_or_else(|| {
3018 MigrateError::Activation(ActivationError::MissingVersion {
3019 actor: a.clone(),
3020 version: m.to.as_str().to_owned(),
3021 })
3022 })?;
3023 let Some(Activation::Virtual(va)) = self.actors.get(a) else {
3024 return Err(MigrateError::NotStarted);
3025 };
3026 let Some(old) = &va.machine else {
3027 return Err(MigrateError::NotStarted);
3028 };
3029 if old.info.version != m.from {
3030 return Err(MigrateError::WrongVersion {
3031 actual: old.info.version.as_str().to_owned(),
3032 expected: m.from.as_str().to_owned(),
3033 });
3034 }
3035 let unmapped = unmapped_configurations(&m, &old.info, &to)
3036 .map_err(MigrateError::TooManyConfigurations)?;
3037 if !unmapped.is_empty() {
3038 return Err(MigrateError::Unmapped(unmapped));
3039 }
3040 let cfg: BTreeSet<String> = old.active().into_iter().collect();
3041 let new_cfg = m
3042 .map(&cfg, &to)
3043 .ok_or_else(|| MigrateError::Unmapped(vec![cfg.clone()]))?;
3044 let value = (m.transform)(old.dm.snapshot()).map_err(MigrateError::TransformFailed)?;
3045 let position = va.head + 1;
3046 let e = a.entry_iri(position);
3047 let before = old.snapshot(
3048 &NamedNode::new_unchecked(format!("{}/before", e.as_str())),
3049 va.head,
3050 );
3051 let (sends, invokes, ioproc) = self.ctx(a);
3052 let ctx = Ctx {
3053 registry: self.registry.as_ref(),
3054 ioprocessors: ioproc,
3055 send_types: sends,
3056 invoke_types: invokes,
3057 };
3058 let session = migrated_session(&old.session, &new_cfg, &to);
3059 let mut probe = SnapshotData {
3060 iri: format!("{}/after", e.as_str()),
3061 actor: a.as_str().to_owned(),
3062 position,
3063 version: to.version.as_str().to_owned(),
3064 digest: String::new(),
3065 session: serde_json::to_string(&session).unwrap_or_default(),
3066 datamodel: crate::json::to_text(&value),
3067 runtime: serde_json::to_string(&old.rt).unwrap_or_default(),
3068 };
3069 let new = Machine::from_snapshot(a, to.clone(), &ctx, &probe)
3070 .map_err(|x| MigrateError::TransformFailed(x.to_string()))?;
3071 probe.digest = new.digest();
3072 let ct = va
3073 .chart_type
3074 .as_ref()
3075 .map(|(t, k)| (t.as_str(), k.as_str()));
3076 let new_state = new.state_triples(ct);
3077 let delta = state_delta(&va.state, &new_state);
3078 let data = EntryData {
3079 actor: a.clone(),
3080 position,
3081 version: to.version.as_str().to_owned(),
3082 message_id: format!("{a}/migration/{position}"),
3083 timestamp_ms: now,
3084 digest: probe.digest.clone(),
3085 body: EntryBody::Migration {
3086 migration: m.iri.clone(),
3087 from: m.from.as_str().to_owned(),
3088 before,
3089 after: probe,
3090 },
3091 };
3092 self.append(a, va.head, &data, delta)
3093 .map_err(MigrateError::Append)?;
3094 if let Some(Activation::Virtual(va)) = self.actors.get_mut(a) {
3095 va.machine = Some(new);
3096 va.head = position;
3097 va.seen.insert(data.message_id.clone());
3098 va.state = new_state;
3099 }
3100 Ok(position)
3101 }
3102
3103 pub fn fork(
3112 &mut self,
3113 source: &Address,
3114 position: u64,
3115 new: &Address,
3116 ) -> Result<(), MigrateError> {
3117 let j = self.shared.storage.journal();
3118 if j.head(new.node()).map(|h| h.get()).unwrap_or(0) > 0 {
3119 return Err(MigrateError::Exists(new.clone()));
3120 }
3121 let loaded = self
3122 .load(source, Some(position))
3123 .map_err(MigrateError::Activation)?;
3124 let Some(mut m) = loaded.machine else {
3125 return Err(MigrateError::NotStarted);
3126 };
3127 m.address = new.clone();
3128 m.session.sessionid = new.as_str().to_owned();
3129 let e = new.entry_iri(1);
3130 let snap = m.snapshot(
3131 &NamedNode::new_unchecked(format!("{}/state", e.as_str())),
3132 position,
3133 );
3134 let state = m.state_triples(None);
3135 let delta = state_delta(&HashSet::new(), &state);
3136 let data = EntryData {
3137 actor: new.clone(),
3138 position: 1,
3139 version: m.info.version.as_str().to_owned(),
3140 message_id: format!("{new}/fork"),
3141 timestamp_ms: self.now_ms(),
3142 digest: snap.digest.clone(),
3143 body: EntryBody::Fork {
3144 source: source.as_str().to_owned(),
3145 position,
3146 snapshot: snap,
3147 },
3148 };
3149 self.append(new, 0, &data, delta)
3150 .map_err(MigrateError::Append)
3151 }
3152
3153 pub fn state_at(&mut self, a: &Address, position: u64) -> Result<ActorState, ActivationError> {
3160 let loaded = self.load(a, Some(position))?;
3161 Ok(actor_state(loaded.machine.as_ref(), loaded.head))
3162 }
3163
3164 pub fn state(&mut self, a: &Address) -> Result<ActorState, ActivationError> {
3170 if let Some(Activation::Virtual(v)) = self.actors.get(a) {
3171 return Ok(actor_state(v.machine.as_ref(), v.head));
3172 }
3173 let loaded = self.load(a, None)?;
3174 Ok(actor_state(loaded.machine.as_ref(), loaded.head))
3175 }
3176
3177 pub fn can(&mut self, a: &Address, event: &Event) -> Result<bool, ActivationError> {
3186 if let Some(Activation::Virtual(v)) = self.actors.get(a)
3187 && let Some(m) = &v.machine
3188 {
3189 return Ok(m.can(self.registry.as_ref(), event));
3190 }
3191 let loaded = self.load(a, None)?;
3192 Ok(loaded
3193 .machine
3194 .as_ref()
3195 .is_some_and(|m| m.can(self.registry.as_ref(), event)))
3196 }
3197
3198 pub fn journal(&self, a: &Address) -> Result<Vec<EntryData>, ActivationError> {
3204 let recs = self
3205 .shared
3206 .storage
3207 .journal()
3208 .read(a.node(), Position::ZERO)
3209 .map_err(ActivationError::Store)?;
3210 recs.iter()
3211 .map(|r| EntryData::from_record(a, r).map_err(ActivationError::Decode))
3212 .collect()
3213 }
3214
3215 pub fn pending_timers(&self) -> Vec<(i64, TimerRecord)> {
3217 self.timers
3218 .iter()
3219 .map(|((due, _), a)| (*due, a.rec.clone()))
3220 .collect()
3221 }
3222
3223 pub fn datamodels(&self) -> &Arc<dyn DatamodelRegistry + Send + Sync> {
3225 &self.registry
3226 }
3227
3228 pub fn fresh_id(&self, prefix: &str) -> String {
3230 self.shared.fresh_id(prefix)
3231 }
3232
3233 pub fn short_hex(text: &str) -> String {
3235 hex(text.as_bytes())
3236 }
3237}
3238
3239fn index_of(v: &VirtualActivation, req: &InvokeRequest) -> usize {
3240 v.machine
3241 .as_ref()
3242 .and_then(|m| {
3243 m.session
3244 .invocations
3245 .iter()
3246 .find(|i| i.invokeid == req.invokeid)
3247 .map(|i| i.index)
3248 })
3249 .unwrap_or(0)
3250}
3251
3252fn actor_state(m: Option<&Machine>, head: u64) -> ActorState {
3253 match m {
3254 Some(m) => ActorState {
3255 position: head,
3256 version: m.info.version.as_str().to_owned(),
3257 configuration: m.active(),
3258 datamodel: m.dm.snapshot(),
3259 terminated: m.session.is_terminated(),
3260 },
3261 None => ActorState {
3262 position: head,
3263 version: String::new(),
3264 configuration: Vec::new(),
3265 datamodel: Value::Undefined,
3266 terminated: false,
3267 },
3268 }
3269}
3270
3271fn state_delta(old: &HashSet<Triple>, new: &HashSet<Triple>) -> StateDelta {
3273 let mut retract: Vec<Triple> = old.difference(new).cloned().collect();
3274 let mut assert: Vec<Triple> = new.difference(old).cloned().collect();
3275 retract.sort_by_cached_key(ToString::to_string);
3276 assert.sort_by_cached_key(ToString::to_string);
3277 StateDelta { retract, assert }
3278}