Forum
Usage with the SDK
Driving Forum through WeaveNode — the recommended entry point for most applications.
Why use the SDK
The weave-sdk crate exposes Forum through a namespaced API on WeaveNode. The SDK handles the Strand lifecycle, replication wiring, and identity binding. You write, read, take, and the SDK does the rest.
Open, write, take
use weave_sdk::prelude::*;
#[tokio::main]
async fn main() -> WeaveResult<()> {
let node = WeaveNode::builder()
.namespace("l1fe")
.identifier("queue-runner")
.storage_dir("/tmp/weave-queue")
.build()
.await?;
node.open_forum("jobs").await?;
// Producer.
node.forum_write("jobs", b"render frame 42".to_vec()).await?;
node.forum_write("jobs", b"render frame 43".to_vec()).await?;
// Peek (does not consume).
let peek = node.forum_read("jobs", b"render frame 42").await?;
assert_eq!(peek.as_deref(), Some(b"render frame 42".as_slice()));
// Consume.
let taken = node.forum_take("jobs", b"render frame 42").await?;
assert_eq!(taken.as_deref(), Some(b"render frame 42".as_slice()));
// The take is durable — restart the process and the tuple is gone.
let gone = node.forum_take("jobs", b"render frame 42").await?;
assert!(gone.is_none());
Ok(())
}SDK surface for Forum
| Call | Returns | Notes |
|---|---|---|
node.open_forum(name) | WeaveResult<()> | Idempotent; first call creates the strand |
node.forum_write(name, tuple) | WeaveResult<u64> | Returns the EntryId of the written tuple |
node.forum_read(name, pattern) | WeaveResult<Option<Vec<u8>>> | Non-consuming match |
node.forum_take(name, pattern) | WeaveResult<Option<Vec<u8>>> | Atomic consume |
node.forums() | &Arc<RwLock<ForumStore>> | Direct access for advanced flows |
A worker pool
use weave_sdk::prelude::*;
use std::time::Duration;
async fn worker(node: WeaveNode, pattern: &'static [u8]) -> WeaveResult<()> {
loop {
match node.forum_take("jobs", pattern).await? {
Some(payload) => {
tracing::info!("got {} bytes", payload.len());
process(&payload).await;
}
None => tokio::time::sleep(Duration::from_millis(50)).await,
}
}
}
async fn process(_p: &[u8]) {
// your job logic
}Errors and recovery
| Surface | Error | What to do |
|---|---|---|
open_forum on existing name | Returns existing handle | No action |
forum_write with disk full | WeaveError::Storage from Strand | Free disk, retry; previous state is consistent |
forum_take returning None | No matching live tuple | Backoff and poll |
| Process crash mid-take | live_tuples may have removed the entry without the tombstone landing in the strand | On restart, the Forum rebuilds live_tuples from the strand, including the dropped entry — your worker may see the tuple again. Make handlers idempotent. |
Idempotent consumption pattern
Pair each tuple with a Lens-stored claim before doing irreversible work:
use weave_sdk::prelude::*;
async fn consume_idempotent(node: &WeaveNode, tuple: Vec<u8>) -> WeaveResult<()> {
let claim_key = blake3::hash(&tuple).as_bytes().to_vec();
if node.lens_get("processed", &claim_key).await?.is_some() {
return Ok(()); // already handled
}
// Take the tuple from the forum.
if let Some(payload) = node.forum_take("jobs", &tuple).await? {
do_work(&payload).await;
node.lens_put("processed", &claim_key, b"1").await?;
}
Ok(())
}
async fn do_work(_p: &[u8]) { /* ... */ }This is the recommended shape for cross-peer or crash-tolerant consumers; see Distributed Coordination for the broader pattern.