use serde::{Serialize, de::DeserializeOwned};
use es_entity::hooks::HookOperation;
use crate::{
inbox::{InboxError, InboxEvent, InboxEventId, InboxEventStatus, InboxIdempotencyKey},
out::{
DecodeFailure, EphemeralEventType, EphemeralOutboxEvent, OutboxEventId,
PersistentOutboxEvent, UndecodableEventError,
},
sequence::*,
};
#[derive(Clone)]
#[cfg_attr(feature = "default-tables", derive(obix_macros::MailboxTables))]
#[cfg_attr(feature = "default-tables", obix(crate = "crate"))]
pub struct DefaultMailboxTables;
#[doc(hidden)]
#[allow(clippy::result_large_err)]
pub fn decode_persistent_event<P>(
id: OutboxEventId,
sequence: u64,
recorded_at: chrono::DateTime<chrono::Utc>,
tracing_context: Option<es_entity::context::TracingContext>,
payload: Option<serde_json::Value>,
commit_group: CommitGroupId,
) -> Result<PersistentOutboxEvent<P>, UndecodableEventError>
where
P: Serialize + DeserializeOwned + Send,
{
let sequence = EventSequence::from(sequence);
let payload = match payload {
None => None,
Some(raw) => match P::deserialize(&raw) {
Ok(payload) => Some(payload),
Err(error) => {
record_persistent_payload_undecodable(&error, u64::from(sequence));
return Err(UndecodableEventError {
id,
sequence,
recorded_at,
failure: DecodeFailure {
error: error.to_string(),
raw,
},
commit_group,
});
}
},
};
Ok(PersistentOutboxEvent {
id,
sequence,
payload,
tracing_context,
recorded_at,
commit_group,
})
}
#[tracing::instrument(
name = "obix.tables.persistent_payload_undecodable",
level = "error",
skip_all,
fields(otel.status_code = "ERROR", error = %error, sequence = sequence)
)]
fn record_persistent_payload_undecodable(error: &serde_json::Error, sequence: u64) {}
#[doc(hidden)]
#[tracing::instrument(
name = "obix.tables.ephemeral_payload_undecodable",
level = "error",
skip_all,
fields(otel.status_code = "ERROR", error = %error, event_type = %event_type)
)]
pub fn record_ephemeral_payload_undecodable(error: &serde_json::Error, event_type: &str) {}
#[doc(hidden)]
#[tracing::instrument(
name = "obix.tables.ephemeral_event_type_undecodable",
level = "error",
skip_all,
fields(otel.status_code = "ERROR", error = %error, event_type = %event_type)
)]
pub fn record_ephemeral_event_type_undecodable(error: &serde_json::Error, event_type: &str) {}
#[doc(hidden)]
#[tracing::instrument(
name = "obix.tables.tracing_context_undecodable",
level = "error",
skip_all,
fields(otel.status_code = "ERROR", error = %error)
)]
pub fn record_tracing_context_undecodable(error: &serde_json::Error) {}
pub type PersistentEventRows<P> = Vec<Result<PersistentOutboxEvent<P>, UndecodableEventError>>;
pub trait MailboxTables: Send + Sync + 'static {
fn highest_known_persistent_sequence<'a>(
op: impl es_entity::IntoOneTimeExecutor<'a>,
) -> impl Future<Output = Result<EventSequence, sqlx::Error>> + Send;
fn persist_events<'a, P>(
op: &mut HookOperation<'a>,
events: impl Iterator<Item = P>,
) -> impl Future<Output = Result<Vec<PersistentOutboxEvent<P>>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn persist_events_notifying<'a, P>(
op: &mut HookOperation<'a>,
events: impl Iterator<Item = P>,
) -> impl Future<Output = Result<Vec<PersistentOutboxEvent<P>>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn load_next_page<P>(
pool: &sqlx::PgPool,
from_sequence: EventSequence,
buffer_size: usize,
) -> impl Future<Output = Result<PersistentEventRows<P>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn load_next_contiguous_page<P>(
pool: &sqlx::PgPool,
from_sequence: EventSequence,
buffer_size: usize,
) -> impl Future<Output = Result<PersistentEventRows<P>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn sequence_present(
pool: &sqlx::PgPool,
sequence: EventSequence,
) -> impl Future<Output = Result<bool, sqlx::Error>> + Send;
fn missing_sequences(
pool: &sqlx::PgPool,
after_sequence: EventSequence,
up_to_sequence: EventSequence,
) -> impl Future<Output = Result<Vec<EventSequence>, sqlx::Error>> + Send;
fn fill_gaps<P>(
pool: &sqlx::PgPool,
sequences: Vec<EventSequence>,
) -> impl Future<Output = Result<PersistentEventRows<P>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn fill_gaps_deduped<P>(
pool: &sqlx::PgPool,
sequences: Vec<EventSequence>,
) -> impl Future<Output = Result<Option<PersistentEventRows<P>>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn abandonment_marker(
pool: &sqlx::PgPool,
) -> impl Future<Output = Result<(String, EventSequence), sqlx::Error>> + Send;
fn abandonment_proof_passed(
pool: &sqlx::PgPool,
marker: &str,
) -> impl Future<Output = Result<bool, sqlx::Error>> + Send;
fn load_events_in_range<P>(
pool: &sqlx::PgPool,
after_sequence: EventSequence,
up_to_sequence: EventSequence,
) -> impl Future<Output = Result<PersistentEventRows<P>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn persist_ephemeral_event<P>(
pool: &sqlx::PgPool,
now: Option<chrono::DateTime<chrono::Utc>>,
event_type: EphemeralEventType,
payload: P,
) -> impl Future<Output = Result<EphemeralOutboxEvent<P>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn persist_ephemeral_event_in_op<'a, P>(
op: &mut HookOperation<'a>,
event_type: EphemeralEventType,
payload: P,
) -> impl Future<Output = Result<EphemeralOutboxEvent<P>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn load_ephemeral_events<P>(
pool: &sqlx::PgPool,
event_type_filter: Option<EphemeralEventType>,
) -> impl Future<Output = Result<Vec<EphemeralOutboxEvent<P>>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn append_commit_group<P>(
pool: &sqlx::PgPool,
group: CommitGroupId,
at: EventSequence,
) -> impl Future<Output = Result<CommitGroupAppend<P>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn commit_log_restart_state(
pool: &sqlx::PgPool,
) -> impl Future<Output = Result<CommitRestartState, sqlx::Error>> + Send;
fn load_commit_ordered_page<P>(
pool: &sqlx::PgPool,
after: CommitSequence,
limit: usize,
) -> impl Future<Output = Result<Vec<CommitLogRow<P>>, sqlx::Error>> + Send
where
P: Serialize + DeserializeOwned + Send;
fn commit_log_state(
pool: &sqlx::PgPool,
) -> impl Future<Output = Result<(CommitSequence, EventSequence), sqlx::Error>> + Send;
fn persistent_outbox_events_channel() -> &'static str;
fn ephemeral_outbox_events_channel() -> &'static str;
fn persistent_outbox_commit_log_table() -> &'static str;
fn persistent_outbox_events_table() -> &'static str;
const KEYED_WAKER_JOB_TYPE: &'static str;
fn insert_inbox_event<P>(
op: &mut impl es_entity::AtomicOperation,
idempotency_key: &InboxIdempotencyKey,
payload: &P,
) -> impl Future<Output = Result<Option<InboxEventId>, sqlx::Error>> + Send
where
P: Serialize + Send + Sync;
fn find_inbox_event_by_id(
pool: &sqlx::PgPool,
id: InboxEventId,
) -> impl Future<Output = Result<InboxEvent, InboxError>> + Send;
fn update_inbox_event_status(
pool: &sqlx::PgPool,
now: Option<chrono::DateTime<chrono::Utc>>,
id: InboxEventId,
status: InboxEventStatus,
error: Option<&str>,
) -> impl Future<Output = Result<(), sqlx::Error>> + Send;
fn update_inbox_event_status_in_op(
op: &mut impl es_entity::AtomicOperation,
id: InboxEventId,
status: InboxEventStatus,
error: Option<&str>,
) -> impl Future<Output = Result<(), sqlx::Error>> + Send;
fn list_inbox_events_by_status(
pool: &sqlx::PgPool,
status: InboxEventStatus,
limit: usize,
) -> impl Future<Output = Result<Vec<InboxEvent>, InboxError>> + Send;
fn insert_subscription_in_op(
op: &mut impl es_entity::AtomicOperation,
subscriber_type: &str,
key: &str,
wake_keys: &[String],
instance_config: serde_json::Value,
start_after: EventSequence,
) -> impl Future<Output = Result<(), sqlx::Error>> + Send;
fn delete_subscription_in_op(
op: &mut impl es_entity::AtomicOperation,
subscriber_type: &str,
key: &str,
) -> impl Future<Output = Result<(), sqlx::Error>> + Send;
fn find_subscription(
pool: &sqlx::PgPool,
subscriber_type: &str,
key: &str,
) -> impl Future<Output = Result<Option<SubscriptionRow>, sqlx::Error>> + Send;
fn update_subscription_checkpoint_in_op(
op: &mut impl es_entity::AtomicOperation,
subscriber_type: &str,
key: &str,
checkpoint: EventSequence,
) -> impl Future<Output = Result<(), sqlx::Error>> + Send;
fn subscriptions_behind(
op: &mut impl es_entity::AtomicOperation,
subscriber_types: &[String],
below: EventSequence,
limit: i64,
) -> impl Future<Output = Result<Vec<(String, String)>, sqlx::Error>> + Send;
fn subscriptions_for_wake_keys(
op: &mut impl es_entity::AtomicOperation,
subscriber_types: &[String],
wake_keys: &[String],
) -> impl Future<Output = Result<Vec<(String, String)>, sqlx::Error>> + Send;
}
#[doc(hidden)]
pub struct CommitLogRow<P>
where
P: Serialize + DeserializeOwned + Send,
{
pub commit_sequence: CommitSequence,
pub commit_boundary: bool,
pub event: Result<PersistentOutboxEvent<P>, UndecodableEventError>,
}
#[doc(hidden)]
pub struct CommitGroupAppend<P>
where
P: Serialize + DeserializeOwned + Send,
{
pub group_max: EventSequence,
pub appended: Vec<CommitLogRow<P>>,
}
#[derive(Debug, Clone)]
pub struct CommitRestartState {
pub last_commit_seq: CommitSequence,
pub logged_through: EventSequence,
pub logged_ahead: Vec<EventSequence>,
}
#[derive(Debug, Clone)]
pub struct SubscriptionRow {
pub wake_keys: Vec<String>,
pub instance_config: serde_json::Value,
pub start_after: EventSequence,
pub created_at: chrono::DateTime<chrono::Utc>,
}