I would like to be able to call a reducer on another database from within my database. I want the reducer to be able to return the result, and I would like it to do so atomically:
Receiver (payments) — a plain reducer that returns a value. ctx.sender is the calling database's identity.
#[spacetimedb::reducer]
fn charge(ctx: &ReducerContext, customer: Identity, amount: u64) -> Result<u64, String> {
let mut acct = ctx.db.accounts().customer().find(customer).ok_or("no account")?;
if acct.balance < amount { return Err("insufficient funds".into()); } // aborts the whole tx
acct.balance -= amount;
let new_balance = acct.balance;
ctx.db.accounts().customer().update(acct);
Ok(new_balance)
}
Caller (orders) — the call is inline; the local insert and the remote charge commit together or both roll back.
use payments;
#[spacetimedb::reducer]
fn place_order(ctx: &ReducerContext, item_id: u64, price: u64) -> Result<(), String> {
let payment_db = ctx.db.config().single().payment_db; // a stored payments::Identity
// Sync cross-database call (Level 7, 2PC). `?` aborts the whole distributed tx on Err.
let new_balance = payments::Identity(payment_db).reducers.charge(ctx.sender, price)?;
ctx.db.orders().insert(Order { id: 0, customer: ctx.sender, item_id, price, balance_after: new_balance });
Ok(())
}