레퍼런스
Turso의 Rust 레퍼런스예요. crate 세 개가 각각 어떤 상황에 맞는지, 연결·쿼리·트랜잭션·동기화·암호화 API를 하나씩 살펴볼게요.
출처: 문서
본문
Turso는 세 가지 Rust crate를 제공해요:
turso |
turso_serverless |
libsql |
|
|---|---|---|---|
| Use case | 로컬/임베디드 데이터베이스, sync | 원격 Turso 데이터베이스 (over-the-wire) | 원격 libSQL 데이터베이스, 기존 libSQL 코드베이스 |
| Engine | Turso (rewrite) | Turso (rewrite) | libSQL (SQLite fork) |
| Concurrent writes | Yes (MVCC) | Yes (MVCC) | Not supported |
| Sync | push/pull (local-first) | — | Embedded Replicas (쓰기는 클라우드 primary로) |
| C compiler | 불필요 | 불필요 | core, replication, encryption 피처에 필요 |
새 프로젝트를 시작하나요? 로컬/임베디드 용도나 sync에는 turso를 쓰세요. 원격 접근에는 엔진에 맞는 crate를 고르면 돼요: Turso 데이터베이스에는 turso_serverless, libSQL 데이터베이스에는 remote 피처를 켠 libsql(C 컴파일러 불필요)이요.
turso
로컬·임베디드 용도예요. 동시 쓰기(MVCC)와 비동기 I/O를 지원하는 Turso Database 엔진 기반이에요.
Installing
cargo add turso tokio --features tokio/full
Connecting
use turso::Builder;
let db = Builder::new_local("app.db").build().await?;
let conn = db.connect()?;
인메모리 데이터베이스도 지원해요:
let db = Builder::new_local(":memory:").build().await?;
Querying
conn.execute(
"CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
)",
(),
).await?;
conn.execute("INSERT INTO users (name) VALUES (?)", ("Alice",)).await?;
let mut rows = conn.query("SELECT * FROM users", ()).await?;
while let Some(row) = rows.next().await? {
let id: i64 = row.get(0)?;
let name: String = row.get(1)?;
println!("User: {} {}", id, name);
}
Prepared Statements
let mut stmt = conn.prepare("SELECT * FROM users WHERE id = ?1").await?;
let mut rows = stmt.query([42]).await?;
Transactions
let tx = conn.transaction().await?;
tx.execute("INSERT INTO users (name) VALUES (?1)", ["Alice"]).await?;
tx.execute("INSERT INTO users (name) VALUES (?1)", ["Bob"]).await?;
tx.commit().await?;
Encryption
로컬 데이터베이스를 저장 시점(at rest)에 암호화할 수 있어요:
use turso::{Builder, EncryptionOpts};
let db = Builder::new_local("encrypted.db")
.experimental_encryption(true)
.with_encryption(EncryptionOpts {
cipher: "aegis256".to_string(),
hexkey: "b1bbfda4f589dc9daaf004fe21111e00dc00c98237102f5c7002a5669fc76327".to_string(),
})
.build()
.await?;
지원 암호: aegis256, aegis256x2, aegis128l, aegis128x2, aegis128x4, aes256gcm, aes128gcm.
암호화된 데이터베이스는 표준 SQLite 데이터베이스로는 읽을 수 없어요 — 반드시 Turso Database 엔진으로 열어야 해요.
참고로, Turso Cloud 데이터베이스도 bring-your-own-key로 암호화할 수 있어요 — 자세히 보기.
Sync (Push and Pull)
클라우드 sync가 있는 로컬 데이터베이스용이에요. 모든 읽기와 쓰기는 로컬에서 일어나고, push()로 변경을 클라우드에 보내고 pull()로 원격 변경을 가져와요.
sync 피처를 활성화하세요:
cargo add turso --features sync
use turso::sync::Builder;
let db = Builder::new_remote("app.db")
.with_remote_url(&std::env::var("TURSO_DATABASE_URL")?)
.with_auth_token(&std::env::var("TURSO_AUTH_TOKEN")?)
.bootstrap_if_empty(true) // Download schema on first sync (default)
.build()
.await?;
let conn = db.connect().await?;
첫 실행 때 로컬 데이터베이스가 원격에서 자동으로 bootstrap돼요. 전체 내용은 Turso Sync에서 확인하세요.
Push and Pull
// Push local writes to Turso Cloud
db.push().await?;
// Pull remote changes (returns true if changes were applied)
let changed = db.pull().await?;
Checkpoint
sync 상태를 유지하면서 로컬 WAL을 압축해 디스크 사용량을 제한해요:
db.checkpoint().await?;
Stats
let stats = db.stats().await?;
println!("Network received: {} bytes", stats.network_received_bytes);
println!("Network sent: {} bytes", stats.network_sent_bytes);
println!("WAL size: {} bytes", stats.main_wal_size);
libsql (Remote)
remote 피처를 켠 libsql crate로 Turso Cloud에 over-the-wire 접근할 수 있어요. 순수 Rust HTTP를 사용하고, C 컴파일러가 필요 없어요.
Installing
cargo add libsql --features remote
Connecting
use libsql::Builder;
let url = std::env::var("TURSO_DATABASE_URL")?;
let token = std::env::var("TURSO_AUTH_TOKEN")?;
let db = Builder::new_remote(url, token).build().await?;
let conn = db.connect()?;
Querying
let mut rows = conn.query("SELECT * FROM users WHERE id = ?1", [1]).await?;
libsql (libSQL)
libsql crate는 오늘날 Turso Cloud를 구동하는 SQLite 오픈소스 포크인 libSQL 기반이에요. 프로덕션에서 검증이 끝났고, 기존 libsql 기반 코드베이스를 다룰 때 맞는 선택이에요.
참고로, libsql Embedded Replicas에서는 읽기가 로컬에서 일어나고 쓰기는 클라우드 primary로 전송된 뒤 복제본에 반영돼요. Embedded Replicas는 완전히 지원돼요. sync가 필요한 새 프로젝트에는 turso crate의 turso::sync를 추천해요 — 퀵스타트를 보세요.
Embedded Replicas
Turso Cloud 데이터베이스에서 로컬 SQLite 파일로 sync되는 Embedded Replicas를 쓸 수 있어요. 읽기는 로컬로 실행되고, 쓰기는 클라우드 primary로 전송된 후 복제본에 반영돼요:
use libsql::Builder;
let url = std::env::var("TURSO_DATABASE_URL")?;
let token = std::env::var("TURSO_AUTH_TOKEN")?;
let db = Builder::new_remote_replica("local.db", url, token)
.build()
.await?;
let conn = db.connect()?;
Manual Sync
db.sync().await?;
Sync Interval
use std::time::Duration;
let db = Builder::new_remote_replica("local.db", url, token)
.sync_interval(Duration::from_secs(300))
.build()
.await?;
Read Your Own Writes
기본적으로 push() 이후의 다음 pull()은 서버가 내 변경을 완전히 따라잡을 때까지 기다렸다가 반환해요 — 항상 자기 쓰기를 읽는 것을 보장하죠. 더 안전하지만 훨씬 느려요, 서버가 모든 대기 중 변경을 처리해야 하거든요. eventual consistency 읽기를 견딜 수 있다면 끄면 pull이 훨씬 빨라져요:
let db = Builder::new_remote_replica("local.db", url, token)
.read_your_writes(false)
.build()
.await?;
Encryption
새 프로젝트에서는 로컬 암호화에 turso crate를 추천해요 — Turso Database 엔진 기반이라 성능이 좋고 동시 쓰기를 지원하거든요.
SQLite 파일에 암호화를 켜려면, 생성자에 암호화 키 값을 인자로 넘기면 돼요.
참고로, 암호화된 데이터베이스는 원시 데이터로 보이고 표준 SQLite 데이터베이스로는 읽을 수 없어요. 모든 작업에 libSQL 클라이언트를 써야 해요 — 자세히 보기.
Conditional compilation
libsql crate는 조건부 컴파일 피처를 지원해요:
| Feature | Description |
|---|---|
remote |
HTTP 전용 클라이언트, 순수 Rust. C 컴파일러 불필요. |
core |
로컬 데이터베이스 전용. C 컴파일러 필요. |
replication |
core에 임베디드 복제본 지원을 합침. C 컴파일러 필요. |
encryption |
저장 시 암호화. cmake 필요. 기본 비활성화. |
Simple query
conn.execute("SELECT * FROM users", ()).await?;
conn.execute("SELECT * FROM users WHERE id = ?1", [1]).await?;
Placeholders
Positional
conn.execute("SELECT * FROM users WHERE id = ?1", libsql::params![1]).await?;
Named
conn.execute("INSERT INTO users (name) VALUES (:name)", libsql::named_params! { ":name": "Iku" }).await?;
Deserialization
use libsql::{de, Builder};
#[derive(Debug, serde::Deserialize)]
struct User {
name: String,
age: i64,
}
let mut stmt = conn.prepare("SELECT * FROM users WHERE id = ?1").await?;
let row = stmt.query([1]).await?.next().await?.unwrap();
let user = de::from_row::<User>(&row)?;
Batch Transactions
conn.execute_batch(r#"
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
INSERT INTO users (name) VALUES ('Alice');
INSERT INTO users (name) VALUES ('Bob');
"#).await?;
Interactive Transactions
Default
let tx = conn.transaction().await?;
tx.execute("INSERT INTO users (name) VALUES (?1)", ["Alice"]).await?;
tx.execute("INSERT INTO users (name) VALUES (?1)", ["Bob"]).await?;
tx.commit().await?;
Advanced control
use libsql::TransactionBehavior;
let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).await?;
tx.execute("UPDATE users SET age = age + 1 WHERE id = ?1", [1]).await?;
tx.commit().await?;
더 알아보기 (Learn more)
- Rust 퀵스타트 — crate 선택부터 sync까지 빠르게 시작
- Toasty + Turso — Rust ORM으로 모델 정의하기
- Embedded Replicas — 로컬 읽기 복제본 개념 정리
- Turso Sync 사용법 — push/pull과 충돌 해결 상세