1use crate::retrieval::SuspenseEvents;
2use crate::{
3 changes::EntityChange,
4 entity::Entity,
5 error::{MutationError, RetrievalError},
6 livequery::{EntityLiveQuery, LiveQuery},
7 model::View,
8 node::{MatchArgs, Node},
9 policy::{AccessDenied, PolicyAgent},
10 storage::{StorageCollectionWrapper, StorageEngine},
11 transaction::Transaction,
12};
13use ankurah_proto::{self as proto, Attested, Clock, CollectionId, EntityState, Event};
14use async_trait::async_trait;
15use std::sync::{atomic::AtomicBool, Arc};
16use tracing::debug;
17#[cfg(feature = "wasm")]
18use wasm_bindgen::prelude::*;
19
20#[cfg_attr(feature = "wasm", wasm_bindgen)]
24#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
25pub struct Context(Arc<dyn TContext + Send + Sync + 'static>);
26impl Clone for Context {
27 fn clone(&self) -> Self { Self(self.0.clone()) }
28}
29
30pub struct NodeAndContext<SE, PA: PolicyAgent>
31where
32 SE: StorageEngine + Send + Sync + 'static,
33 PA: PolicyAgent + Send + Sync + 'static,
34{
35 pub node: Node<SE, PA>,
36 pub cdata: PA::ContextData,
37}
38
39#[async_trait]
40pub trait TContext {
41 fn node_id(&self) -> proto::EntityId;
42 fn create_entity(&self, collection: proto::CollectionId, trx_alive: Arc<AtomicBool>) -> Entity;
46 fn check_write(&self, entity: &Entity) -> Result<(), AccessDenied>;
47 async fn get_entity(&self, id: proto::EntityId, collection: &proto::CollectionId, cached: bool) -> Result<Entity, RetrievalError>;
48 fn get_resident_entity(&self, id: proto::EntityId) -> Option<Entity>;
49 async fn fetch_entities(&self, collection: &proto::CollectionId, args: MatchArgs) -> Result<Vec<Entity>, RetrievalError>;
50 async fn commit_local_trx(&self, trx: &Transaction) -> Result<Vec<Event>, MutationError>;
51 fn query(&self, collection_id: proto::CollectionId, args: MatchArgs) -> Result<EntityLiveQuery, RetrievalError>;
52 async fn collection(&self, id: &proto::CollectionId) -> Result<StorageCollectionWrapper, RetrievalError>;
53}
54
55#[async_trait]
56impl<SE: StorageEngine + Send + Sync + 'static, PA: PolicyAgent + Send + Sync + 'static> TContext for NodeAndContext<SE, PA> {
57 fn node_id(&self) -> proto::EntityId { self.node.id }
58 fn create_entity(&self, collection: proto::CollectionId, trx_alive: Arc<AtomicBool>) -> Entity {
59 let primary_entity = self.node.entities.create(collection);
60 primary_entity.snapshot(trx_alive)
61 }
62 fn check_write(&self, entity: &Entity) -> Result<(), AccessDenied> { self.node.policy_agent.check_write(&self.cdata, entity, None) }
63 async fn get_entity(&self, id: proto::EntityId, collection: &proto::CollectionId, cached: bool) -> Result<Entity, RetrievalError> {
64 self.get_entity(collection, id, cached).await
65 }
66 fn get_resident_entity(&self, id: proto::EntityId) -> Option<Entity> { self.node.entities.get(&id) }
67 async fn fetch_entities(&self, collection: &proto::CollectionId, args: MatchArgs) -> Result<Vec<Entity>, RetrievalError> {
68 self.fetch_entities(collection, args).await
69 }
70 async fn commit_local_trx(&self, trx: &Transaction) -> Result<Vec<Event>, MutationError> {
71 use std::sync::atomic::Ordering;
72
73 if trx.alive.compare_exchange(true, false, Ordering::AcqRel, Ordering::Acquire).is_err() {
76 return Err(MutationError::General("Transaction already committed or rolled back".into()));
77 }
78
79 let trx_id = trx.id.clone();
81 let mut entity_events = Vec::new();
82 for entity in trx.entities.iter() {
83 if let Some(event) = entity.generate_commit_event()? {
84 if event.is_entity_create() {
87 let created_ids = trx.created_entity_ids.read().unwrap();
88 if !created_ids.contains(&entity.id) {
89 return Err(MutationError::General(
90 format!(
91 "Cannot commit phantom entity {}: entity has empty parent (creation event) \
92 but was not created in this transaction via create()",
93 entity.id
94 )
95 .into(),
96 ));
97 }
98 }
99 entity_events.push((entity.clone(), event));
100 }
101 }
102
103 let mut attested_events = Vec::new();
105 let mut entity_attested_events = Vec::new();
106
107 for (entity, event) in entity_events {
111 use std::sync::atomic::AtomicBool;
113 let trx_alive = Arc::new(AtomicBool::new(true));
114 let forked = entity.snapshot(trx_alive);
115
116 let entity_before = match &entity.kind {
118 crate::entity::EntityKind::Transacted { upstream, .. } => upstream.clone(),
119 crate::entity::EntityKind::Primary => entity.clone(),
120 };
121
122 let collection_id = &event.collection;
124 let collection = self.node.collections.get(collection_id).await?;
125 let event_getter = crate::retrieval::LocalEventGetter::new(collection, self.node.durable);
126 event_getter.stage_event(event.clone());
127 forked.apply_event(&event_getter, &event).await?;
128
129 let attestation = self.node.policy_agent.check_event(&self.node, &self.cdata, &entity_before, &forked, &event)?;
130 let attested = Attested::opt(event.clone(), attestation);
131
132 attested_events.push(attested.clone());
133 entity_attested_events.push((entity, attested));
134 }
135
136 for (_, attested) in &entity_attested_events {
138 let collection = self.node.collections.get(&attested.payload.collection).await?;
139 let event_getter = crate::retrieval::LocalEventGetter::new(collection, self.node.durable);
140 event_getter.commit_event(attested).await?;
141 }
142
143 for (entity, attested_event) in &entity_attested_events {
145 entity.commit_head(Clock::new([attested_event.payload.id()]));
146 }
147 self.node.relay_to_required_peers(&self.cdata, trx_id, &attested_events).await?;
149
150 let mut changes: Vec<EntityChange> = Vec::new();
152 for (entity, attested_event) in entity_attested_events {
153 let collection_id = &attested_event.payload.collection;
154 let collection = self.node.collections.get(collection_id).await?;
155
156 let canonical_entity = match &entity.kind {
158 crate::entity::EntityKind::Transacted { upstream, .. } => {
159 let event_getter = crate::retrieval::LocalEventGetter::new(collection.clone(), self.node.durable);
161 upstream.apply_event(&event_getter, &attested_event.payload).await?;
162 upstream.clone()
163 }
164 crate::entity::EntityKind::Primary => entity,
165 };
166
167 let state = canonical_entity.to_state()?;
168
169 let entity_state = EntityState { entity_id: canonical_entity.id(), collection: canonical_entity.collection().clone(), state };
170 let attestation = self.node.policy_agent.attest_state(&self.node, &entity_state);
171 let attested = Attested::opt(entity_state, attestation);
172 collection.set_state(attested).await?;
173
174 changes.push(EntityChange::new(canonical_entity, vec![attested_event])?);
175 }
176
177 self.node.reactor.notify_change(changes).await;
179
180 Ok(attested_events.into_iter().map(|a| a.payload).collect())
181 }
182 fn query(&self, collection_id: proto::CollectionId, args: MatchArgs) -> Result<EntityLiveQuery, RetrievalError> {
183 EntityLiveQuery::new(&self.node, collection_id, args, self.cdata.clone())
184 }
185 async fn collection(&self, id: &proto::CollectionId) -> Result<StorageCollectionWrapper, RetrievalError> {
186 self.node.system.collection(id).await
187 }
188}
189
190#[cfg(feature = "wasm")]
192#[wasm_bindgen]
193impl Context {
194 #[wasm_bindgen(js_name = "node_id")]
195 pub fn js_node_id(&self) -> proto::EntityId { self.0.node_id() }
196}
197
198#[cfg_attr(feature = "wasm", wasm_bindgen)]
200#[cfg_attr(feature = "uniffi", uniffi::export)]
201impl Context {
202 pub fn begin(&self) -> Transaction { Transaction::new(self.0.clone()) }
204}
205
206impl Context {
207 pub fn new<SE: StorageEngine + Send + Sync + 'static, PA: PolicyAgent + Send + Sync + 'static>(
208 node: Node<SE, PA>,
209 data: PA::ContextData,
210 ) -> Self {
211 Self(Arc::new(NodeAndContext { node, cdata: data }))
212 }
213
214 pub fn node_id(&self) -> proto::EntityId { self.0.node_id() }
215
216 pub async fn get<R: View>(&self, id: proto::EntityId) -> Result<R, RetrievalError> {
229 let entity = self.0.get_entity(id, &R::collection(), false).await?;
230 Ok(R::from_entity(entity))
231 }
232
233 pub async fn get_cached<R: View>(&self, id: proto::EntityId) -> Result<R, RetrievalError> {
235 let entity = self.0.get_entity(id, &R::collection(), true).await?;
236 Ok(R::from_entity(entity))
237 }
238
239 pub async fn fetch<R: View>(&self, args: impl TryInto<MatchArgs, Error = impl Into<RetrievalError>>) -> Result<Vec<R>, RetrievalError> {
240 let args: MatchArgs = args.try_into().map_err(|e| e.into())?;
241 use crate::model::Model;
242 let collection_id = R::Model::collection();
243
244 let entities = self.0.fetch_entities(&collection_id, args).await?;
245
246 Ok(entities.into_iter().map(|e| R::from_entity(e)).collect())
247 }
248
249 pub async fn fetch_one<R: View + Clone + 'static>(
250 &self,
251 args: impl TryInto<MatchArgs, Error = impl Into<RetrievalError>>,
252 ) -> Result<Option<R>, RetrievalError> {
253 let views = self.fetch::<R>(args).await?;
254 Ok(views.into_iter().next())
255 }
256 pub fn query<R>(&self, args: impl TryInto<MatchArgs, Error = impl Into<RetrievalError>>) -> Result<LiveQuery<R>, RetrievalError>
258 where R: View {
259 let args: MatchArgs = args.try_into().map_err(|e| e.into())?;
260 use crate::model::Model;
261 Ok(self.0.query(R::Model::collection(), args)?.map::<R>())
262 }
263
264 pub async fn query_wait<R>(
266 &self,
267 args: impl TryInto<MatchArgs, Error = impl Into<RetrievalError>>,
268 ) -> Result<LiveQuery<R>, RetrievalError>
269 where
270 R: View,
271 {
272 let livequery = self.query::<R>(args)?;
273 livequery.wait_initialized().await;
274 Ok(livequery)
275 }
276 pub async fn collection(&self, id: &proto::CollectionId) -> Result<StorageCollectionWrapper, RetrievalError> {
277 self.0.collection(id).await
278 }
279}
280
281impl<SE, PA> NodeAndContext<SE, PA>
282where
283 SE: StorageEngine + Send + Sync + 'static,
284 PA: PolicyAgent + Send + Sync + 'static,
285{
286 pub(crate) async fn get_entity(
288 &self,
289 collection_id: &CollectionId,
290 id: proto::EntityId,
291 cached: bool,
292 ) -> Result<Entity, RetrievalError> {
293 debug!("Node({}).get_entity {:?}-{:?}", self.node.id, id, collection_id);
294
295 if !self.node.durable {
296 match self.node.get_from_peer(collection_id, vec![id], &self.cdata).await {
298 Ok(_) => (),
299 Err(RetrievalError::NoDurablePeers) if cached => (),
300 Err(e) => {
301 return Err(e);
302 }
303 }
304 }
305
306 if let Some(local) = self.node.entities.get(&id) {
307 debug!("Node({}).get_entity found local entity - returning", self.node.id);
308 let state = local.to_state()?;
309 let entity_id = local.id();
310 self.node.policy_agent.check_read(&self.cdata, &entity_id, collection_id, &state)?;
311 return Ok(local);
312 }
313 debug!("{}.get_entity fetching from storage", self.node);
314
315 let collection = self.node.collections.get(collection_id).await?;
316 match collection.get_state(id).await {
317 Ok(entity_state) => {
318 self.node.policy_agent.check_read(
319 &self.cdata,
320 &entity_state.payload.entity_id,
321 collection_id,
322 &entity_state.payload.state,
323 )?;
324 let state_getter = crate::retrieval::LocalStateGetter::new(collection.clone());
325 let event_getter = crate::retrieval::CachedEventGetter::new(collection_id.clone(), collection, &self.node, &self.cdata);
326 let (_changed, entity) = self
327 .node
328 .entities
329 .with_state(&state_getter, &event_getter, id, collection_id.clone(), entity_state.payload.state)
330 .await?;
331 Ok(entity)
332 }
333 Err(e) => Err(e),
334 }
335 }
336 pub async fn fetch_entities(&self, collection_id: &CollectionId, mut args: MatchArgs) -> Result<Vec<Entity>, RetrievalError> {
338 self.node.policy_agent.can_access_collection(&self.cdata, collection_id)?;
339 args.selection.predicate = self.node.policy_agent.filter_predicate(&self.cdata, collection_id, args.selection.predicate)?;
342
343 args.selection = self.node.type_resolver.resolve_selection_types(args.selection);
345
346 if !self.node.durable {
348 Ok(self.fetch_from_peer(collection_id, args.selection).await?)
350 } else {
351 let storage_collection = self.node.collections.get(collection_id).await?;
352 let states = storage_collection.fetch_states(&args.selection).await?;
353
354 let mut entities = Vec::new();
356 let state_getter = crate::retrieval::LocalStateGetter::new(storage_collection.clone());
357 let event_getter = crate::retrieval::CachedEventGetter::new(collection_id.clone(), storage_collection, &self.node, &self.cdata);
358 for state in states {
359 let (_, entity) = self
360 .node
361 .entities
362 .with_state(&state_getter, &event_getter, state.payload.entity_id, collection_id.clone(), state.payload.state)
363 .await?;
364 entities.push(entity);
365 }
366 Ok(entities)
367 }
368 }
369
370 async fn fetch_from_peer(
372 &self,
373 collection_id: &proto::CollectionId,
374 selection: ankql::ast::Selection,
375 ) -> Result<Vec<crate::entity::Entity>, RetrievalError> {
376 let peer_id = self.node.get_durable_peer_random().ok_or(RetrievalError::NoDurablePeers)?;
377
378 let known_matched_entities = self.node.fetch_entities_from_local(collection_id, &selection).await?;
380
381 let known_matches = known_matched_entities
382 .iter()
383 .map(|entity| proto::KnownEntity { entity_id: entity.id(), head: entity.head().clone() })
384 .collect();
385
386 let selection_clone = selection.clone();
388 match self
389 .node
390 .request(peer_id, &self.cdata, proto::NodeRequestBody::Fetch { collection: collection_id.clone(), selection, known_matches })
391 .await?
392 {
393 proto::NodeResponseBody::Fetch(deltas) => {
394 let collection = self.node.collections.get(collection_id).await?;
395 let event_getter =
396 crate::retrieval::CachedEventGetter::new(collection_id.clone(), collection.clone(), &self.node, &self.cdata);
397 let state_getter = crate::retrieval::LocalStateGetter::new(collection);
398
399 crate::node_applier::NodeApplier::apply_deltas(&self.node, &peer_id, deltas, &event_getter, &state_getter).await?;
401 self.node.fetch_entities_from_local(collection_id, &selection_clone).await
405 }
406 proto::NodeResponseBody::Error(e) => {
407 tracing::debug!("Error from peer fetch: {}", e);
408 Err(RetrievalError::Other(format!("{:?}", e)))
409 }
410 _ => {
411 tracing::debug!("Unexpected response type from peer fetch");
412 Err(RetrievalError::Other("Unexpected response type".to_string()))
413 }
414 }
415 }
416}