1use crate::event_dag::DEFAULT_BUDGET;
2use crate::retrieval::{GetEvents, GetState};
3use crate::selection::filter::Filterable;
4use crate::{
5 error::{LineageError, MutationError, RetrievalError, StateError},
6 event_dag::AbstractCausalRelation,
7 model::View,
8 property::backend::{backend_from_string, PropertyBackend},
9 reactor::AbstractEntity,
10 value::Value,
11};
12use ankurah_proto::{Clock, CollectionId, EntityId, EntityState, Event, EventId, OperationSet, State};
13use std::collections::BTreeMap;
14use std::sync::atomic::{AtomicBool, Ordering};
15use std::sync::{Arc, Weak};
16use tracing::{debug, error, warn};
17
18pub enum StateApplyResult {
20 Applied,
22 DivergedRequiresEvents,
24 AlreadyApplied,
26 Older,
28}
29
30#[derive(Debug, Clone)]
33pub struct Entity(Arc<EntityInner>);
34
35pub struct TemporaryEntity(Arc<EntityInner>);
38
39#[derive(Debug)]
41struct EntityInnerState {
42 head: Clock,
43 backends: BTreeMap<String, Arc<dyn PropertyBackend>>,
45}
46
47impl EntityInnerState {
48 fn apply_operations_from_event(
55 &mut self,
56 backend_name: String,
57 operations: &[ankurah_proto::Operation],
58 event_id: EventId,
59 ) -> Result<(), MutationError> {
60 if let Some(backend) = self.backends.get(&backend_name) {
61 backend.apply_operations_with_event(operations, event_id)?;
62 } else {
63 let backend = backend_from_string(&backend_name, None)?;
64 backend.apply_operations_with_event(operations, event_id)?;
65 self.backends.insert(backend_name, backend);
66 }
67 Ok(())
68 }
69}
70
71#[derive(Debug)]
72pub struct EntityInner {
73 pub id: EntityId,
74 pub collection: CollectionId,
75 state: std::sync::RwLock<EntityInnerState>,
77 pub(crate) kind: EntityKind,
78 pub(crate) broadcast: ankurah_signals::broadcast::Broadcast,
80}
81
82#[derive(Debug)]
83pub enum EntityKind {
84 Primary, Transacted { trx_alive: Arc<AtomicBool>, upstream: Entity }, }
87
88impl std::ops::Deref for Entity {
89 type Target = EntityInner;
90
91 fn deref(&self) -> &Self::Target { &self.0 }
92}
93
94impl std::ops::Deref for TemporaryEntity {
95 type Target = EntityInner;
96
97 fn deref(&self) -> &Self::Target { &self.0 }
98}
99
100impl PartialEq for Entity {
101 fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.0, &other.0) }
102}
103
104pub struct WeakEntity(Weak<EntityInner>);
106
107impl WeakEntity {
108 pub fn upgrade(&self) -> Option<Entity> { self.0.upgrade().map(Entity) }
109}
110
111impl Entity {
112 pub fn id(&self) -> EntityId { self.id }
113
114 fn weak(&self) -> WeakEntity { WeakEntity(Arc::downgrade(&self.0)) }
116
117 pub fn collection(&self) -> &CollectionId { &self.collection }
118
119 pub fn head(&self) -> Clock { self.state.read().unwrap().head.clone() }
120
121 pub fn is_writable(&self) -> bool {
123 match &self.kind {
124 EntityKind::Primary => false, EntityKind::Transacted { trx_alive, .. } => trx_alive.load(Ordering::Acquire),
126 }
127 }
128
129 pub fn to_state(&self) -> Result<State, StateError> {
130 let state = self.state.read().expect("other thread panicked, panic here too");
131 let mut state_buffers = BTreeMap::default();
132 for (name, backend) in &state.backends {
133 let state_buffer = backend.to_state_buffer()?;
134 state_buffers.insert(name.clone(), state_buffer);
135 }
136 let state_buffers = ankurah_proto::StateBuffers(state_buffers);
137 Ok(State { state_buffers, head: state.head.clone() })
138 }
139
140 pub fn to_entity_state(&self) -> Result<EntityState, StateError> {
141 let state = self.to_state()?;
142 Ok(EntityState { entity_id: self.id(), collection: self.collection.clone(), state })
143 }
144
145 pub fn create(id: EntityId, collection: CollectionId) -> Self {
147 Self(Arc::new(EntityInner {
148 id,
149 collection,
150 state: std::sync::RwLock::new(EntityInnerState { head: Clock::default(), backends: BTreeMap::default() }),
151 kind: EntityKind::Primary,
152 broadcast: ankurah_signals::broadcast::Broadcast::new(),
153 }))
154 }
155
156 fn from_state(id: EntityId, collection: CollectionId, state: &State) -> Result<Self, RetrievalError> {
158 let mut backends = BTreeMap::new();
159 for (name, state_buffer) in state.state_buffers.iter() {
160 let backend = backend_from_string(name, Some(state_buffer))?;
161 backends.insert(name.to_owned(), backend);
162 }
163
164 Ok(Self(Arc::new(EntityInner {
165 id,
166 collection,
167 state: std::sync::RwLock::new(EntityInnerState { head: state.head.clone(), backends }),
168 kind: EntityKind::Primary,
169 broadcast: ankurah_signals::broadcast::Broadcast::new(),
170 })))
171 }
172
173 pub(crate) fn generate_commit_event(&self) -> Result<Option<Event>, MutationError> {
177 let state = self.state.read().expect("other thread panicked, panic here too");
178 let mut operations = BTreeMap::<String, Vec<ankurah_proto::Operation>>::new();
179 for (name, backend) in &state.backends {
180 if let Some(ops) = backend.to_operations()? {
181 operations.insert(name.clone(), ops);
182 }
183 }
184
185 if operations.is_empty() {
186 Ok(None)
187 } else {
188 let operations = OperationSet(operations);
189 let event = Event { entity_id: self.id, collection: self.collection.clone(), operations, parent: state.head.clone() };
190 Ok(Some(event))
191 }
192 }
193
194 pub(crate) fn commit_head(&self, new_head: Clock) {
196 self.state.write().unwrap().head = new_head;
199 }
200
201 fn try_mutate<F, E>(&self, expected_head: &mut Clock, body: F) -> Result<bool, E>
210 where F: FnOnce(&mut EntityInnerState) -> Result<(), E> {
211 let mut state = self.state.write().unwrap();
212 if &state.head != expected_head {
213 *expected_head = state.head.clone();
214 return Ok(false);
215 }
216 body(&mut state)?;
217 Ok(true)
218 }
219
220 pub fn view<V: View>(&self) -> Option<V> {
221 if self.collection() != &V::collection() {
222 None
223 } else {
224 Some(V::from_entity(self.clone()))
225 }
226 }
227
228 #[cfg_attr(feature = "instrument", tracing::instrument(level="debug", skip_all, fields(entity = %self, event = %event)))]
230 pub async fn apply_event<E>(&self, getter: &E, event: &Event) -> Result<bool, MutationError>
231 where E: GetEvents + Send + Sync {
232 debug!("apply_event head: {event} to {self}");
233
234 if event.is_entity_create() && !self.head().is_empty() {
252 if getter.event_stored(&event.id()).await? {
253 return Ok(false);
254 }
255 if getter.storage_is_definitive() {
256 return Err(LineageError::Disjoint.into());
257 }
258 }
260
261 if event.is_entity_create() {
263 let mut state = self.state.write().unwrap();
264 if state.head.is_empty() {
266 for (backend_name, operations) in event.operations.iter() {
268 state.apply_operations_from_event(backend_name.clone(), operations, event.id())?;
269 }
270 state.head = event.id().into();
271 drop(state); self.broadcast.send(());
274 return Ok(true);
275 }
276 }
278
279 if !event.is_entity_create() && self.head().is_empty() {
283 return Err(MutationError::InvalidEvent);
284 }
285
286 let mut head = self.head();
287 const MAX_RETRIES: usize = 5;
289
290 for attempt in 0..MAX_RETRIES {
291 let subject_clock: Clock = event.id().into();
293 let comparison_result = crate::event_dag::compare(getter, &subject_clock, &head, DEFAULT_BUDGET).await?;
294 match comparison_result.relation {
295 AbstractCausalRelation::Equal => {
296 debug!("Equal - skip");
297 return Ok(false);
298 }
299 AbstractCausalRelation::StrictDescends { .. } => {
300 debug!("Descends - apply (attempt {})", attempt + 1);
301 let new_head: Clock = event.id().into();
302 let event_id = event.id();
303 if self.try_mutate(&mut head, |state| -> Result<(), MutationError> {
304 for (backend_name, operations) in event.operations.iter() {
305 state.apply_operations_from_event(backend_name.clone(), operations, event_id.clone())?;
306 }
307 state.head = new_head.clone();
308 Ok(())
309 })? {
310 self.broadcast.send(());
311 return Ok(true);
312 }
313 continue;
314 }
315 AbstractCausalRelation::StrictAscends => {
316 debug!("StrictAscends - incoming event is older, ignoring");
318 return Ok(false);
319 }
320 AbstractCausalRelation::DivergedSince { ref meet, .. } => {
321 debug!("DivergedSince - true concurrency, applying via layers (attempt {})", attempt + 1);
322
323 let meet = meet.clone();
324
325 let (_relation, accumulator) = comparison_result.into_parts();
328 let mut layers = accumulator.into_layers(meet.clone(), head.as_slice().to_vec());
329
330 let mut applied_layers: Vec<crate::event_dag::EventLayer> = Vec::new();
331
332 let mut all_layers = Vec::new();
334 while let Some(layer) = layers.next().await? {
335 all_layers.push(layer);
336 }
337
338 {
340 let mut state = self.state.write().unwrap();
341 if state.head != head {
343 warn!("Head changed during lineage comparison, retrying...");
344 head = state.head.clone();
345 continue;
346 }
347
348 for layer in all_layers {
350 for evt in &layer.to_apply {
352 for (backend_name, _) in evt.operations.iter() {
353 if !state.backends.contains_key(backend_name) {
354 let backend = backend_from_string(backend_name, None)?;
355 for earlier in &applied_layers {
357 backend.apply_layer(earlier)?;
358 }
359 state.backends.insert(backend_name.clone(), backend);
360 }
361 }
362 }
363
364 for (_backend_name, backend) in state.backends.iter() {
366 backend.apply_layer(&layer)?;
367 }
368 applied_layers.push(layer);
369 }
370
371 for parent_id in &meet {
375 state.head.remove(parent_id);
376 }
377 state.head.insert(event.id());
378 }
379 self.broadcast.send(());
380 return Ok(true);
381 }
382 AbstractCausalRelation::Disjoint { .. } => {
383 return Err(LineageError::Disjoint.into());
384 }
385 AbstractCausalRelation::BudgetExceeded { subject, other } => {
386 return Err(LineageError::BudgetExceeded {
387 original_budget: DEFAULT_BUDGET,
388 subject_frontier: subject,
389 other_frontier: other,
390 }
391 .into());
392 }
393 }
394 }
395
396 warn!("apply_event retries exhausted while chasing moving head");
397 Err(MutationError::TOCTOUAttemptsExhausted)
398 }
399
400 pub async fn apply_state<E>(&self, getter: &E, state: &State) -> Result<StateApplyResult, MutationError>
408 where E: GetEvents + Send + Sync {
409 let mut head = self.head();
410 let new_head = state.head.clone();
411
412 debug!("{self} apply_state - new head: {new_head}");
413 const MAX_RETRIES: usize = 5;
414
415 for attempt in 0..MAX_RETRIES {
416 let comparison_result = crate::event_dag::compare(getter, &new_head, &head, DEFAULT_BUDGET).await?;
417 match comparison_result.relation {
418 AbstractCausalRelation::Equal => {
419 debug!("{self} apply_state - heads are equal, skipping");
420 return Ok(StateApplyResult::AlreadyApplied);
421 }
422 AbstractCausalRelation::StrictDescends { .. } => {
423 debug!("{self} apply_state - new head descends from current, applying (attempt {})", attempt + 1);
424 let new_head = state.head.clone();
425 if self.try_mutate(&mut head, |es| -> Result<(), MutationError> {
426 for (name, state_buffer) in state.state_buffers.iter() {
427 let backend = backend_from_string(name, Some(state_buffer))?;
428 es.backends.insert(name.to_owned(), backend);
429 }
430 es.head = new_head;
431 Ok(())
432 })? {
433 self.broadcast.send(());
434 return Ok(StateApplyResult::Applied);
435 }
436 continue;
437 }
438 AbstractCausalRelation::StrictAscends => {
439 debug!("{self} apply_state - new head {new_head} is older than current {head}, ignoring");
441 return Ok(StateApplyResult::Older);
442 }
443 AbstractCausalRelation::DivergedSince { meet, .. } => {
444 warn!(
450 "{self} apply_state - new head {new_head} diverged from {head}, meet: {meet:?}. \
451 State not applied; events required for proper merge."
452 );
453 return Ok(StateApplyResult::DivergedRequiresEvents);
454 }
455 AbstractCausalRelation::Disjoint { .. } => {
456 error!("{self} apply_state - heads are disjoint (different genesis)");
457 return Err(LineageError::Disjoint.into());
458 }
459 AbstractCausalRelation::BudgetExceeded { subject, other } => {
460 tracing::warn!("{self} apply_state - budget exceeded. subject: {subject:?}, other: {other:?}");
461 return Err(LineageError::BudgetExceeded {
462 original_budget: DEFAULT_BUDGET,
463 subject_frontier: subject,
464 other_frontier: other,
465 }
466 .into());
467 }
468 }
469 }
470
471 warn!("apply_state retries exhausted while chasing moving head");
472 Err(MutationError::TOCTOUAttemptsExhausted)
473 }
474
475 pub fn snapshot(&self, trx_alive: Arc<AtomicBool>) -> Self {
478 let state = self.state.read().expect("other thread panicked, panic here too");
480 let mut forked = BTreeMap::new();
481 for (name, backend) in &state.backends {
482 forked.insert(name.clone(), backend.fork());
483 }
484
485 Self(Arc::new(EntityInner {
486 id: self.id,
487 collection: self.collection.clone(),
488 state: std::sync::RwLock::new(EntityInnerState { head: state.head.clone(), backends: forked }),
489 kind: EntityKind::Transacted { trx_alive, upstream: self.clone() },
490 broadcast: ankurah_signals::broadcast::Broadcast::new(),
491 }))
492 }
493
494 pub fn broadcast(&self) -> &ankurah_signals::broadcast::Broadcast { &self.broadcast }
496
497 pub fn get_backend<P: PropertyBackend>(&self) -> Result<Arc<P>, RetrievalError> {
499 let backend_name = P::property_backend_name();
500 let mut state = self.state.write().expect("other thread panicked, panic here too");
501 if let Some(backend) = state.backends.get(backend_name) {
502 let upcasted = backend.clone().as_arc_dyn_any();
503 Ok(upcasted.downcast::<P>().unwrap()) } else {
505 let backend = backend_from_string(backend_name, None)?;
506 let upcasted = backend.clone().as_arc_dyn_any();
507 let typed_backend = upcasted.downcast::<P>().unwrap(); state.backends.insert(backend_name.to_owned(), backend);
509 Ok(typed_backend)
510 }
511 }
512
513 pub fn values(&self) -> Vec<(String, Option<Value>)> {
514 let state = self.state.read().expect("other thread panicked, panic here too");
515 state
516 .backends
517 .values()
518 .flat_map(|backend| {
519 backend
520 .property_values()
521 .iter()
522 .map(|(name, value)| (name.to_string(), value.clone()))
523 .collect::<Vec<(String, Option<Value>)>>()
524 })
525 .collect()
526 }
527}
528
529impl AbstractEntity for Entity {
531 fn collection(&self) -> ankurah_proto::CollectionId { self.collection.clone() }
532
533 fn id(&self) -> &ankurah_proto::EntityId { &self.id }
534
535 fn value(&self, field: &str) -> Option<crate::value::Value> {
536 if field == "id" {
537 Some(crate::value::Value::EntityId(self.id))
538 } else {
539 let state = self.state.read().expect("other thread panicked, panic here too");
541 state.backends.values().find_map(|backend| backend.property_value(&field.into()))
542 }
543 }
544}
545
546impl std::fmt::Display for Entity {
547 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
548 write!(f, "Entity({}/{} {:#})", self.collection, self.id.to_base64_short(), self.head())
549 }
550}
551
552impl Filterable for Entity {
553 fn collection(&self) -> &str { self.collection.as_str() }
554
555 fn value(&self, name: &str) -> Option<Value> {
556 if name == "id" {
557 Some(Value::EntityId(self.id))
558 } else {
559 let state = self.state.read().expect("other thread panicked, panic here too");
561 state.backends.values().find_map(|backend| backend.property_value(&name.to_owned()))
562 }
563 }
564}
565
566impl TemporaryEntity {
567 pub fn new(id: EntityId, collection: CollectionId, state: &State) -> Result<Self, RetrievalError> {
568 let mut backends = BTreeMap::new();
570 for (name, state_buffer) in state.state_buffers.iter() {
571 let backend = backend_from_string(name, Some(state_buffer))?;
572 backends.insert(name.to_owned(), backend);
573 }
574
575 Ok(Self(Arc::new(EntityInner {
576 id,
577 collection,
578 state: std::sync::RwLock::new(EntityInnerState { head: state.head.clone(), backends }),
579 kind: EntityKind::Primary,
580 broadcast: ankurah_signals::broadcast::Broadcast::new(),
582 })))
583 }
584 pub fn values(&self) -> Vec<(String, Option<Value>)> {
585 let state = self.0.state.read().expect("other thread panicked, panic here too");
586 state.backends.values().flat_map(|backend| backend.property_values()).collect()
587 }
588}
589
590impl Filterable for TemporaryEntity {
592 fn collection(&self) -> &str { self.0.collection.as_str() }
593
594 fn value(&self, name: &str) -> Option<Value> {
595 if name == "id" {
596 Some(Value::EntityId(self.0.id))
597 } else {
598 let state = self.0.state.read().expect("other thread panicked, panic here too");
600 state.backends.values().find_map(|backend| backend.property_value(&name.to_owned()))
601 }
602 }
603}
604
605impl std::fmt::Display for TemporaryEntity {
606 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
607 write!(f, "TemporaryEntity({}/{}) = {}", &self.collection, self.id, self.0.state.read().unwrap().head)
608 }
609}
610
611#[derive(Clone, Default)]
614pub struct WeakEntitySet(Arc<std::sync::RwLock<BTreeMap<EntityId, WeakEntity>>>);
615impl WeakEntitySet {
616 pub fn get(&self, id: &EntityId) -> Option<Entity> {
617 let entities = self.0.read().unwrap();
618 if let Some(entity) = entities.get(id) {
620 entity.upgrade()
621 } else {
622 None
623 }
624 }
625
626 pub async fn get_or_retrieve<S, E>(
627 &self,
628 state_getter: &S,
629 event_getter: &E,
630 collection_id: &CollectionId,
631 id: &EntityId,
632 ) -> Result<Option<Entity>, RetrievalError>
633 where
634 S: GetState + Send + Sync,
635 E: GetEvents + Send + Sync,
636 {
637 match self.get(id) {
639 Some(entity) => Ok(Some(entity)),
640 None => match state_getter.get_state(*id).await? {
641 None => Ok(None),
642 Some(state) => {
643 let (_, entity) =
646 self.with_state(state_getter, event_getter, *id, collection_id.to_owned(), state.payload.state).await?;
647 Ok(Some(entity))
648 }
649 },
650 }
651 }
652 pub async fn get_retrieve_or_create<S, E>(
654 &self,
655 state_getter: &S,
656 event_getter: &E,
657 collection_id: &CollectionId,
658 id: &EntityId,
659 ) -> Result<Entity, RetrievalError>
660 where
661 S: GetState + Send + Sync,
662 E: GetEvents + Send + Sync,
663 {
664 match self.get_or_retrieve(state_getter, event_getter, collection_id, id).await? {
665 Some(entity) => Ok(entity),
666 None => {
667 let mut entities = self.0.write().unwrap();
668 if let Some(entity) = entities.get(id) {
670 if let Some(entity) = entity.upgrade() {
671 return Ok(entity);
672 }
673 }
674 let entity = Entity::create(*id, collection_id.to_owned());
675 entities.insert(*id, entity.weak());
676 Ok(entity)
677 }
678 }
679 }
680 pub fn create(&self, collection: CollectionId) -> Entity {
682 let mut entities = self.0.write().unwrap();
683 let id = EntityId::new();
684 let entity = Entity::create(id, collection);
685 entities.insert(id, entity.weak());
686 entity
687 }
688
689 pub fn remove_if_phantom(&self, id: &EntityId) -> bool {
695 let mut entities = self.0.write().unwrap();
696 if let Some(weak) = entities.get(id) {
697 if let Some(entity) = weak.upgrade() {
698 if !entity.head().is_empty() {
699 return false;
700 }
701 }
702 entities.remove(id);
703 return true;
704 }
705 false
706 }
707
708 #[cfg(feature = "test-helpers")]
719 pub fn conjure_evil_phantom(&self, id: EntityId, collection: CollectionId) -> Entity {
720 let mut entities = self.0.write().unwrap();
721 let entity = Entity::create(id, collection);
722 entities.insert(id, entity.weak());
723 entity
724 }
725
726 fn private_get_or_create(&self, id: EntityId, collection_id: &CollectionId, state: &State) -> Result<(bool, Entity), RetrievalError> {
729 let mut entities = self.0.write().unwrap();
730 if let Some(existing_weak) = entities.get(&id) {
731 if let Some(existing_entity) = existing_weak.upgrade() {
732 debug!("Entity {id} was created by another thread during async work, using that one");
733 return Ok((true, existing_entity));
734 }
735 }
736 let entity = Entity::from_state(id, collection_id.to_owned(), state)?;
737 entities.insert(id, entity.weak());
738 Ok((false, entity))
739 }
740
741 pub async fn with_state<S, E>(
745 &self,
746 state_getter: &S,
747 event_getter: &E,
748 id: EntityId,
749 collection_id: CollectionId,
750 state: State,
751 ) -> Result<(Option<bool>, Entity), RetrievalError>
752 where
753 S: GetState + Send + Sync,
754 E: GetEvents + Send + Sync,
755 {
756 let entity = match self.get(&id) {
757 Some(entity) => entity, None => {
759 if let Some(stored_state) = state_getter.get_state(id).await? {
761 self.private_get_or_create(id, &collection_id, &stored_state.payload.state)?.1
764 } else {
765 match self.private_get_or_create(id, &collection_id, &state)? {
767 (true, entity) => entity, (false, entity) => {
769 return Ok((None, entity));
771 }
772 }
773 }
774 }
775 };
776
777 let result = entity.apply_state(event_getter, &state).await?;
779 let changed = matches!(result, StateApplyResult::Applied);
780 Ok((Some(changed), entity))
781 }
782}