Repository navigation
Expand file tree
/
Copy path09_durable_object.rs
More file actions
76 lines (65 loc) · 2.91 KB
/
Copy path09_durable_object.rs
File metadata and controls
76 lines (65 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/// Example: Durable Objects
///
/// A durable object is a chDB database whose authoritative state lives in
/// storage you own — a full checkpoint plus a statement WAL under a leased
/// `head.json`, in the layout every chDB binding shares. This example writes
/// one, loses the handle, and recovers the database from storage alone.
///
/// Run it with the feature on:
///
/// cargo run --features durable --example 09_durable_object
use chdb_rust::durable::{Namespace, OpenOptions};
use chdb_rust::format::OutputFormat;
fn main() -> Result<(), chdb_rust::durable::Error> {
println!("=== Durable Object Examples ===\n");
// One directory per namespace, one subdirectory per object. A cloud backend
// is what makes an object recoverable on *another* machine; a directory is
// for development and for seeing the layout.
let root = std::env::temp_dir().join("chdb-durable-example");
let namespace =
Namespace::new(root.to_str().expect("a UTF-8 temp dir"))?.with_owner("example-writer");
println!("1. Opening the object (creating it if this is the first run)...");
let (object, existed) = namespace.open(
"tenant-123",
OpenOptions {
database: Some("mem".to_string()),
..OpenOptions::default()
},
)?;
println!(
" {} at {}",
if existed { "restored" } else { "created" },
root.display()
);
if !existed {
object.execute(
"CREATE TABLE events (id UInt64, tag String) ENGINE = MergeTree ORDER BY id",
)?;
}
println!("2. Writing...");
// Log literals, never now() or rand(): recovery re-executes the statement,
// and V1 promises ordered replay, not the same answer twice.
let ticket = object.execute("INSERT INTO events VALUES (1, 'first')")?;
// execute() ran the statement here. flush_through() is what makes it
// survive losing this machine — wait for it before answering anyone.
println!("3. Flushing, which is the durability barrier...");
object.flush_through(ticket)?;
println!("4. Folding the WAL into a fresh checkpoint...");
let base = object.checkpoint()?;
println!(" base is now {}", base.key);
let stats = object.stats();
println!(
" generation {}, seq {}, {} statement(s) committed",
stats.generation, stats.committed_seq, stats.committed_statements
);
// close() drains, flushes and releases the lease. Dropping the handle
// instead reclaims local resources but leaves the lease to expire.
object.close()?;
println!("\n5. Reopening from storage alone...");
let (object, _) = namespace.open("tenant-123", OpenOptions::default())?;
let rows = object.query("SELECT id, tag FROM events ORDER BY id", OutputFormat::CSV)?;
println!("{}", String::from_utf8_lossy(&rows));
object.close()?;
println!("Run it again: the row count grows, and each run recovers the last.");
Ok(())
}