use crate::{
changes::EntityChange,
error::{ApplyError, ApplyErrorItem, MutationError},
node::Node,
policy::PolicyAgent,
retrieval::{CachedEventGetter, GetState, LocalStateGetter, SuspenseEvents},
storage::StorageEngine,
util::ready_chunks::ReadyChunks,
};
use ankurah_proto::{self as proto};
use futures::stream::StreamExt;
use proto::Attested;
pub struct NodeApplier;
impl NodeApplier {
pub(crate) async fn apply_updates<SE, PA>(
node: &Node<SE, PA>,
from_peer_id: &proto::EntityId,
items: Vec<proto::SubscriptionUpdateItem>,
) -> Result<(), ApplyError>
where
SE: StorageEngine + Send + Sync + 'static,
PA: PolicyAgent + Send + Sync + 'static,
{
tracing::debug!("received subscription update for {} items", items.len());
let Some(relay) = &node.subscription_relay else {
return Err(MutationError::InvalidUpdate("Should not be receiving updates without a subscription relay").into());
};
let cdata = relay.get_contexts_for_peer(from_peer_id);
if cdata.is_empty() {
return Err(MutationError::InvalidUpdate("Should not be receiving updates without at least predicate context").into());
}
let mut changes = Vec::new();
let mut errors: Vec<ApplyErrorItem> = Vec::new();
for update in items {
let entity_id = update.entity_id;
let item_collection = update.collection.clone();
let result = async {
let collection = node.collections.get(&update.collection).await?;
let event_getter = CachedEventGetter::new(update.collection.clone(), collection.clone(), node, &cdata);
let state_getter = LocalStateGetter::new(collection);
Self::apply_update(node, from_peer_id, update, &event_getter, &state_getter, &mut changes, &mut ()).await
}
.await;
if let Err(cause) = result {
tracing::warn!("failed to apply update for {}/{}: {}", item_collection, entity_id, cause);
errors.push(ApplyErrorItem { entity_id, collection: item_collection, cause });
}
}
node.reactor.notify_change(changes).await;
if !errors.is_empty() {
return Err(ApplyError::Items(errors));
}
Ok(())
}
fn validate_and_stage<SE, PA, E>(
node: &Node<SE, PA>,
from_peer_id: &proto::EntityId,
entity_id: proto::EntityId,
collection_id: &proto::CollectionId,
event_fragments: Vec<proto::EventFragment>,
event_getter: &E,
) -> Result<Vec<Attested<proto::Event>>, MutationError>
where
SE: StorageEngine + Send + Sync + 'static,
PA: PolicyAgent + Send + Sync + 'static,
E: SuspenseEvents + Send + Sync,
{
let mut attested_events = Vec::new();
for fragment in event_fragments {
let attested_event: Attested<proto::Event> = (entity_id, collection_id.clone(), fragment).into();
node.policy_agent.validate_received_event(node, from_peer_id, &attested_event)?;
event_getter.stage_event(attested_event.payload.clone());
attested_events.push(attested_event);
}
Ok(attested_events)
}
async fn apply_update<SE, PA, E, S>(
node: &Node<SE, PA>,
from_peer_id: &proto::EntityId,
update: proto::SubscriptionUpdateItem,
event_getter: &E,
state_getter: &S,
changes: &mut Vec<EntityChange>,
entities: &mut impl Pushable<crate::entity::Entity>,
) -> Result<(), MutationError>
where
SE: StorageEngine + Send + Sync + 'static,
PA: PolicyAgent + Send + Sync + 'static,
E: SuspenseEvents + Send + Sync,
S: GetState + Send + Sync,
{
let proto::SubscriptionUpdateItem { entity_id, collection: collection_id, content, predicate_relevance: _ } = update;
let collection = node.collections.get(&collection_id).await?;
match content {
proto::UpdateContent::EventOnly(event_fragments) => {
let attested_events =
Self::validate_and_stage(node, from_peer_id, entity_id, &collection_id, event_fragments, event_getter)?;
let attested_events = crate::event_dag::ordering::topo_sort_events(attested_events)?;
let entity = node.entities.get_retrieve_or_create(state_getter, event_getter, &collection_id, &entity_id).await?;
entities.push(entity.clone());
let mut applied_events = Vec::new();
let mut failure: Option<MutationError> = None;
for event in attested_events {
let applied = match entity.apply_event(event_getter, &event.payload).await {
Ok(applied) => applied,
Err(e) => {
failure = Some(e);
break;
}
};
if applied {
if let Err(e) = event_getter.commit_event(&event).await {
failure = Some(e);
break;
}
applied_events.push(event);
}
}
if !applied_events.is_empty() {
changes.push(EntityChange::new(entity.clone(), applied_events)?);
}
if let Some(e) = failure {
node.entities.remove_if_phantom(&entity_id);
return Err(e);
}
}
proto::UpdateContent::StateAndEvent(state_fragment, event_fragments) => {
let attested_events =
Self::validate_and_stage(node, from_peer_id, entity_id, &collection_id, event_fragments, event_getter)?;
let attested_events = crate::event_dag::ordering::topo_sort_events(attested_events)?;
let state = (entity_id, collection_id.clone(), state_fragment.clone()).into();
node.policy_agent.validate_received_state(node, from_peer_id, &state)?;
let (changed, entity) =
node.entities.with_state(state_getter, event_getter, entity_id, collection_id.clone(), state.payload.state).await?;
entities.push(entity.clone());
if matches!(changed, Some(true) | None) {
for event in &attested_events {
event_getter.commit_event(event).await?;
}
Self::save_state(node, &entity, &collection).await?;
changes.push(EntityChange::new(entity, attested_events)?);
} else {
let mut applied_events = Vec::new();
for event in attested_events {
if entity.apply_event(event_getter, &event.payload).await? {
event_getter.commit_event(&event).await?;
applied_events.push(event);
}
}
if !applied_events.is_empty() {
Self::save_state(node, &entity, &collection).await?;
changes.push(EntityChange::new(entity, applied_events)?);
}
}
}
}
Ok(())
}
async fn save_state<SE, PA>(
node: &Node<SE, PA>,
entity: &crate::entity::Entity,
collection_wrapper: &crate::storage::StorageCollectionWrapper,
) -> Result<(), MutationError>
where
SE: StorageEngine + Send + Sync + 'static,
PA: PolicyAgent + Send + Sync + 'static,
{
let state = entity.to_state()?;
let entity_state = proto::EntityState { entity_id: entity.id(), collection: entity.collection().clone(), state };
let attestation = node.policy_agent.attest_state(node, &entity_state);
let attested = Attested::opt(entity_state, attestation);
collection_wrapper.set_state(attested).await?;
Ok(())
}
pub(crate) async fn apply_deltas<SE, PA, E, S>(
node: &Node<SE, PA>,
from_peer_id: &proto::EntityId,
deltas: Vec<proto::EntityDelta>,
event_getter: &E,
state_getter: &S,
) -> Result<(), ApplyError>
where
SE: StorageEngine + Send + Sync + 'static,
PA: PolicyAgent + Send + Sync + 'static,
E: SuspenseEvents + Send + Sync,
S: GetState + Send + Sync,
{
let mut ready_chunks =
ReadyChunks::new(deltas.into_iter().map(|delta| Self::apply_delta(node, from_peer_id, delta, event_getter, state_getter)));
let mut all_errors = Vec::new();
while let Some(results) = ready_chunks.next().await {
let mut batch = Vec::new();
for result in results {
match result {
Ok(Some(change)) => batch.push(change),
Ok(None) => {} Err(error_item) => {
all_errors.push(error_item);
}
}
}
if !batch.is_empty() {
node.reactor.notify_change(batch).await;
}
}
if !all_errors.is_empty() {
return Err(ApplyError::Items(all_errors));
}
Ok(())
}
async fn apply_delta<SE, PA, E, S>(
node: &Node<SE, PA>,
from_peer_id: &proto::EntityId,
delta: proto::EntityDelta,
event_getter: &E,
state_getter: &S,
) -> Result<Option<EntityChange>, ApplyErrorItem>
where
SE: StorageEngine + Send + Sync + 'static,
PA: PolicyAgent + Send + Sync + 'static,
E: SuspenseEvents + Send + Sync,
S: GetState + Send + Sync,
{
let entity_id = delta.entity_id;
let collection = delta.collection.clone();
let result = Self::apply_delta_inner(node, from_peer_id, delta, event_getter, state_getter).await;
result.map_err(|cause| ApplyErrorItem { entity_id, collection, cause })
}
async fn apply_delta_inner<SE, PA, E, S>(
node: &Node<SE, PA>,
from_peer_id: &proto::EntityId,
delta: proto::EntityDelta,
event_getter: &E,
state_getter: &S,
) -> Result<Option<EntityChange>, MutationError>
where
SE: StorageEngine + Send + Sync + 'static,
PA: PolicyAgent + Send + Sync + 'static,
E: SuspenseEvents + Send + Sync,
S: GetState + Send + Sync,
{
let collection = node.collections.get(&delta.collection).await?;
match delta.content {
proto::DeltaContent::StateSnapshot { state } => {
let attested_state = (delta.entity_id, delta.collection.clone(), state).into();
node.policy_agent.validate_received_state(node, from_peer_id, &attested_state)?;
let (_, entity) = node
.entities
.with_state(state_getter, event_getter, delta.entity_id, delta.collection, attested_state.payload.state)
.await?;
Self::save_state(node, &entity, &collection).await?;
Ok(Some(EntityChange::new(entity, Vec::new())?))
}
proto::DeltaContent::EventBridge { events } => {
let attested_events =
Self::validate_and_stage(node, from_peer_id, delta.entity_id, &delta.collection, events, event_getter)?;
let entity = node.entities.get_retrieve_or_create(state_getter, event_getter, &delta.collection, &delta.entity_id).await?;
let attested_events = crate::event_dag::ordering::topo_sort_events(attested_events)?;
for event in attested_events.into_iter() {
entity.apply_event(event_getter, &event.payload).await?;
event_getter.commit_event(&event).await?;
}
Self::save_state(node, &entity, &collection).await?;
Ok(Some(EntityChange::new(entity, Vec::new())?))
}
proto::DeltaContent::StateAndRelation { .. } => Err(MutationError::InvalidUpdate("StateAndRelation not yet implemented")),
}
}
}
trait Pushable<T> {
fn push(&mut self, value: T);
}
impl<T> Pushable<T> for Vec<T> {
fn push(&mut self, value: T) { self.push(value); }
}
impl<T> Pushable<T> for &mut Vec<T> {
fn push(&mut self, value: T) { (*self).push(value); }
}
impl<T> Pushable<T> for () {
fn push(&mut self, _: T) {
}
}