bamboo_engine/events/
root_actor.rs1use super::AccountEventSink;
4use bamboo_agent_core::AgentEvent;
5use bamboo_domain::{RootActorRuntimeWrite, Storage};
6use std::sync::Arc;
7
8#[derive(Clone)]
9pub struct RootActorEventPublication {
10 storage: Arc<dyn Storage>,
11 account_sink: Arc<AccountEventSink>,
12 owner: RootActorRuntimeWrite,
13}
14
15impl RootActorEventPublication {
16 pub fn new(
17 storage: Arc<dyn Storage>,
18 account_sink: Arc<AccountEventSink>,
19 owner: RootActorRuntimeWrite,
20 ) -> Self {
21 Self {
22 storage,
23 account_sink,
24 owner,
25 }
26 }
27
28 pub fn matches_execution(&self, session_id: &str, run_id: &str) -> bool {
29 self.owner.fence.actor_id == session_id && self.owner.fence.run_id == run_id
30 }
31
32 pub(crate) fn diagnostic_identity(&self) -> (u64, u64, String) {
35 (
36 self.owner.fence.lease_epoch,
37 self.owner.fence.attempt,
38 crate::execution::workspace_diagnostics::fence_fingerprint(&self.owner.fence),
39 )
40 }
41
42 pub async fn publish(
43 &self,
44 session_id: &str,
45 event: &AgentEvent,
46 publish: Box<dyn FnOnce() -> bool + Send>,
47 ) -> std::io::Result<()> {
48 let storage = self.storage.clone();
49 let sink = self.account_sink.clone();
50 let owner_for_queue = self.owner.clone();
51 let event = event.clone();
52 let session_id = session_id.to_owned();
53 let (queued, receipt) = tokio::sync::oneshot::channel();
54 self.storage
55 .publish_root_actor_runtime_event(
56 &self.owner,
57 Box::new(move |check_current| {
58 check_current()?;
59 if !publish() {
60 return Err(std::io::Error::other(
61 "Root per-session event publication was retired",
62 ));
63 }
64 check_current()?;
65 let confirmation = if event.is_durable_change() {
66 Some(
67 sink.record_root_actor(
68 storage,
69 owner_for_queue,
70 Some(&session_id),
71 &event,
72 )
73 .ok_or_else(|| {
74 std::io::Error::other(
75 "Root account event queue rejected publication",
76 )
77 })?,
78 )
79 } else {
80 None
81 };
82 let _ = queued.send(confirmation);
83 Ok(())
84 }),
85 )
86 .await?;
87 let confirmation = receipt
90 .await
91 .map_err(|_| std::io::Error::other("Root account admission receipt closed"))?;
92 if let Some(confirmation) = confirmation {
93 if !tokio::time::timeout(std::time::Duration::from_secs(30), confirmation)
94 .await
95 .ok()
96 .and_then(Result::ok)
97 .unwrap_or(false)
98 {
99 if let Err(error) = self
105 .storage
106 .publish_root_actor_runtime_event(
107 &self.owner,
108 Box::new(|check_current| check_current()),
109 )
110 .await
111 {
112 if error
113 .get_ref()
114 .is_some_and(|cause| cause.is::<bamboo_domain::SessionAuthorityConflict>())
115 {
116 return Err(error);
117 }
118 }
119 return Err(std::io::Error::other(
120 "Root account final publication was not confirmed",
121 ));
122 }
123 }
124 Ok(())
125 }
126}