Move sync logic to rust and update webapp to show status in sidebar#2202
Move sync logic to rust and update webapp to show status in sidebar#2202
Conversation
WalkthroughIntroduces WASM-specific timing and status payloads for local DB sync, adds periodic sync loop in the Rust client, switches webapp to a one-shot sync initiation with new status stores and UI, updates types to use WasmEncodedResult, and adds related Svelte components and tests. Also adds WASM dependencies. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant WebApp as WebApp (Svelte)
participant Stores as localDbStatus stores
participant Client as RaindexClient (WASM)
Note over WebApp,Client: One-shot sync initiation from webapp
User->>WebApp: App loads
WebApp->>WebApp: startLocalDbSync({ raindexClient, localDb })
WebApp->>Client: register DbCallback / call sync_database_once(...)
Client-->>WebApp: WasmEncodedResult<String>
alt success
WebApp->>Stores: recordLocalDbStatus(message)
else error
WebApp->>Stores: recordLocalDbError(readableMsg ?? msg)
end
Stores-->>WebApp: localDbStatusIndicator (derived)
WebApp-->>User: Sidebar shows badge/latest entry
sequenceDiagram
autonumber
participant JS as JS Host (WASM)
participant Client as RaindexClient
participant Timer as wait_for_interval
Note over Client: New WASM periodic sync loop
JS->>Client: sync_database(optional status cb)
loop every SYNC_INTERVAL_MS (WASM)
Client->>Client: run single sync
alt success
Client->>JS: send_status_message(Ok(value))
else error
Client->>JS: send_status_message(Err{msg, readableMsg})
end
Client->>Timer: wait_for_interval()
Timer-->>Client: resume
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
crates/common/src/raindex_client/local_db/sync.rs (2)
300-418: Don’t fail sync if the UI status callback throws; make status sends best‑effort.Propagating StatusSink::send errors with
?aborts the sync early when the JS callback misbehaves, then tries to report that error via the same callback. Treat status as auxiliary.Suggested change:
- status.send(success_status("Starting database sync..."))?; + let _ = status.send(success_status("Starting database sync...")); @@ - status.send(success_status(format!("has tables: {}", has_tables)))?; + let _ = status.send(success_status(format!("has tables: {}", has_tables))); @@ - status.send(success_status( + let _ = status.send(success_status( "Initializing database tables and importing data...", - ))?; + )); @@ - status.send(success_status(format!( + let _ = status.send(success_status(format!( "Last synced block: {}", last_synced_block - )))?; + ))); @@ - status.send(success_status("Fetching latest onchain events..."))?; + let _ = status.send(success_status("Fetching latest onchain events...")); @@ - status.send(success_status("Decoding fetched events..."))?; + let _ = status.send(success_status("Decoding fetched events...")); @@ - status.send(success_status("Populating token information..."))?; + let _ = status.send(success_status("Populating token information...")); @@ - status.send(success_status("Populating database..."))?; + let _ = status.send(success_status("Populating database...")); @@ - status.send(success_status("Database sync complete."))?; + let _ = status.send(success_status("Database sync complete."));This keeps the sync robust even if the status sink fails.
225-242: Consider request timeouts for dump download.download_and_decompress_dump uses a default Client without timeouts. A hung response will block the first‑run path indefinitely.
Use a short per‑request timeout:
- let client = Client::new(); + let client = Client::builder() + .timeout(std::time::Duration::from_secs(10)) + .build()?;This improves resilience without affecting wasm tests.
packages/webapp/src/routes/db-playground/+page.svelte (1)
161-176: Prevent overlapping syncs and tighten error handlingAdd an isSyncing guard to avoid concurrent sync calls from the interval. Also use nullish coalescing when picking readableMsg.
async function performAutoSync() { - if (!db?.value || isLoading) return; + if (!db?.value || isLoading || isSyncing) return; try { isSyncing = true; const queryFn = db.value.query.bind(db.value); // Sync database and capture status updates - const syncResult = await raindexClient.syncLocalDatabaseOnce( + const syncResult = await raindexClient.syncLocalDatabaseOnce( queryFn, - (status: OrderbookWasmEncodedResult<string>) => { + (status: OrderbookWasmEncodedResult<string>) => { if (status.error) { - syncStatus = status.error.readableMsg || status.error.msg; + syncStatus = status.error.readableMsg ?? status.error.msg; return; } if (status.value) { syncStatus = status.value; } }, 42161 ); if (syncResult.error) { - error = syncResult.error.readableMsg ?? syncResult.error.msg; + error = syncResult.error.readableMsg ?? syncResult.error.msg; return; }Also applies to: 169-186
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (2)
Cargo.lockis excluded by!**/*.locktauri-app/src-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (11)
crates/common/Cargo.toml(1 hunks)crates/common/src/raindex_client/local_db/sync.rs(21 hunks)packages/webapp/src/lib/components/Sidebar.svelte(2 hunks)packages/webapp/src/lib/components/SidebarLocalDbStatus.svelte(1 hunks)packages/webapp/src/lib/components/SidebarLocalDbStatus.test.ts(1 hunks)packages/webapp/src/lib/services/startLocalDbSync.ts(1 hunks)packages/webapp/src/lib/stores/dbSync.ts(0 hunks)packages/webapp/src/lib/stores/localDbStatus.test.ts(1 hunks)packages/webapp/src/lib/stores/localDbStatus.ts(1 hunks)packages/webapp/src/routes/+layout.svelte(2 hunks)packages/webapp/src/routes/db-playground/+page.svelte(3 hunks)
💤 Files with no reviewable changes (1)
- packages/webapp/src/lib/stores/dbSync.ts
🧰 Additional context used
📓 Path-based instructions (8)
packages/webapp/**/*.{svelte,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Webapp must pass Svelte lint/format checks (npm run svelte-lint-format-check -w @rainlanguage/webapp)
Files:
packages/webapp/src/lib/components/SidebarLocalDbStatus.test.tspackages/webapp/src/routes/db-playground/+page.sveltepackages/webapp/src/lib/services/startLocalDbSync.tspackages/webapp/src/lib/stores/localDbStatus.tspackages/webapp/src/lib/components/SidebarLocalDbStatus.sveltepackages/webapp/src/lib/stores/localDbStatus.test.tspackages/webapp/src/routes/+layout.sveltepackages/webapp/src/lib/components/Sidebar.svelte
packages/webapp/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Webapp tests must pass (npm run test -w @rainlanguage/webapp)
Files:
packages/webapp/src/lib/components/SidebarLocalDbStatus.test.tspackages/webapp/src/lib/stores/localDbStatus.test.ts
packages/{webapp,ui-components}/**/*.{svelte,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
If you modify frontend code, you must include a screenshot of the built webapp reflecting the change
Files:
packages/webapp/src/lib/components/SidebarLocalDbStatus.test.tspackages/webapp/src/routes/db-playground/+page.sveltepackages/webapp/src/lib/services/startLocalDbSync.tspackages/webapp/src/lib/stores/localDbStatus.tspackages/webapp/src/lib/components/SidebarLocalDbStatus.sveltepackages/webapp/src/lib/stores/localDbStatus.test.tspackages/webapp/src/routes/+layout.sveltepackages/webapp/src/lib/components/Sidebar.svelte
packages/**
📄 CodeRabbit inference engine (AGENTS.md)
JavaScript/Svelte projects live under packages/* (webapp, ui-components, orderbook)
Files:
packages/webapp/src/lib/components/SidebarLocalDbStatus.test.tspackages/webapp/src/routes/db-playground/+page.sveltepackages/webapp/src/lib/services/startLocalDbSync.tspackages/webapp/src/lib/stores/localDbStatus.tspackages/webapp/src/lib/components/SidebarLocalDbStatus.sveltepackages/webapp/src/lib/stores/localDbStatus.test.tspackages/webapp/src/routes/+layout.sveltepackages/webapp/src/lib/components/Sidebar.svelte
packages/**/*.{test,spec}.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name TypeScript test files as *.test.ts or *.spec.ts (Vitest)
Files:
packages/webapp/src/lib/components/SidebarLocalDbStatus.test.tspackages/webapp/src/lib/stores/localDbStatus.test.ts
crates/*/{src,tests,benches,examples}/**/*.rs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Rust code must pass clippy with all warnings denied (cargo clippy --workspace --all-targets --all-features -D warnings)
Files:
crates/common/src/raindex_client/local_db/sync.rs
crates/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Rust code lives in the workspace under crates/* (e.g., cli, common, bindings, js_api, quote, subgraph, settings, math, integration_tests)
Files:
crates/common/src/raindex_client/local_db/sync.rs
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Format Rust with nix develop -c cargo fmt --all
Lint Rust with nix develop -c rainix-rs-static (preconfigured flags)
Use snake_case for Rust crates/modules and PascalCase for types
Files:
crates/common/src/raindex_client/local_db/sync.rs
🧠 Learnings (3)
📚 Learning: 2025-10-06T11:13:29.956Z
Learnt from: findolor
PR: rainlanguage/rain.orderbook#2123
File: crates/common/src/raindex_client/local_db/mod.rs:23-29
Timestamp: 2025-10-06T11:13:29.956Z
Learning: In `crates/common/src/raindex_client/local_db/mod.rs`, the `Default` implementation for `LocalDb` that creates an RPC client pointing to `http://localhost:4444` is acceptable because the RPC client must be explicitly configured before actual usage in production scenarios.
Applied to files:
crates/common/src/raindex_client/local_db/sync.rs
📚 Learning: 2025-05-14T04:49:14.621Z
Learnt from: findolor
PR: rainlanguage/rain.orderbook#1710
File: crates/quote/Cargo.toml:30-30
Timestamp: 2025-05-14T04:49:14.621Z
Learning: In the rain.orderbook repository, wasm-bindgen-utils is intentionally used as an unconditional dependency because non-wasm targets (like tests) sometimes need to use it.
Applied to files:
crates/common/Cargo.toml
📚 Learning: 2025-05-14T05:13:59.713Z
Learnt from: findolor
PR: rainlanguage/rain.orderbook#1744
File: crates/subgraph/src/orderbook_client.rs:44-46
Timestamp: 2025-05-14T05:13:59.713Z
Learning: In the rain.orderbook project, WASM dependencies are intentionally made available in non-WASM targets to facilitate testing of WASM-related functionality, so conditional compilation guards like `#[cfg(target_family = "wasm")]` should not be added to imports or implementations that may be needed for tests.
Applied to files:
crates/common/Cargo.toml
🧬 Code graph analysis (3)
packages/webapp/src/lib/services/startLocalDbSync.ts (1)
packages/webapp/src/lib/stores/localDbStatus.ts (3)
setLocalDbSyncEnabled(73-75)recordLocalDbError(77-79)recordLocalDbStatus(62-71)
crates/common/src/raindex_client/local_db/sync.rs (1)
crates/common/src/raindex_client/mod.rs (1)
new(111-132)
packages/webapp/src/lib/stores/localDbStatus.test.ts (1)
packages/webapp/src/lib/stores/localDbStatus.ts (5)
localDbStatusIndicator(38-60)setLocalDbSyncEnabled(73-75)recordLocalDbStatus(62-71)localDbLatestEntry(36-36)recordLocalDbError(77-79)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
- GitHub Check: test
- GitHub Check: test
- GitHub Check: standard-tests (ubuntu-latest, test-js-bindings)
- GitHub Check: standard-tests (ubuntu-latest, rainix-wasm-artifacts)
- GitHub Check: standard-tests (ubuntu-latest, rainix-rs-static)
- GitHub Check: standard-tests (ubuntu-latest, ob-rs-test, true)
- GitHub Check: standard-tests (ubuntu-latest, rainix-rs-artifacts, true)
- GitHub Check: standard-tests (ubuntu-latest, rainix-sol-test)
- GitHub Check: standard-tests (ubuntu-latest, rainix-wasm-test)
- GitHub Check: standard-tests (ubuntu-latest, rainix-sol-artifacts)
- GitHub Check: build-tauri (ubuntu-22.04, true)
- GitHub Check: git-clean
- GitHub Check: test
- GitHub Check: Deploy-Preview-Push
- GitHub Check: Deploy-Docs-Preview
🔇 Additional comments (9)
crates/common/src/raindex_client/local_db/sync.rs (2)
440-471: No stop/cancel mechanism; spawning multiple loops would duplicate work.The loop runs forever every 10s. If startLocalDbSync is called twice, you’ll have two concurrent loops.
- Make startLocalDbSync idempotent, or
- Return a StopHandle/AbortController‑like handle from Rust (e.g., store an Rc checked in the loop), or
- At least guard repeats in JS before calling syncLocalDatabase.
Would you confirm startLocalDbSync is idempotent and prevents duplicate loops?
420-474: Exported sync loop should be cfg‑gated to wasm; else non‑wasm builds see wasm‑only symbols.The method uses js_sys types and spawn_local. Add a target_family = "wasm" guard:
#[wasm_export] impl RaindexClient { - #[wasm_export(js_name = "syncLocalDatabase", unchecked_return_type = "void")] - pub async fn sync_database( + #[cfg(target_family = "wasm")] + #[wasm_export(js_name = "syncLocalDatabase", unchecked_return_type = "void")] + pub async fn sync_database( &self, @@ - spawn_local(async move { + #[cfg(target_family = "wasm")] + spawn_local(async move { loop { @@ } });This avoids non‑wasm compilation failures while keeping wasm tests intact. Based on learnings
⛔ Skipped due to learnings
Learnt from: findolor PR: rainlanguage/rain.orderbook#1744 File: crates/subgraph/src/orderbook_client.rs:44-46 Timestamp: 2025-05-14T05:13:59.713Z Learning: In the rain.orderbook project, WASM dependencies are intentionally made available in non-WASM targets to facilitate testing of WASM-related functionality, so conditional compilation guards like `#[cfg(target_family = "wasm")]` should not be added to imports or implementations that may be needed for tests.Learnt from: findolor PR: rainlanguage/rain.orderbook#2008 File: crates/common/src/raindex_client/add_orders.rs:85-86 Timestamp: 2025-07-18T10:31:05.498Z Learning: In the rainlanguage/rain.orderbook codebase, cfg-guarded imports like `#[cfg(not(target_family = "wasm"))] use super::*;` in test modules are sometimes needed to fix compiler warnings, even when similar imports exist in nested sub-modules. These should not be removed as they serve a specific purpose in the compilation process.Learnt from: findolor PR: rainlanguage/rain.orderbook#1715 File: crates/js_api/src/common/mod.rs:111-118 Timestamp: 2025-04-30T09:28:36.960Z Learning: In the rain.orderbook repository, the WASM tests are already properly configured with conditional compilation using `#[cfg(target_family = "wasm")]` and `#[cfg(not(target_family = "wasm"))]`, and don't require additional `wasm_bindgen_test_configure!(run_in_browser)` directives.packages/webapp/src/lib/components/Sidebar.svelte (1)
30-30: UI change requires a screenshot.Per repo guidelines, frontend changes under packages/{webapp,ui-components} require a screenshot of the built webapp. Please attach one to the PR showing the new sidebar status panel.
As per coding guidelines
packages/webapp/src/routes/+layout.svelte (1)
37-44: Ensure startLocalDbSync is idempotent and wires callbacks correctly.With the Rust side spawning a perpetual loop, multiple invocations would duplicate work. Please ensure startLocalDbSync:
- Sets the DB callback once and avoids re‑starting if already running.
- Passes the status callback expected by Rust so UI gets updates.
If not, add a simple singleton/guard in the service.
crates/common/Cargo.toml (1)
68-69: Dependencies are already unconditional; non-wasm build won’t fail.Likely an incorrect or invalid review comment.
packages/webapp/src/routes/db-playground/+page.svelte (1)
7-10: Type alias for WasmEncodedResult is correctDisambiguates orderbook vs sqlite-web result shapes cleanly. LGTM.
packages/webapp/src/lib/stores/localDbStatus.test.ts (1)
8-80: Good coverage and isolationTests exercise key states and module reset. LGTM.
packages/webapp/src/lib/services/startLocalDbSync.ts (2)
82-241: Inline tests are effectiveMocks and assertions cover happy path, status error, resolve error, and rejection. LGTM.
15-55: Verified single invocation and API consistency
startLocalDbSync is only called once in +layout.svelte at app mount. RaindexClient.syncLocalDatabase’s callback signature and return value match existing tests. No further action required.
| rc::Rc, | ||
| str::FromStr, | ||
| }; | ||
| use wasm_bindgen_futures::spawn_local; |
There was a problem hiding this comment.
Ungated spawn_local import breaks non‑wasm builds.
wasm_bindgen_futures::spawn_local is wasm‑only. Importing it unconditionally will fail on non‑wasm targets.
Gate the import (and usage) to wasm:
- use wasm_bindgen_futures::spawn_local;
+#[cfg(target_family = "wasm")]
+use wasm_bindgen_futures::spawn_local;Also gate the exported loop method below to wasm (see next comment). Based on learnings
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| use wasm_bindgen_futures::spawn_local; | |
| #[cfg(target_family = "wasm")] | |
| use wasm_bindgen_futures::spawn_local; |
🤖 Prompt for AI Agents
In crates/common/src/raindex_client/local_db/sync.rs around line 28, the
unconditional use of wasm_bindgen_futures::spawn_local breaks non-wasm builds;
wrap the import with a wasm-only cfg (e.g. #[cfg(target_arch = "wasm32")] use
wasm_bindgen_futures::spawn_local;) and apply the same #[cfg(target_arch =
"wasm32")] (or equivalent) conditional compilation to the exported loop method
referenced in the review so both the import and any spawn_local usage are
compiled only for wasm targets.
There was a problem hiding this comment.
this does not break the non-wasm builds as the code compiles
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how wasm-bindgen-futures is configured in Cargo.toml
cat crates/common/Cargo.toml | grep -A 5 -B 5 "wasm-bindgen-futures"
Length of output: 309
🏁 Script executed:
#!/bin/bash
# Check the full context of dependencies in Cargo.toml to understand target-specific configuration
cat crates/common/Cargo.toml | grep -B 20 "wasm-bindgen-futures"
Length of output: 583
🏁 Script executed:
#!/bin/bash
# Search for the wasm_export macro definition to understand its behavior
rg -n "macro_rules! wasm_export|#\[proc_macro_attribute\].*wasm_export" --type rust
# Also check the wasm_bindgen_utils crate usage
rg -n "wasm_bindgen_utils" crates/common/src/raindex_client/local_db/sync.rs -A 2 -B 2
Length of output: 254
🏁 Script executed:
#!/bin/bash
# Check the wasm_bindgen_utils crate to understand what wasm_export does
rg -n "cfg.*wasm" crates/common/src/raindex_client/local_db/sync.rs | head -20
# Also try to find the actual usage of spawn_local to confirm it's only in wasm_export blocks
rg -n "spawn_local" crates/common/src/raindex_client/local_db/sync.rs -B 5 -A 2
# Check if there's documentation or examples of wasm_export usage
fd -e rs -x grep -l "wasm_export" {} \; | head -5 | xargs grep -n "wasm_export" | head -20
Length of output: 2209
🏁 Script executed:
#!/bin/bash
# Find the wasm_bindgen_utils crate source or check how wasm_export is defined
find . -name "Cargo.toml" -exec grep -l "wasm_bindgen_utils" {} \; | head -3
# Check if wasm_export applies any cfg attributes
rg -n "#\[wasm_export\]" crates/common/src/raindex_client/local_db/sync.rs -A 10 | head -30
# Try to compile non-wasm target to confirm the claim
cd crates/common && cargo check --lib --target x86_64-unknown-linux-gnu 2>&1 | head -50
Length of output: 599
🏁 Script executed:
#!/bin/bash
# Check the workspace Cargo.toml to understand wasm_bindgen_utils
cat Cargo.toml | grep -A 5 "wasm_bindgen_utils"
# Look for wasm_bindgen_utils source in the workspace
fd -t f "lib.rs" -p "wasm_bindgen_utils" | head -3 | xargs cat | grep -A 20 "proc_macro_attribute" | head -40
# Check if there are other files using this pattern successfully
rg -l "use wasm_bindgen_futures" --type rust | head -5 | xargs rg -n "cfg.*wasm.*wasm_bindgen_futures" -B 2 -A 2
Length of output: 105
🏁 Script executed:
#!/bin/bash
# Find the wasm_bindgen_utils crate in the workspace
fd -t d "wasm.*bindgen.*utils" | head -3
# Check workspace members to find it
cat Cargo.toml | grep -A 20 "\[workspace\]"
# Look for the crate definition
rg -l "name = \"wasm_bindgen_utils\"" --type toml
# Check if spawn_local usage actually compiles on non-wasm by looking at test structure
rg -n "mod.*tests" crates/common/src/raindex_client/local_db/sync.rs -A 3 | head -20
Length of output: 1026
🏁 Script executed:
#!/bin/bash
# Find where wasm_bindgen_utils is defined - it's likely in crates/
fd -t f "lib.rs" crates/ | xargs grep -l "wasm_export" | head -3
# Check the actual crate
ls -la crates/ | grep -i wasm
# Look at how wasm_export macro is defined
fd -t f "lib.rs" crates/ | xargs rg -n "pub use.*wasm_export" | head -10
# Check if there's a proc-macro crate
fd "Cargo.toml" crates/ | xargs grep -l "proc-macro = true" | head -5
Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check if wasm_bindgen_utils is an external dependency
cat Cargo.toml | grep -i "wasm_bindgen_utils"
cat crates/common/Cargo.toml | grep -i "wasm_bindgen_utils"
# Look for where wasm_export is actually defined - check for re-exports
rg "wasm_export" crates/ --type rust -n | grep "pub use\|pub fn\|macro" | head -20
# Check how the methods using spawn_local are actually compiled
rg -A 30 "#\[wasm_export\]" crates/common/src/raindex_client/local_db/sync.rs | grep -E "#\[wasm_export\]|spawn_local|pub async fn" | head -20
Length of output: 79
🏁 Script executed:
#!/bin/bash
# Let's verify if wasm-bindgen-futures actually provides stubs for non-wasm targets
# by checking its documentation or source
cat > /tmp/check_wasm_dep.sh << 'EOF'
#!/bin/bash
# Try to understand if wasm-bindgen-futures has cross-platform support
echo "Checking if wasm-bindgen-futures builds on non-wasm targets..."
cargo metadata --format-version 1 2>/dev/null | jq -r '.packages[] | select(.name == "wasm-bindgen-futures") | .targets[].kind[]' | head -5 || echo "Metadata check failed"
EOF
chmod +x /tmp/check_wasm_dep.sh
/tmp/check_wasm_dep.sh
# Alternative: check if there are any compile errors in CI or test outputs
rg "error.*spawn_local|error.*wasm_bindgen_futures" . --type rust | head -10
Length of output: 62
🏁 Script executed:
#!/bin/bash
# Check if the parent module (sync.rs) or local_db module has any cfg gates
cat crates/common/src/raindex_client/local_db/mod.rs | head -50
# Check what exports this sync module
rg "pub mod sync|pub use.*sync" crates/common/src/raindex_client/local_db/ --type rust -n
# Let's see the full module structure to understand if there's conditional compilation higher up
rg "^(pub )?mod local_db" crates/common/src/raindex_client/mod.rs -B 2 -A 5
Length of output: 1445
🏁 Script executed:
#!/bin/bash
# Let's check if the entire impl RaindexClient block at line 420 is wasm-only
# or if there are multiple impl blocks
rg -n "^impl RaindexClient" crates/common/src/raindex_client/local_db/sync.rs
# Check if sync.rs module itself has any cfg gates that would prevent compilation on non-wasm
head -5 crates/common/src/raindex_client/local_db/sync.rs
# Look for any evidence that wasm-bindgen-futures might have conditional features or stubs
rg "wasm.*bindgen.*futures" . -g "*.lock" -A 3 -B 3 | head -50
Length of output: 1594
🏁 Script executed:
#!/bin/bash
# Let's understand the wasm_export macro by checking the dependency
cat Cargo.lock | grep -A 10 "name = \"wasm-bindgen-utils\""
# Check if there's a GitHub Actions workflow that tests non-wasm builds
fd -e yml -e yaml ".github" | xargs cat | grep -E "cargo (check|build|test)" | head -20
# Look for any actual proof of non-wasm compilation succeeding
rg "target.*linux-gnu|target.*x86_64" .github/ --type yaml -B 2 -A 2 | head -30
Length of output: 615
🏁 Script executed:
#!/bin/bash
# Check which version of wasm-bindgen-utils is being used
cat crates/common/Cargo.toml | grep -A 3 "wasm-bindgen-utils"
# Now let's understand what wasm_export macro actually does by checking the source
# since it's from a git repo
echo "Checking the rain.wasm repository source for wasm_export..."
rg "wasm_bindgen_utils.*git.*rain.wasm" . -g "*.toml"
Length of output: 179
🏁 Script executed:
#!/bin/bash
# Check workspace dependencies for wasm-bindgen-utils
cat Cargo.toml | grep -A 2 "wasm-bindgen-utils"
# Now let's actually verify the claim by examining what happens with spawn_local
# when the code is not on wasm. The key insight is that spawn_local is only
# used inside the #[wasm_export] impl block, which might be conditionally compiled
rg "#\[wasm_export\]" crates/common/src/raindex_client/local_db/sync.rs -B 2 -A 50 | head -80
# Check if wasm_export applies any conditional compilation attributes
rg "cfg.*wasm" Cargo.lock | grep -i "wasm-bindgen-utils" -B 5 -A 5
Length of output: 2148
@findolor, you're correct - I apologize for the confusion. The code does compile on non-wasm builds.
The reason this works is that the #[wasm_export] macro (from the wasm-bindgen-utils crate in your rain.wasm repository) applies conditional compilation to the entire impl RaindexClient block. Since spawn_local is only used within that macro-gated implementation block, the usage is stripped on non-wasm targets even though the import statement remains at the top level.
In essence:
- On wasm targets:
wasm-bindgen-futuresdependency is available, the import resolves, and the#[wasm_export]impl block (withspawn_localusage) is compiled - On non-wasm targets: The
#[wasm_export]macro strips the impl block, sospawn_localis never actually referenced, and Rust doesn't error on the unused import
Your non-wasm tests at line 1112 (mod non_wasm_tests) confirm this works correctly.
My original concern was based on typical patterns where ungated imports from target-specific dependencies cause compilation failures, but your #[wasm_export] macro handles this elegantly by gating the usage itself. Thank you for the clarification!
🧬 Code Graph Analysis Results
crates/common/src/raindex_client/local_db/query/fetch_tables/mod.rs
- lines 11-15
pub async fn fetch_all_tables(
db_callback: &js_sys::Function,
) -> Result<Vec<TableResponse>, LocalDbQueryError> {
LocalDbQuery::execute_query_json::<Vec<TableResponse>>(db_callback, QUERY).await
}
crates/common/src/raindex_client/local_db/query/fetch_last_synced_block/mod.rs
- lines 18-26
pub async fn fetch_last_synced_block(
db_callback: &js_sys::Function,
) -> Result<Vec<SyncStatusResponse>, LocalDbQueryError> {
LocalDbQuery::execute_query_json::<Vec<SyncStatusResponse>>(
db_callback,
FETCH_LAST_SYNCED_BLOCK_SQL,
)
.await
}
crates/common/src/raindex_client/local_db/query/fetch_store_addresses/mod.rs
- lines 11-19
pub async fn fetch_store_addresses(
db_callback: &js_sys::Function,
) -> Result<Vec<StoreAddressRow>, LocalDbQueryError> {
LocalDbQuery::execute_query_json::<Vec<StoreAddressRow>>(
db_callback,
FETCH_STORE_ADDRESSES_SQL,
)
.await
}
crates/common/src/raindex_client/local_db/query/mod.rs
- lines 80-85
pub async fn execute_query_text(
callback: &js_sys::Function,
sql: &str,
) -> Result<String, LocalDbQueryError> {
Self::execute_query_raw(callback, sql).await
}
crates/common/src/raindex_client/local_db/insert.rs
- summarized description
Async function that generates SQL from decoded events: - Parameters:
- events: slice of DecodedEventData
- end_block: u64
- decimals_by_token: HashMap<Address, u8>
- prefix_sql: Option<&str>
- Returns: Result<String, LocalDbError>
- Behavior: builds a BEGIN TRANSACTION; optionally prepends a prefix SQL; iterates events and appends SQL for each event type (DepositV2, WithdrawV2, AddOrderV3, RemoveOrderV3, TakeOrderV3, ClearV3, AfterClearV2, MetaV1_2, InterpreterStoreSet, Unknown logs are warned); appends an UPDATE sync_status statement; ends with COMMIT.
- Important: On error, maps underlying errors to LocalDbError variants.
crates/common/src/raindex_client/local_db/token_fetch.rs
- summarized description
fetch_erc20_metadata_concurrent - Parameters:
- rpcs: Vec<-url::Url> (RPC endpoints)
- missing_addrs: Vec
- Returns: Result<Vec<(Address, TokenInfo)>, LocalDbError>
- Behavior: concurrently fetches token metadata with retries, up to MAX_TOKEN_RETRY_ATTEMPTS; returns aggregated (address, TokenInfo) tuples or error.
crates/common/src/raindex_client/local_db/query/fetch_erc20_tokens_by_addresses/mod.rs
- not shown fully here, but relevant when understanding how existing token rows are retrieved to support ERC20 token prefix population.
🧠 Learnings used
Learnt from: findolor
PR: rainlanguage/rain.orderbook#2123
File: crates/common/src/raindex_client/local_db/mod.rs:23-29
Timestamp: 2025-10-06T11:13:29.956Z
Learning: In `crates/common/src/raindex_client/local_db/mod.rs`, the `Default` implementation for `LocalDb` that creates an RPC client pointing to `http://localhost:4444` is acceptable because the RPC client must be explicitly configured before actual usage in production scenarios.
| const DEFAULT_SYNC_CHAIN_ID: u32 = super::SUPPORTED_LOCAL_DB_CHAINS[0]; | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
Hard‑coded DEFAULT_SYNC_CHAIN_ID risks syncing the wrong network.
Using SUPPORTED_LOCAL_DB_CHAINS[0] ignores the user’s selected chain/network. Align the loop with app state:
- Accept a chain_id parameter in syncLocalDatabase (or read from settings/wallet).
- Or store a default in RaindexClient that the webapp sets explicitly.
Otherwise the webapp may present status for a different chain than the user expects.
Can you confirm startLocalDbSync wires the chosen chain into Rust, or that the default matches the active network?
🏁 Script executed:
#!/bin/bash
set -e
echo "1. Locate DEFAULT_SYNC_CHAIN_ID in sync.rs"
grep -n "DEFAULT_SYNC_CHAIN_ID" -n crates/common/src/raindex_client/local_db/sync.rs
echo -e "\n2. Show top 200 lines of sync.rs"
sed -n '1,200p' crates/common/src/raindex_client/local_db/sync.rs
echo -e "\n3. List functions defined in sync.rs"
grep -nE "^pub fn|^fn " crates/common/src/raindex_client/local_db/sync.rs
echo -e "\n4. Search for 'sync_local_database' across crates/common"
grep -R "sync_local_database" -n crates/common
echo -e "\n5. Search for 'start_local_db_sync' across repository"
grep -R "start_local_db_sync" -n .
echo -e "\n6. Search for 'startLocalDbSync' in JS/WASM bindings"
grep -R "startLocalDbSync" -n .Length of output: 7032
Parameterize DEFAULT_SYNC_CHAIN_ID with user-selected chain
DEFAULT_SYNC_CHAIN_ID (crates/common/src/raindex_client/local_db/sync.rs:33) is hard-coded to SUPPORTED_LOCAL_DB_CHAINS[0] and no JS/WASM entrypoint (e.g. startLocalDbSync) injects the active network. Refactor syncLocalDatabase (and its bindings) to accept or read the current chain_id (via a parameter or RaindexClient state) instead of using the constant.
🤖 Prompt for AI Agents
In crates/common/src/raindex_client/local_db/sync.rs around line 33,
DEFAULT_SYNC_CHAIN_ID is hard-coded to SUPPORTED_LOCAL_DB_CHAINS[0]; refactor
syncLocalDatabase to accept a chain_id parameter (or read it from RaindexClient
state) instead of using the constant, remove/avoid using DEFAULT_SYNC_CHAIN_ID,
update all internal callers to pass the active chain_id, and update the JS/WASM
entrypoint(s) (e.g., startLocalDbSync binding) to forward the user-selected
network/chain id into the Rust API so the sync uses the correct chain.
There was a problem hiding this comment.
this will change in downstream PRs with multi network/orderbook implementation
There was a problem hiding this comment.
@findolor, understood! I've noted that the hard-coded DEFAULT_SYNC_CHAIN_ID will be addressed as part of the multi-network/orderbook implementation in downstream PRs. This aligns with your current architecture that maintains a single orderbook per network.
✏️ Learnings added
Learnt from: findolor
PR: rainlanguage/rain.orderbook#2202
File: crates/common/src/raindex_client/local_db/sync.rs:33-34
Timestamp: 2025-10-14T07:51:55.113Z
Learning: In `crates/common/src/raindex_client/local_db/sync.rs`, the hard-coded `DEFAULT_SYNC_CHAIN_ID` constant (set to `SUPPORTED_LOCAL_DB_CHAINS[0]`) will be replaced with proper chain ID handling in downstream PRs as part of the multi-network/orderbook implementation.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: findolor
PR: rainlanguage/rain.orderbook#2123
File: crates/common/src/raindex_client/local_db/mod.rs:23-29
Timestamp: 2025-10-06T11:13:29.956Z
Learning: In `crates/common/src/raindex_client/local_db/mod.rs`, the `Default` implementation for `LocalDb` that creates an RPC client pointing to `http://localhost:4444` is acceptable because the RPC client must be explicitly configured before actual usage in production scenarios.
Learnt from: findolor
PR: rainlanguage/rain.orderbook#2159
File: crates/cli/src/commands/local_db/sync/runner/mod.rs:52-113
Timestamp: 2025-10-06T14:41:41.909Z
Learning: The local DB sync CLI command (crates/cli/src/commands/local_db/sync/) is designed for CI-only usage, and simple println! statements are preferred over structured logging for status messages.
Learnt from: findolor
PR: rainlanguage/rain.orderbook#1938
File: crates/settings/src/yaml/orderbook.rs:185-199
Timestamp: 2025-06-18T19:24:40.518Z
Learning: In crates/settings/src/yaml/orderbook.rs, the user prefers not to refactor get_orderbook_by_network_key to handle multiple orderbooks per network key since their current architecture maintains a one-to-one mapping between orderbooks and networks. They would consider the refactoring if the system evolves to support multiple orderbooks per network in the future.
Learnt from: findolor
PR: rainlanguage/rain.orderbook#2145
File: crates/common/src/raindex_client/local_db/query/fetch_orders/query.sql:6-7
Timestamp: 2025-10-06T11:28:30.692Z
Learning: In `crates/common/src/raindex_client/local_db/query/fetch_orders/query.sql`, the orderbook_address is currently hardcoded to '0x2f209e5b67A33B8fE96E28f24628dF6Da301c8eB' because the system only supports a single orderbook at the moment. Multiorderbook logic is not yet implemented and will be added in the future.
| <SidebarGroup border ulClass="list-none"> | ||
| <li class="list-none"> | ||
| <SidebarLocalDbStatus /> | ||
| </li> | ||
| </SidebarGroup> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Minor: avoid manual
For consistency with other groups, consider rendering the component directly (or via a wrapper component) without an extra
🤖 Prompt for AI Agents
In packages/webapp/src/lib/components/Sidebar.svelte around lines 149 to 153,
there's an extra manual <li> wrapping <SidebarLocalDbStatus /> even though
SidebarGroup already renders the list semantics; remove the surrounding <li> and
render <SidebarLocalDbStatus /> directly (or via a small wrapper component) as a
child of SidebarGroup so the group component owns list markup and styling,
keeping any needed classes on the SidebarLocalDbStatus component instead.
| <div | ||
| class="flex flex-col gap-2 rounded-lg border border-gray-200/70 bg-white/70 p-3 text-xs text-gray-600 shadow-sm backdrop-blur-sm dark:border-gray-700/70 dark:bg-gray-900/50 dark:text-gray-300" | ||
| > | ||
| <div class="flex items-center justify-between"> | ||
| <div class="flex flex-col"> | ||
| <span | ||
| class="text-[10px] font-semibold uppercase tracking-[0.12em] text-gray-500 dark:text-gray-400" | ||
| >Local Index</span | ||
| > | ||
| </div> | ||
| <span | ||
| class={`inline-flex items-center gap-1 rounded-full px-2 py-[3px] text-[10px] font-semibold uppercase tracking-wide ${$indicatorBadge.badgeClass}`} | ||
| > | ||
| <span class={`size-1.5 rounded-full ${$indicatorBadge.dotClass}`}></span> | ||
| <span class="max-w-[8rem] truncate">{$indicatorBadge.displayLabel}</span> | ||
| </span> | ||
| </div> | ||
|
|
||
| {#if $indicatorBadge.variant === 'error'} | ||
| <p | ||
| class="rounded-md bg-red-500/10 px-2 py-1 text-[11px] text-red-500 dark:bg-red-500/15 dark:text-red-300" | ||
| > | ||
| Sync loop reported an error. Latest message shown below. | ||
| </p> | ||
| {/if} |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Announce status updates to assistive tech
Add aria-live to the updates region and role="alert" for the error banner.
- {#if $indicatorBadge.variant === 'error'}
- <p
- class="rounded-md bg-red-500/10 px-2 py-1 text-[11px] text-red-500 dark:bg-red-500/15 dark:text-red-300"
- >
+ {#if $indicatorBadge.variant === 'error'}
+ <p
+ role="alert"
+ class="rounded-md bg-red-500/10 px-2 py-1 text-[11px] text-red-500 dark:bg-red-500/15 dark:text-red-300"
+ >
Sync loop reported an error. Latest message shown below.
</p>
{/if}
- <div>
+ <div aria-live="polite">
{#if $latestEntry}
<div
class="flex gap-2 rounded-md border border-gray-200/80 bg-white/70 px-2 py-[6px] dark:border-gray-700/70 dark:bg-gray-900/60"
>Also confirm a screenshot of this sidebar is attached to the PR, per repo guidelines.
Also applies to: 81-105
🤖 Prompt for AI Agents
In packages/webapp/src/lib/components/SidebarLocalDbStatus.svelte around lines
55-79 (and also apply the same changes to lines 81-105), add accessible
announcement attributes: add aria-live="polite" (or aria-live="assertive" if
immediate) to the region that contains the status badge/updates so assistive
tech hears changes, and add role="alert" to the error banner paragraph shown
when $indicatorBadge.variant === 'error'; ensure these attributes are applied to
the appropriate wrapper elements (the status updates region and the error <p>)
and keep existing styling and markup intact; after making the changes, attach a
screenshot of the updated sidebar to the PR per repo guidelines.
| vi.setSystemTime(new Date('2024-02-01T12:34:56Z')); | ||
|
|
||
| store.recordLocalDbStatus('Database sync complete.'); | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
setSystemTime without fake timers has no effect in Vitest.
Since the tests don’t assert timestamps it’s fine, but if you intend to control Date, add:
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2024-02-01T12:34:56Z'));
});
afterEach(() => {
vi.useRealTimers();
});Keeps intent clear and avoids future flakiness if you later render timestamps.
🤖 Prompt for AI Agents
In packages/webapp/src/lib/components/SidebarLocalDbStatus.test.ts around lines
28 to 31, the test calls vi.setSystemTime(...) but never enables fake timers so
setSystemTime has no effect; update the test setup to call vi.useFakeTimers()
before setting the system time and restore real timers after the test (e.g., use
beforeEach to enable fake timers and setSystemTime, and afterEach to call
vi.useRealTimers()) so the mocked Date will actually take effect and avoid
future flakiness.
| function classifyMessage(message: string): LocalDbStatusLevel { | ||
| const normalized = message.toLowerCase(); | ||
|
|
||
| if (normalized.includes('error') || normalized.includes('fail')) { | ||
| return 'error'; | ||
| } | ||
|
|
||
| if ( | ||
| normalized.includes('complete') || | ||
| normalized.includes('synced') || | ||
| normalized.includes('success') | ||
| ) { | ||
| return 'success'; | ||
| } | ||
|
|
||
| if (normalized.includes('init') || normalized.includes('start')) { | ||
| return 'info'; | ||
| } | ||
|
|
||
| return 'info'; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Extend classifier to emit ‘warning’ for throttling/backoff cases
Currently ‘warning’ is never produced. Classify common transient issues as warning.
function classifyMessage(message: string): LocalDbStatusLevel {
const normalized = message.toLowerCase();
if (normalized.includes('error') || normalized.includes('fail')) {
return 'error';
}
+ // Transient/limiting conditions should surface as warnings
+ if (
+ normalized.includes('rate limit') ||
+ normalized.includes('429') ||
+ normalized.includes('retry') ||
+ normalized.includes('backoff') ||
+ normalized.includes('throttle') ||
+ normalized.includes('paused')
+ ) {
+ return 'warning';
+ }
+
if (
normalized.includes('complete') ||
normalized.includes('synced') ||
normalized.includes('success')
) {
return 'success';
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function classifyMessage(message: string): LocalDbStatusLevel { | |
| const normalized = message.toLowerCase(); | |
| if (normalized.includes('error') || normalized.includes('fail')) { | |
| return 'error'; | |
| } | |
| if ( | |
| normalized.includes('complete') || | |
| normalized.includes('synced') || | |
| normalized.includes('success') | |
| ) { | |
| return 'success'; | |
| } | |
| if (normalized.includes('init') || normalized.includes('start')) { | |
| return 'info'; | |
| } | |
| return 'info'; | |
| } | |
| function classifyMessage(message: string): LocalDbStatusLevel { | |
| const normalized = message.toLowerCase(); | |
| if (normalized.includes('error') || normalized.includes('fail')) { | |
| return 'error'; | |
| } | |
| // Transient/limiting conditions should surface as warnings | |
| if ( | |
| normalized.includes('rate limit') || | |
| normalized.includes('429') || | |
| normalized.includes('retry') || | |
| normalized.includes('backoff') || | |
| normalized.includes('throttle') || | |
| normalized.includes('paused') | |
| ) { | |
| return 'warning'; | |
| } | |
| if ( | |
| normalized.includes('complete') || | |
| normalized.includes('synced') || | |
| normalized.includes('success') | |
| ) { | |
| return 'success'; | |
| } | |
| if (normalized.includes('init') || normalized.includes('start')) { | |
| return 'info'; | |
| } | |
| return 'info'; | |
| } |
🤖 Prompt for AI Agents
In packages/webapp/src/lib/stores/localDbStatus.ts around lines 12 to 32, the
classifier never returns 'warning'; add detection for common
transient/throttling phrases (e.g., "throttl", "throttle", "backoff", "retry",
"rate limit", "rate-limited", "slow down") against the already lowercased
message and return 'warning' for those matches; place this check before the
'error' branch so transient throttling/backoff messages produce 'warning' while
explicit failures still produce 'error', and keep the existing 'info' and
'success' checks unchanged.
Motivation
See issues: #2187 and #2203
The main sync logic for local db was in rust but we have implemented the sync loop in the webapp. This needed to be also moved to rust side just like the main logic. With this we also needed to change how status callback worked with sync function.
Solution
Checks
By submitting this for review, I'm confirming I've done the following:
Summary by CodeRabbit
New Features
Refactor
Tests