use std::time::Duration;
use ruststream_fred::stream::prelude::*;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Order {
id: u64,
}
#[subscriber(
RedisStream::new("audit").group("auditors"),
start_at(RedisGroupPosition::beginning())
)]
async fn replay(order: &Order) -> HandlerOutcome {
println!("audit: replayed order {}", order.id);
HandlerOutcome::ack()
}
#[subscriber(RedisStream::new("orders").group("workers"))]
async fn handle(order: &Order, Ctx(seeker): Ctx<keys::SeekHandle>) -> HandlerOutcome {
if order.id == 0 {
if seeker.seek(RedisGroupPosition::end()).await.is_err() {
return HandlerOutcome::retry();
}
println!("orders: skipped the poison region");
return HandlerOutcome::ack();
}
println!("orders: processed {}", order.id);
HandlerOutcome::ack()
}
#[subscriber(RedisStream::new("orders.bulk").group("bulk"))]
async fn handle_batch(
orders: &[Order],
ctx: &mut Context<'_, StreamBatchContext>,
) -> HandlerOutcome {
if orders.iter().any(|order| order.id == 0) {
println!(
"orders.bulk: rewinding group {}",
ctx.context(keys::ConsumerGroup)
);
if ctx
.context(keys::SeekHandle)
.seek(RedisGroupPosition::beginning())
.await
.is_err()
{
return HandlerOutcome::retry();
}
}
HandlerOutcome::ack()
}
#[ruststream::app]
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(
RedisBroker::standalone("redis://localhost:6379"),
|b| {
b.include(handle);
b.include(
handle_batch
.batch(nonzero!(16))
.block(Duration::from_secs(2)),
);
b.include(replay);
},
)
}