Idempotency Key Replay Store
Track in-flight and completed requests by idempotency key, replay stored responses within a TTL, and release abandoned work.
Code
use std::collections::HashMap;
use std::sync::Mutex;
use std::time::{Duration, Instant};
#[derive(Clone)]
struct CachedResponse {
status: u16,
body: String,
stored_at: Instant,
}
enum Entry {
InProgress,
Done(CachedResponse),
}
enum Outcome {
Fresh,
InProgress,
Replay(CachedResponse),
}
struct IdempotencyStore {
entries: Mutex<HashMap<String, Entry>>,
ttl: Duration,
}
impl IdempotencyStore {
fn new(ttl: Duration) -> Self {
Self {
entries: Mutex::new(HashMap::new()),
ttl,
}
}
fn begin(&self, key: &str) -> Outcome {
let mut entries = self.entries.lock().expect("store poisoned");
let replay = match entries.get(key) {
Some(Entry::InProgress) => return Outcome::InProgress,
Some(Entry::Done(cached)) if cached.stored_at.elapsed() < self.ttl => {
Some((*cached).clone())
}
_ => None,
};
if let Some(cached) = replay {
return Outcome::Replay(cached);
}
entries.insert(key.to_string(), Entry::InProgress);
Outcome::Fresh
}
fn complete(&self, key: &str, status: u16, body: &str) {
let mut entries = self.entries.lock().expect("store poisoned");
entries.insert(
key.to_string(),
Entry::Done(CachedResponse {
status,
body: body.to_string(),
stored_at: Instant::now(),
}),
);
}
fn abandon(&self, key: &str) {
self.entries
.lock()
.expect("store poisoned")
.remove(key);
}
}
fn main() {
let store = IdempotencyStore::new(Duration::from_secs(60));
match store.begin("order-1") {
Outcome::Fresh => println!("first request: fresh"),
_ => println!("unexpected first outcome"),
}
match store.begin("order-1") {
Outcome::InProgress => println!("duplicate while running: in progress"),
_ => println!("unexpected duplicate outcome"),
}
store.complete("order-1", 201, "{\"id\":1}");
match store.begin("order-1") {
Outcome::Replay(cached) => println!("replay: {} {}", cached.status, cached.body),
_ => println!("unexpected replay outcome"),
}
store.abandon("order-2");
}
Report a problem with this exampleOpens a pre-filled issue on GitHub.