use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use amalgam::{
Backplane, Cache, Clock, DistributedCache, DistributedSerializer, EntryOptions,
InMemoryDistributedCache, InProcessBackplane, JsonSerializer, SystemClock,
};
#[tokio::main]
async fn main() {
let clock: Arc<dyn Clock> = Arc::new(SystemClock);
let l2: Arc<dyn DistributedCache> = Arc::new(InMemoryDistributedCache::new(clock.clone()));
let serializer: Arc<dyn DistributedSerializer<String>> = Arc::new(JsonSerializer);
let backplane: Arc<dyn Backplane> = Arc::new(InProcessBackplane::default());
let opts = EntryOptions::new(Duration::from_secs(60));
let build = |id: &str| -> Cache<String> {
Cache::builder()
.clock(clock.clone())
.distributed(l2.clone())
.serializer(serializer.clone())
.backplane(backplane.clone())
.default_options(opts.clone())
.instance_id(id)
.build()
};
let node1 = build("node-1");
let node2 = build("node-2");
let factory_runs = Arc::new(AtomicUsize::new(0));
let v1 = {
let runs = factory_runs.clone();
node1
.get_or_set("profile", move |ctx| async move {
runs.fetch_add(1, Ordering::SeqCst);
println!(" [node-1 factory] producing the value");
Ok(ctx.value("Alice".to_owned()))
})
.await
.expect("node-1 produces the value")
};
println!("node-1 get_or_set => {v1:?}");
let v2 = {
let runs = factory_runs.clone();
node2
.get_or_set("profile", move |ctx| async move {
runs.fetch_add(1, Ordering::SeqCst);
println!(" [node-2 factory] (this should NOT run)");
Ok(ctx.value("should-not-run".to_owned()))
})
.await
.expect("node-2 reads through L2")
};
println!("node-2 get_or_set => {v2:?} (came from shared L2, no factory)");
assert_eq!(v2, "Alice", "node-2 must serve node-1's value from L2");
assert_eq!(
factory_runs.load(Ordering::SeqCst),
1,
"only node-1's factory ran"
);
assert_eq!(
node2.try_get("profile", None).await.value(),
Some(&"Alice".to_owned())
);
println!("\nnode-1 removes \"profile\" — backplane will invalidate node-2…");
node1.remove("profile").await;
tokio::time::sleep(Duration::from_millis(150)).await;
let after = node2.try_get("profile", None).await;
println!(
"node-2 try_get => {:?} (evicted by the backplane)",
after.value()
);
assert!(
!after.has_value(),
"node-2's L1 copy must be evicted after node-1's remove propagates"
);
println!("OK: L2 read-through worked and the backplane invalidated the peer.");
}