Using Embedded SQLite from Rust
Use the same patched rusqlite and lock-free WASI VFS that back node:sqlite in the TypeScript
runtime. The crates.io release alone is not sufficient for this configuration.
Add the dependency
Add this to the component’s Cargo.toml:
[dependencies]
rusqlite = { version = "0.38.0", default-features = false, features = ["bundled", "wasm32-wasi-vfs"] }
[patch.crates-io]
rusqlite = { git = "https://github.com/golemcloud/rusqlite", rev = "40024c3f7a2e1dfc2b36aac33e3d84ee04bfb700" }
libsqlite3-sys = { git = "https://github.com/golemcloud/rusqlite", rev = "40024c3f7a2e1dfc2b36aac33e3d84ee04bfb700" }Add the optional functions, session, backup, or serialize features only when the agent uses
those APIs. Build with golem build --yes, which targets wasm32-wasip2.
File-backed agent database
use golem_rust::{agent_definition, agent_implementation};
use rusqlite::{params, Connection};
#[agent_definition]
pub trait NotesAgent {
fn new(name: String) -> Self;
fn add(&mut self, text: String) -> u64;
fn list(&self) -> Vec<String>;
}
struct NotesAgentImpl {
connection: Connection,
}
#[agent_implementation]
impl NotesAgent for NotesAgentImpl {
fn new(_name: String) -> Self {
std::fs::create_dir_all("/data").expect("failed to create database directory");
let connection = Connection::open("/data/notes.db").expect("failed to open SQLite");
connection
.execute_batch("PRAGMA temp_store=MEMORY;")
.expect("failed to configure temporary storage");
connection
.execute(
"CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, text TEXT NOT NULL)",
(),
)
.expect("failed to create notes table");
Self { connection }
}
fn add(&mut self, text: String) -> u64 {
self.connection
.execute("INSERT INTO notes (text) VALUES (?1)", params![text])
.expect("failed to insert note");
self.connection
.query_row("SELECT count(*) FROM notes", (), |row| row.get::<_, i64>(0))
.expect("failed to count notes")
.try_into()
.expect("note count cannot be represented as u64")
}
fn list(&self) -> Vec<String> {
let mut statement = self
.connection
.prepare("SELECT text FROM notes ORDER BY id")
.expect("failed to prepare query");
statement
.query_map((), |row| row.get(0))
.expect("failed to query notes")
.map(|row| row.expect("failed to decode note"))
.collect()
}
}Use Connection::open_in_memory() when the database should live in linear memory. Rust snapshot
handling does not automatically serialize a Connection; explicitly serialize and restore the
database with the serialize feature when implementing custom snapshots. For file-backed databases,
enable executor filesystem snapshots for snapshot-based manual updates. Golem captures and restores
the files before load_snapshot; do not copy database files into the memory snapshot or write them
from the read-only restoration hook. Without filesystem snapshots, changed files prevent a
snapshot-based manual update. See golem-custom-snapshot-rust for executor setup and restoration rules.
Resource and runtime limits
- In-memory databases, temporary tables, query results, and SQLite caches consume WebAssembly linear memory and count toward the account’s per-agent memory limit.
- Database files, journals, and other SQLite files count toward the per-agent filesystem limit.
Inspect the effective values with
golem account limits show; do not assume a fixed cloud limit. - The database belongs to one agent. Keep exactly one connection open per file: the WASI VFS has no file locking and is compiled without thread safety.
- The VFS has no temporary-file support. Keep temporary storage in memory.
- Do not use
journal_mode=TRUNCATE; file truncation is not implemented. Avoidjournal_mode=PERSISTwith attached databases. - Native loadable extensions are unavailable, and Unix-style paths are limited to 512 bytes.
Use an external relational database when data must be shared or queried across agents, exceed one agent’s memory or disk allowance, or be managed independently of the agent lifecycle.