simple/
simple.rs

1use std::sync::Arc;
2
3use atomo::{AtomoBuilder, DefaultSerdeBackend};
4
5fn main() {
6    type Key = u64;
7    type Value = u64;
8
9    // Create a database with the provided configuration and setup.
10    // We should provide the different tables using the `with_table` method
11    // and provide the type for the key and value pair in that table.
12    //
13    // It is important to always use the same type for accessing the key and value on
14    // a table since Atomo perform runtime type confirmation to ensure that the same
15    // rust type is used on the table when accessing the table.
16    //
17    // Once we're done with providing the tables we call `build` to build and
18    // open the database.
19    //
20    // This ensure that only one update is happening at any given time.
21    let mut db = AtomoBuilder::<DefaultSerdeBackend>::new()
22        .with_table::<Key, Value>("name-of-table")
23        .build();
24
25    // Build returns an `Atomo<UpdatePerm, _>` which allows us to mutate the data
26    // there can only ever be one Atomo instance with update permission.
27    //
28    // Through Rust's borrow check logic we ensure that by explicitly making an
29    // `Atomo<UpdatePerm, _>: ~Clone`.
30    //
31    // But we can have as many `Atomo<QueryPerm, _>` as we want.
32    //
33    // Here we get access to a query runner instance.
34    let query_runner = db.query();
35
36    // It is generally a good idea to cache the table resolution. This removes the need to perform
37    // a table lookup and type check when accessing the table while running query/mutations.
38    //
39    // And since the type check happens during instantiating, you can avoid possible type
40    // mismatches and therefore is safer to do so.
41    let table_res = db.resolve::<Key, Value>("name-of-table");
42
43    // Insert `(0, 17)` to the table.
44    db.run(|ctx: &mut atomo::TableSelector<atomo::BincodeSerde>| {
45        let mut table_ref = table_res.get(ctx);
46        // Or if we didn't have a `ResolvedTableReference`.
47        // let mut table_ref = ctx.get_table::<Key, Value>("name-of-table");
48        
49        table_ref.insert(0, 17);
50    });
51
52    // Here we demonstrate the consistent views that Atomo provides:
53    //
54    // We create a thread that will be responsible for running a query. The current thread will try
55    // to update a value in the middle of the execution of the query.
56    //
57    // We use two [`std::sync::Barrier`]s for synchronization.
58    //
59    // This is the linear log of the execution that we want to make happen:
60    //
61    // [Query Thread]: Enter the `run` closure. And print the value for key=0.
62    // [Main Thread]:  Insert (key=0, value=12) to the table.
63    // [Query Thread]: Read the value for key=0 again.
64    //
65    // The value when read the second time MUST equal to the initial read (17) since
66    // the query is still running on the same snapshot of data.
67
68    let barrier_1 = Arc::new(std::sync::Barrier::new(2));
69    let barrier_2 = Arc::new(std::sync::Barrier::new(2));
70
71    let c_1 = barrier_1.clone();
72    let c_2 = barrier_2.clone();
73    let handle = std::thread::spawn(move || {
74        query_runner.run(|ctx: _| {
75            let table_ref = table_res.get(ctx);
76            println!("Query Started [get(0) == {:?}]", table_ref.get(0));
77
78            // Allow the main thread to continue.
79            c_1.wait();
80
81            // Waiting until the main thread updates the data.
82            c_2.wait();
83
84            println!("Query Finishing [get(0) == {:?}]", table_ref.get(0));
85        });
86
87        // Run a second query this should get the new data.
88        query_runner.run(|ctx: _| {
89            let table_ref = table_res.get(ctx);
90            println!("2nd Query Got [get(0) == {:?}]", table_ref.get(0));
91        });
92    });
93
94    // Wait for the query thread to 'start' running the query.
95    barrier_1.wait();
96
97    println!("Starting the update");
98    db.run(|ctx: _| {
99        let mut table_ref = table_res.get(ctx);
100
101        // Update the value and then allow the 
102        table_ref.insert(0, 12);
103        println!("Value updated to 12");
104    });
105
106    // Allow the query thread to continue.
107    barrier_2.wait();
108
109    // Wait for the query thread to finish executing.
110    let _ = handle.join();
111}