fmt
Some checks failed
CI / Check / Test / Build (push) Has been cancelled

This commit is contained in:
2026-05-13 23:38:57 +02:00
parent 7415b91e23
commit 19171806b9
142 changed files with 4140 additions and 2025 deletions

View File

@@ -18,33 +18,42 @@ use payload::DbEventPayload;
pub struct DbEventQueueConfig {
pub poll_interval_ms: u64,
pub batch_size: i64,
pub max_attempts: i32,
pub batch_size: i64,
pub max_attempts: i32,
}
impl DbEventQueueConfig {
pub fn from_env() -> Self {
Self {
poll_interval_ms: std::env::var("EVENT_QUEUE_POLL_INTERVAL_MS")
.ok().and_then(|v| v.parse().ok()).unwrap_or(500),
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(500),
batch_size: std::env::var("EVENT_QUEUE_BATCH_SIZE")
.ok().and_then(|v| v.parse().ok()).unwrap_or(10),
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(10),
max_attempts: std::env::var("EVENT_QUEUE_MAX_ATTEMPTS")
.ok().and_then(|v| v.parse().ok()).unwrap_or(5),
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(5),
}
}
}
#[derive(Clone)]
pub struct PostgresEventQueue {
pool: PgPool,
pool: PgPool,
config: Arc<DbEventQueueConfig>,
}
impl PostgresEventQueue {
pub async fn create(pool: PgPool, config: DbEventQueueConfig) -> anyhow::Result<Self> {
migrations::run(&pool).await?;
Ok(Self { pool, config: Arc::new(config) })
Ok(Self {
pool,
config: Arc::new(config),
})
}
pub async fn create_publisher(pool: PgPool) -> anyhow::Result<Arc<dyn EventPublisher>> {
@@ -68,14 +77,12 @@ impl EventPublisher for PostgresEventQueue {
let payload_json = serde_json::to_string(&db_payload)
.map_err(|e| DomainError::InfrastructureError(format!("serialize: {e}")))?;
sqlx::query(
"INSERT INTO event_queue (event_type, payload) VALUES ($1, $2)"
)
.bind(event_type)
.bind(payload_json)
.execute(&self.pool)
.await
.map_err(|e| DomainError::InfrastructureError(format!("insert event: {e}")))?;
sqlx::query("INSERT INTO event_queue (event_type, payload) VALUES ($1, $2)")
.bind(event_type)
.bind(payload_json)
.execute(&self.pool)
.await
.map_err(|e| DomainError::InfrastructureError(format!("insert event: {e}")))?;
Ok(())
}
@@ -83,10 +90,10 @@ impl EventPublisher for PostgresEventQueue {
impl EventConsumer for PostgresEventQueue {
fn consume(&self) -> BoxStream<'_, Result<EventEnvelope, DomainError>> {
let pool = self.pool.clone();
let config = Arc::clone(&self.config);
let (tx, rx) = mpsc::channel(128);
let rx = Arc::new(Mutex::new(rx));
let pool = self.pool.clone();
let config = Arc::clone(&self.config);
let (tx, rx) = mpsc::channel(128);
let rx = Arc::new(Mutex::new(rx));
tokio::spawn(async move {
let poll_interval = Duration::from_millis(config.poll_interval_ms);
@@ -124,13 +131,13 @@ impl EventConsumer for PostgresEventQueue {
#[derive(sqlx::FromRow)]
struct QueueRow {
id: i64,
payload: String,
id: i64,
payload: String,
attempts: i32,
}
async fn claim_batch(
pool: &PgPool,
pool: &PgPool,
config: &DbEventQueueConfig,
) -> Result<Vec<QueueRow>, DomainError> {
// CTE with FOR UPDATE SKIP LOCKED — atomic and safe for multiple workers
@@ -148,7 +155,7 @@ async fn claim_batch(
FROM claimed
WHERE q.id = claimed.id
RETURNING q.id, q.payload, q.attempts
"#
"#,
)
.bind(config.batch_size)
.fetch_all(pool)
@@ -159,25 +166,28 @@ async fn claim_batch(
}
fn decode_row(
pool: &PgPool,
row: QueueRow,
pool: &PgPool,
row: QueueRow,
max_attempts: i32,
) -> Result<EventEnvelope, DomainError> {
let db_payload: DbEventPayload = serde_json::from_str(&row.payload)
.map_err(|e| DomainError::InfrastructureError(format!("deserialize: {e}")))?;
let event = DomainEvent::try_from(db_payload)?;
Ok(EventEnvelope::new(event, Box::new(DbAckHandle {
pool: pool.clone(),
row_id: row.id,
attempts: row.attempts,
max_attempts,
})))
Ok(EventEnvelope::new(
event,
Box::new(DbAckHandle {
pool: pool.clone(),
row_id: row.id,
attempts: row.attempts,
max_attempts,
}),
))
}
struct DbAckHandle {
pool: PgPool,
row_id: i64,
attempts: i32,
pool: PgPool,
row_id: i64,
attempts: i32,
max_attempts: i32,
}