1mod in_memory;
4mod realm_profile;
5#[cfg(not(target_arch = "wasm32"))]
6mod sqlite;
7
8pub use in_memory::{
9 InMemoryMobEventStore, InMemoryMobRunStore, InMemoryMobSpecStore, InMemoryRealmProfileStore,
10};
11pub use realm_profile::{RealmProfileStore, StoredRealmProfile};
12#[cfg(not(target_arch = "wasm32"))]
13pub use sqlite::{
14 SqliteMobEventStore, SqliteMobRunStore, SqliteMobSpecStore, SqliteMobStores,
15 SqliteRealmProfileStore,
16};
17
18use crate::definition::MobDefinition;
19use crate::event::{MobEvent, NewMobEvent};
20use crate::ids::{FlowId, FrameId, LoopId, LoopInstanceId, MobId, RunId, StepId};
21use crate::run::{
22 FailureLedgerEntry, FrameSnapshot, LoopIterationLedgerEntry, LoopSnapshot, MobRun,
23 MobRunStatus, StepLedgerEntry,
24};
25use async_trait::async_trait;
26use chrono::{DateTime, Utc};
27use meerkat_machine_kernels::KernelState;
28
29#[derive(Debug, thiserror::Error)]
34pub enum MobStoreError {
35 #[error("Write failed: {0}")]
37 WriteFailed(String),
38
39 #[error("Read failed: {0}")]
41 ReadFailed(String),
42
43 #[error("Not found: {0}")]
45 NotFound(String),
46
47 #[error("CAS conflict: {0}")]
49 CasConflict(String),
50
51 #[error("spec revision conflict for mob {mob_id}: expected {expected:?}, actual {actual}")]
53 SpecRevisionConflict {
54 mob_id: crate::ids::MobId,
55 expected: Option<u64>,
56 actual: u64,
57 },
58
59 #[error("Serialization error: {0}")]
61 Serialization(String),
62
63 #[error("Internal error: {0}")]
65 Internal(String),
66}
67
68#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
70#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
71pub trait MobEventStore: Send + Sync {
72 async fn append(&self, event: NewMobEvent) -> Result<MobEvent, MobStoreError>;
74
75 async fn append_batch(&self, events: Vec<NewMobEvent>) -> Result<Vec<MobEvent>, MobStoreError>;
81
82 async fn poll(&self, after_cursor: u64, limit: usize) -> Result<Vec<MobEvent>, MobStoreError>;
84
85 async fn replay_all(&self) -> Result<Vec<MobEvent>, MobStoreError>;
87
88 async fn clear(&self) -> Result<(), MobStoreError>;
90
91 async fn prune(&self, _older_than: DateTime<Utc>) -> Result<u64, MobStoreError> {
93 Ok(0)
94 }
95}
96
97#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
99#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
100pub trait MobRunStore: Send + Sync {
101 async fn create_run(&self, run: MobRun) -> Result<(), MobStoreError>;
102 async fn get_run(&self, run_id: &RunId) -> Result<Option<MobRun>, MobStoreError>;
103 async fn list_runs(
104 &self,
105 mob_id: &MobId,
106 flow_id: Option<&FlowId>,
107 ) -> Result<Vec<MobRun>, MobStoreError>;
108 async fn cas_run_status(
109 &self,
110 run_id: &RunId,
111 expected: MobRunStatus,
112 next: MobRunStatus,
113 ) -> Result<bool, MobStoreError>;
114 async fn cas_flow_state(
115 &self,
116 run_id: &RunId,
117 expected: &KernelState,
118 next: &KernelState,
119 ) -> Result<bool, MobStoreError>;
120 async fn cas_run_snapshot(
121 &self,
122 run_id: &RunId,
123 expected_status: MobRunStatus,
124 expected_flow_state: &KernelState,
125 next_status: MobRunStatus,
126 next_flow_state: &KernelState,
127 ) -> Result<bool, MobStoreError>;
128 async fn append_step_entry(
129 &self,
130 run_id: &RunId,
131 entry: StepLedgerEntry,
132 ) -> Result<(), MobStoreError>;
133 async fn append_step_entry_if_absent(
134 &self,
135 run_id: &RunId,
136 entry: StepLedgerEntry,
137 ) -> Result<bool, MobStoreError>;
138 async fn put_step_output(
139 &self,
140 run_id: &RunId,
141 step_id: &StepId,
142 output: serde_json::Value,
143 ) -> Result<(), MobStoreError>;
144 async fn append_failure_entry(
145 &self,
146 run_id: &RunId,
147 entry: FailureLedgerEntry,
148 ) -> Result<(), MobStoreError>;
149
150 async fn upsert_loop_snapshot(
160 &self,
161 run_id: &RunId,
162 loop_instance_id: &LoopInstanceId,
163 snapshot: LoopSnapshot,
164 ledger_entry: Option<LoopIterationLedgerEntry>,
165 ) -> Result<(), MobStoreError>;
166
167 async fn cas_frame_state(
177 &self,
178 run_id: &RunId,
179 frame_id: &FrameId,
180 expected: Option<&FrameSnapshot>,
181 next: FrameSnapshot,
182 ) -> Result<bool, MobStoreError>;
183
184 async fn cas_grant_node_slot(
190 &self,
191 run_id: &RunId,
192 expected_run_state: &KernelState,
193 next_run_state: KernelState,
194 frame_id: &FrameId,
195 expected_frame: &FrameSnapshot,
196 next_frame: FrameSnapshot,
197 ) -> Result<bool, MobStoreError>;
198
199 #[allow(clippy::too_many_arguments)]
209 async fn cas_complete_step_and_record_output(
210 &self,
211 run_id: &RunId,
212 frame_id: &FrameId,
213 expected_frame: &FrameSnapshot,
214 next_frame: FrameSnapshot,
215 step_output_key: String,
216 step_output: serde_json::Value,
217 loop_context: Option<(&LoopId, u64)>,
218 ) -> Result<bool, MobStoreError>;
219
220 #[allow(clippy::too_many_arguments)]
226 async fn cas_start_loop(
227 &self,
228 run_id: &RunId,
229 loop_instance_id: &LoopInstanceId,
230 expected_run_state: &KernelState,
231 next_run_state: KernelState,
232 frame_id: &FrameId,
233 expected_frame: &FrameSnapshot,
234 next_frame: FrameSnapshot,
235 initial_loop: LoopSnapshot,
236 ) -> Result<bool, MobStoreError>;
237
238 async fn cas_loop_request_body_frame(
244 &self,
245 run_id: &RunId,
246 loop_instance_id: &LoopInstanceId,
247 expected_loop: &LoopSnapshot,
248 next_loop: LoopSnapshot,
249 expected_run_state: &KernelState,
250 next_run_state: KernelState,
251 ) -> Result<bool, MobStoreError>;
252
253 #[allow(clippy::too_many_arguments)]
259 async fn cas_grant_body_frame_start(
260 &self,
261 run_id: &RunId,
262 loop_instance_id: &LoopInstanceId,
263 expected_loop: &LoopSnapshot,
264 next_loop: LoopSnapshot,
265 frame_id: &FrameId,
266 initial_frame: FrameSnapshot,
267 ledger_entry: LoopIterationLedgerEntry,
268 expected_run_state: &KernelState,
269 next_run_state: KernelState,
270 ) -> Result<bool, MobStoreError>;
271
272 #[allow(clippy::too_many_arguments)]
278 async fn cas_complete_body_frame(
279 &self,
280 run_id: &RunId,
281 loop_instance_id: &LoopInstanceId,
282 expected_loop: &LoopSnapshot,
283 next_loop: LoopSnapshot,
284 frame_id: &FrameId,
285 expected_frame: &FrameSnapshot,
286 next_frame: FrameSnapshot,
287 expected_run_state: &KernelState,
288 next_run_state: KernelState,
289 ) -> Result<bool, MobStoreError>;
290
291 #[allow(clippy::too_many_arguments)]
297 async fn cas_complete_loop(
298 &self,
299 run_id: &RunId,
300 loop_instance_id: &LoopInstanceId,
301 expected_loop: &LoopSnapshot,
302 next_loop: LoopSnapshot,
303 frame_id: &FrameId,
304 expected_frame: &FrameSnapshot,
305 next_frame: FrameSnapshot,
306 expected_run_state: &KernelState,
307 next_run_state: KernelState,
308 ) -> Result<bool, MobStoreError>;
309}
310
311#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
313#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
314pub trait MobSpecStore: Send + Sync {
315 async fn put_spec(
317 &self,
318 mob_id: &MobId,
319 definition: &MobDefinition,
320 revision: Option<u64>,
321 ) -> Result<u64, MobStoreError>;
322
323 async fn get_spec(&self, mob_id: &MobId)
324 -> Result<Option<(MobDefinition, u64)>, MobStoreError>;
325 async fn list_specs(&self) -> Result<Vec<MobId>, MobStoreError>;
326 async fn delete_spec(
327 &self,
328 mob_id: &MobId,
329 revision: Option<u64>,
330 ) -> Result<bool, MobStoreError>;
331}