use netabase_store::NetabaseStore;
use netabase_store::netabase_definition_module;
use netabase_store::traits::batch::{BatchBuilder, Batchable};
#[netabase_definition_module(BankDefinition, BankKeys)]
pub mod models {
use netabase_store::{NetabaseModel, netabase};
#[derive(
NetabaseModel,
Clone,
Debug,
bincode::Encode,
bincode::Decode,
serde::Serialize,
serde::Deserialize,
)]
#[netabase(BankDefinition)]
pub struct Account {
#[primary_key]
pub account_id: u64,
pub owner: String,
pub balance: f64,
#[secondary_key]
pub account_type: String,
}
#[derive(
NetabaseModel,
Clone,
Debug,
bincode::Encode,
bincode::Decode,
serde::Serialize,
serde::Deserialize,
)]
#[netabase(BankDefinition)]
pub struct Transaction {
#[primary_key]
pub tx_id: String,
pub from_account: u64,
pub to_account: u64,
pub amount: f64,
pub timestamp: u64,
}
}
use models::*;
fn main() -> anyhow::Result<()> {
println!("=== Batch Atomicity Example ===\n");
let store = NetabaseStore::<BankDefinition, _>::temp()?;
let account_tree = store.open_tree::<Account>();
let tx_tree = store.open_tree::<Transaction>();
println!("Example 1: Successful Atomic Transfer");
println!("--------------------------------------");
setup_accounts(&account_tree)?;
atomic_transfer(&account_tree, &tx_tree, 1, 2, 100.0)?;
println!("\nExample 2: Multiple Account Updates");
println!("------------------------------------");
batch_update_multiple_accounts(&account_tree)?;
println!("\nExample 3: Conditional Batch Processing");
println!("----------------------------------------");
conditional_batch(&account_tree)?;
println!("\n✅ All atomicity examples completed successfully!");
Ok(())
}
fn setup_accounts(
account_tree: &netabase_store::databases::sled_store::SledStoreTree<
'_,
BankDefinition,
Account,
>,
) -> anyhow::Result<()> {
let mut batch = account_tree.create_batch()?;
let accounts = vec![
Account {
account_id: 1,
owner: "Alice".to_string(),
balance: 1000.0,
account_type: "Checking".to_string(),
},
Account {
account_id: 2,
owner: "Bob".to_string(),
balance: 500.0,
account_type: "Checking".to_string(),
},
Account {
account_id: 3,
owner: "Charlie".to_string(),
balance: 2000.0,
account_type: "Savings".to_string(),
},
];
for account in accounts {
batch.put(account)?;
}
batch.commit()?;
println!("✓ Created 3 accounts");
Ok(())
}
fn atomic_transfer(
account_tree: &netabase_store::databases::sled_store::SledStoreTree<
'_,
BankDefinition,
Account,
>,
tx_tree: &netabase_store::databases::sled_store::SledStoreTree<'_, BankDefinition, Transaction>,
from_id: u64,
to_id: u64,
amount: f64,
) -> anyhow::Result<()> {
println!(
"Transferring ${:.2} from account {} to account {}",
amount, from_id, to_id
);
let from_account = account_tree
.get(AccountPrimaryKey(from_id))?
.ok_or_else(|| anyhow::anyhow!("Source account not found"))?;
let to_account = account_tree
.get(AccountPrimaryKey(to_id))?
.ok_or_else(|| anyhow::anyhow!("Destination account not found"))?;
println!(
" Before: {} has ${:.2}, {} has ${:.2}",
from_account.owner, from_account.balance, to_account.owner, to_account.balance
);
if from_account.balance < amount {
anyhow::bail!("Insufficient funds");
}
let mut account_batch = account_tree.create_batch()?;
let mut updated_from = from_account.clone();
updated_from.balance -= amount;
account_batch.put(updated_from)?;
let mut updated_to = to_account.clone();
updated_to.balance += amount;
account_batch.put(updated_to)?;
account_batch.commit()?;
let tx = Transaction {
tx_id: format!(
"TX-{}-{}-{}",
from_id,
to_id,
chrono::Utc::now().timestamp()
),
from_account: from_id,
to_account: to_id,
amount,
timestamp: chrono::Utc::now().timestamp() as u64,
};
tx_tree.put(tx)?;
let from_after = account_tree.get(AccountPrimaryKey(from_id))?.unwrap();
let to_after = account_tree.get(AccountPrimaryKey(to_id))?.unwrap();
println!(
" After: {} has ${:.2}, {} has ${:.2}",
from_after.owner, from_after.balance, to_after.owner, to_after.balance
);
println!("✓ Transfer completed atomically");
Ok(())
}
fn batch_update_multiple_accounts(
account_tree: &netabase_store::databases::sled_store::SledStoreTree<
'_,
BankDefinition,
Account,
>,
) -> anyhow::Result<()> {
println!("Applying interest to all savings accounts...");
let savings_accounts = account_tree.get_by_secondary_key(AccountSecondaryKeys::AccountType(
AccountAccountTypeSecondaryKey("Savings".to_string()),
))?;
println!("Found {} savings accounts", savings_accounts.len());
let mut batch = account_tree.create_batch()?;
for mut account in savings_accounts.clone() {
let interest = account.balance * 0.05; account.balance += interest;
println!(" {} +${:.2} interest", account.owner, interest);
batch.put(account)?;
}
batch.commit()?;
println!("✓ Applied interest to all savings accounts atomically");
for account in savings_accounts {
let updated = account_tree
.get(AccountPrimaryKey(account.account_id))?
.unwrap();
println!(" {} now has ${:.2}", updated.owner, updated.balance);
}
Ok(())
}
fn conditional_batch(
account_tree: &netabase_store::databases::sled_store::SledStoreTree<
'_,
BankDefinition,
Account,
>,
) -> anyhow::Result<()> {
println!("Processing fee deductions (only if all accounts have sufficient balance)...");
const FEE: f64 = 50.0;
let checking_accounts = account_tree.get_by_secondary_key(
AccountSecondaryKeys::AccountType(AccountAccountTypeSecondaryKey("Checking".to_string())),
)?;
let mut can_process = true;
for account in &checking_accounts {
if account.balance < FEE {
println!(
" ⚠️ {} only has ${:.2}, cannot deduct fee",
account.owner, account.balance
);
can_process = false;
}
}
if !can_process {
println!("✗ Skipping fee deduction - not all accounts have sufficient balance");
println!(" (This demonstrates validation before batch operations)");
return Ok(());
}
let mut batch = account_tree.create_batch()?;
for mut account in checking_accounts {
println!(" Deducting ${:.2} from {}", FEE, account.owner);
account.balance -= FEE;
batch.put(account)?;
}
batch.commit()?;
println!("✓ Deducted fees from all checking accounts atomically");
Ok(())
}