Skip to content

Commit e68e4c4

Browse files
authored
Merge pull request #29 from orbitinghail:carlsverre/issue27
Make it possible for reducers to handle errors
2 parents 3c8147c + 2e02e8f commit e68e4c4

File tree

25 files changed

+293
-158
lines changed

25 files changed

+293
-158
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
This changelog documents changes across multiple projects contained in this monorepo. Each project is released for every SQLSync version, even if the project has not changed. The reason for this decision is to simplify testing and debugging. Lockstep versioning will be relaxed as SQLSync matures.
2+
3+
# 0.2.0 - Dec 1 2023
4+
5+
- Reducer can now handle query errors (#29)
6+
7+
# 0.1.0 - Oct 23 2023
8+
9+
- Initial release

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ members = [
1717
authors = ["Carl Sverre", "orbitinghail"]
1818
edition = "2021"
1919
homepage = "https://sqlsync.dev"
20-
license-file = "LICENSE"
20+
license = "Apache-2.0"
2121
repository = "https://github.com/orbitinghail/sqlsync"
22+
version = "0.2.0"
2223

2324
[profile.release]
2425
lto = true

GUIDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ async fn reducer(mutation: Vec<u8>) -> Result<(), ReducerError> {
7979
msg TEXT NOT NULL,
8080
created_at TEXT NOT NULL
8181
)"
82-
).await;
82+
).await?;
8383
}
8484

8585
Mutation::AddMessage { id, msg } => {
@@ -88,7 +88,7 @@ async fn reducer(mutation: Vec<u8>) -> Result<(), ReducerError> {
8888
"insert into messages (id, msg, created_at)
8989
values (?, ?, datetime('now'))",
9090
id, msg
91-
).await;
91+
).await?;
9292
}
9393
}
9494

demo/demo-reducer/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ async fn reducer(mutation: Vec<u8>) -> Result<(), ReducerError> {
2929
created_at TEXT NOT NULL
3030
)"
3131
)
32-
.await;
32+
.await?;
3333
}
3434

3535
Mutation::CreateTask { id, description } => {
@@ -40,19 +40,19 @@ async fn reducer(mutation: Vec<u8>) -> Result<(), ReducerError> {
4040
id,
4141
description
4242
)
43-
.await;
43+
.await?;
4444
}
4545

4646
Mutation::DeleteTask { id } => {
47-
execute!("delete from tasks where id = ?", id).await;
47+
execute!("delete from tasks where id = ?", id).await?;
4848
}
4949

5050
Mutation::ToggleCompleted { id } => {
5151
execute!(
5252
"update tasks set completed = not completed where id = ?",
5353
id
5454
)
55-
.await;
55+
.await?;
5656
}
5757
}
5858

demo/frontend/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
<meta charset="UTF-8" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<title>SQLSync Demo</title>
8+
9+
<link rel="icon" type="image/png" href="/favicon-dark-32x32.png" media="(prefers-color-scheme: light)" />
10+
<link rel="icon" type="image/png" href="/favicon-light-32x32.png" media="(prefers-color-scheme: dark)" />
11+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
12+
813
<script src="https://cdn.usefathom.com/script.js" data-site="LWITZJZR" async></script>
914
</head>
1015

693 Bytes
Loading
638 Bytes
Loading

demo/frontend/public/favicon.svg

Lines changed: 10 additions & 0 deletions
Loading

demo/frontend/public/robots.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
User-agent: *
2+
Disallow: /

lib/sqlsync-react/package.json

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,15 @@
11
{
22
"name": "@orbitinghail/sqlsync-react",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "SQLSync is a collaborative offline-first wrapper around SQLite. It is designed to synchronize web application state between users, devices, and the edge.",
55
"homepage": "https://sqlsync.dev",
66
"license": "Apache-2.0",
7-
"keywords": [
8-
"sqlsync",
9-
"sql",
10-
"database",
11-
"sqlite",
12-
"offline-first",
13-
"local-first"
14-
],
7+
"keywords": ["sqlsync", "sql", "database", "sqlite", "offline-first", "local-first"],
158
"repository": {
169
"type": "git",
1710
"url": "https://github.com/orbitinghail/sqlsync"
1811
},
19-
"files": [
20-
"dist",
21-
"src"
22-
],
12+
"files": ["dist", "src"],
2313
"type": "module",
2414
"main": "./dist/sqlsync-react.js",
2515
"types": "./src/index.ts",
@@ -55,4 +45,4 @@
5545
"fast-equals": "^5.0.1",
5646
"fast-sha256": "^1.3.0"
5747
}
58-
}
48+
}

lib/sqlsync-react/sqlsync-react-test-reducer/src/lib.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@ async fn reducer(mutation: Vec<u8>) -> Result<(), ReducerError> {
1616

1717
match mutation {
1818
Mutation::InitSchema => {
19-
futures::join!(
20-
execute!(
21-
"CREATE TABLE IF NOT EXISTS counter (
19+
let create_table = execute!(
20+
"CREATE TABLE IF NOT EXISTS counter (
2221
id INTEGER PRIMARY KEY,
2322
value INTEGER
2423
)"
25-
),
26-
execute!("INSERT OR IGNORE INTO counter (id, value) VALUES (0, 0)")
2724
);
25+
let init_counter = execute!(
26+
"INSERT OR IGNORE INTO counter (id, value) VALUES (0, 0)"
27+
);
28+
29+
create_table.await?;
30+
init_counter.await?;
2831
}
2932

3033
Mutation::Incr { value } => {
@@ -33,7 +36,7 @@ async fn reducer(mutation: Vec<u8>) -> Result<(), ReducerError> {
3336
ON CONFLICT (id) DO UPDATE SET value = value + ?",
3437
value
3538
)
36-
.await;
39+
.await?;
3740
}
3841

3942
Mutation::Decr { value } => {
@@ -42,7 +45,7 @@ async fn reducer(mutation: Vec<u8>) -> Result<(), ReducerError> {
4245
ON CONFLICT (id) DO UPDATE SET value = value - ?",
4346
value
4447
)
45-
.await;
48+
.await?;
4649
}
4750
}
4851

lib/sqlsync-reducer/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
[package]
22
name = "sqlsync-reducer"
33
resolver = "2"
4-
version = "0.1.0"
54
description = "SQLSync is a collaborative offline-first wrapper around SQLite. A SQLSync Reducer is responsible for executing mutations on the database both locally and on the server."
65

6+
version.workspace = true
77
authors.workspace = true
88
edition.workspace = true
99
homepage.workspace = true
10-
license-file.workspace = true
10+
license.workspace = true
1111
repository.workspace = true
1212

1313
[dependencies]
1414
serde = { workspace = true, features = ["derive"] }
1515
bincode.workspace = true
1616
futures.workspace = true
1717
log.workspace = true
18+
thiserror.workspace = true
1819

1920
wasmi = { workspace = true, optional = true }
20-
thiserror = { workspace = true, optional = true }
2121

2222
[features]
2323
default = ["guest"]
24-
host = ["wasmi", "thiserror"]
24+
host = ["wasmi"]
2525
guest = []
2626

2727
[dev-dependencies]

lib/sqlsync-reducer/examples/guest.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ async fn reducer(mutation: Vec<u8>) -> Result<(), ReducerError> {
1717
log::info!("running query and execute at the same time");
1818

1919
let x: Option<i64> = None;
20-
let query_future = query!("SELECT * FROM foo WHERE bar = ?", "baz", 1, 1.23, x);
20+
let query_future =
21+
query!("SELECT * FROM foo WHERE bar = ?", "baz", 1, 1.23, x);
2122
let exec_future = execute!("SELECT * FROM foo WHERE bar = ?", "baz");
2223

2324
let (result, result2) = futures::join!(query_future, exec_future);
@@ -35,6 +36,17 @@ async fn reducer(mutation: Vec<u8>) -> Result<(), ReducerError> {
3536
let result = query_future.await;
3637
log::info!("final query result: {:?}", result);
3738

39+
log::info!("testing errors");
40+
if let Err(err) = execute!("FAIL").await {
41+
log::error!("error: {:?}", err);
42+
} else {
43+
panic!("expected error");
44+
}
45+
46+
log::info!("make sure we can resume queries after an error");
47+
let result = execute!("SELECT * FROM foo WHERE bar = ?", "baz").await;
48+
log::info!("result: {:?}", result);
49+
3850
Ok(())
3951
}
4052

lib/sqlsync-reducer/examples/host.rs

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::collections::BTreeMap;
55
use serde::{Deserialize, Serialize};
66
use sqlsync_reducer::{
77
host_ffi::{register_log_handler, WasmFFI},
8-
types::{ExecResponse, QueryResponse, Request},
8+
types::{ErrorResponse, ExecResponse, QueryResponse, Request},
99
};
1010
use wasmi::{Engine, Linker, Module, Store};
1111

@@ -22,8 +22,9 @@ fn main() -> anyhow::Result<()> {
2222
.init()?;
2323

2424
// build guest.wasm using: `cargo build --target wasm32-unknown-unknown --example guest`
25-
let wasm_bytes =
26-
include_bytes!("../../../target/wasm32-unknown-unknown/debug/examples/guest.wasm");
25+
let wasm_bytes = include_bytes!(
26+
"../../../target/wasm32-unknown-unknown/debug/examples/guest.wasm"
27+
);
2728

2829
let engine = Engine::default();
2930
let module = Module::new(&engine, &wasm_bytes[..])?;
@@ -32,7 +33,8 @@ fn main() -> anyhow::Result<()> {
3233
register_log_handler(&mut linker)?;
3334

3435
let mut store = Store::new(&engine, WasmFFI::uninitialized());
35-
let instance = linker.instantiate(&mut store, &module)?.start(&mut store)?;
36+
let instance =
37+
linker.instantiate(&mut store, &module)?.start(&mut store)?;
3638

3739
// initialize the FFI
3840
let ffi = WasmFFI::initialized(&store, &instance)?;
@@ -52,21 +54,39 @@ fn main() -> anyhow::Result<()> {
5254
let mut responses = BTreeMap::new();
5355
for (id, req) in requests_inner {
5456
match req {
55-
Request::Query { .. } => {
56-
log::info!("received query request: {:?}", req);
57+
Request::Query { sql, params } => {
58+
log::info!("received query request: {} {:?}", sql, params);
5759
let ptr = ffi.encode(
5860
&mut store,
59-
&QueryResponse {
61+
&Ok::<_, ErrorResponse>(QueryResponse {
6062
columns: vec!["foo".into(), "bar".into()],
6163
rows: vec![vec!["baz".into(), "qux".into()].into()],
62-
},
64+
}),
6365
)?;
6466
responses.insert(id, ptr);
6567
}
66-
Request::Exec { .. } => {
67-
log::info!("received exec request: {:?}", req);
68-
let ptr = ffi.encode(&mut store, &ExecResponse { changes: 1 })?;
69-
responses.insert(id, ptr);
68+
Request::Exec { sql, params } => {
69+
log::info!("received exec request: {} {:?}", sql, params);
70+
if sql == "FAIL" {
71+
let ptr = ffi.encode(
72+
&mut store,
73+
&Err::<ExecResponse, _>(
74+
ErrorResponse::SqliteError {
75+
code: 1,
76+
message: "error".to_string(),
77+
},
78+
),
79+
)?;
80+
responses.insert(id, ptr);
81+
} else {
82+
let ptr = ffi.encode(
83+
&mut store,
84+
&Ok::<_, ErrorResponse>(ExecResponse {
85+
changes: 1,
86+
}),
87+
)?;
88+
responses.insert(id, ptr);
89+
}
7090
}
7191
}
7292
}

0 commit comments

Comments
 (0)