Make RaindexClient Send+Sync on native targets#2489
Make RaindexClient Send+Sync on native targets#2489findolor wants to merge 6 commits into2026-03-03-raindex-client-localdb-refactorfrom
Conversation
- Conditionally use async_trait(?Send) for WASM, async_trait for native on LocalDbQueryExecutor trait - Add FnPtr<T> alias (Rc on WASM, Arc on native) for LocalDb internals - Split LocalDb::new into WASM and native impl blocks with correct bounds - Add ClientRef type alias (Rc on WASM, Arc on native) - Gate JsCallbackExecutor module and impl to WASM-only
- Split db/scheduler fields: Rc<RefCell<...>> on WASM, Arc<Mutex<...>> on native - Split LocalDbState::new into per-target impl blocks - Use poison-safe lock access (ok()/if-let) in db(), mark_ready(), is_ready(), and Drop impl
- Replace Rc::new/Rc::clone with ClientRef::new/ClientRef::clone - Update OrdersDataSource, VaultsDataSource traits to use async_trait cfg gate - Update LocalDbOrders, LocalDbVaults to accept ClientRef - Update RaindexOrder, RaindexVault to store ClientRef
- Move clear_tables_wasm into #[cfg(target_family = "wasm")] mod wasm - Move get_sync_status into #[cfg(target_family = "wasm")] mod wasm - Keep core logic functions ungated for both targets
- Apply async_trait cfg gate to all LocalDbQueryExecutor test impls - Replace RefCell with Mutex in test executors - Replace Rc with Arc in test code
WalkthroughThis PR introduces wasm/non-wasm specialization across the codebase. It replaces platform-agnostic synchronization primitives with conditional compilation: Rc/RefCell on single-threaded wasm and Arc/Mutex on multi-threaded native. It introduces a Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/common/src/raindex_client/local_db/mod.rs (1)
337-340:⚠️ Potential issue | 🔴 CriticalFix WASM test compile failure from missing
RaindexClientimport.Line [391] uses
RaindexClientwithout bringing it into scope, matching the current CI error.
Line [338] also has an unusedTimeoutFutureimport warning.Suggested patch
#[cfg(all(test, target_family = "wasm", feature = "browser-tests"))] mod wasm_tests { use super::*; - use gloo_timers::future::TimeoutFuture; + use crate::raindex_client::RaindexClient; use rain_orderbook_app_settings::spec_version::SpecVersion;Also applies to: 391-393
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/common/src/raindex_client/local_db/mod.rs` around lines 337 - 340, Add a missing import for RaindexClient and remove the unused TimeoutFuture import: bring RaindexClient into scope (e.g., add a use statement referencing RaindexClient so the code that references RaindexClient at lines ~391–393 compiles), and delete the unused use gloo_timers::future::TimeoutFuture; line (or gate it behind a wasm-only cfg if it will be used conditionally) to silence the unused-import warning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/common/src/raindex_client/local_db/state.rs`:
- Around line 91-96: The code currently swallows poisoned Mutex locks (e.g., in
the db() accessor and the other lock sites referenced) by mapping .lock().ok()
to None/false; instead recover the inner guard from a poisoned lock so local DB
routing and cleanup remain deterministic. Replace .lock().ok().and_then(|guard|
guard.as_ref().cloned()) with lock handling that matches on self.db.lock(): on
Ok(guard) use guard.as_ref().cloned(); on Err(poisoned) call
poisoned.into_inner() and then guard.as_ref().cloned(); apply the same pattern
to the other mutex accesses (the places around the other boolean/guard checks in
the file) so that poison does not silently fall back to subgraph or skip
scheduler stop. Ensure the logic still clones/returns LocalDb (or boolean) as
before after recovering the inner guard.
---
Outside diff comments:
In `@crates/common/src/raindex_client/local_db/mod.rs`:
- Around line 337-340: Add a missing import for RaindexClient and remove the
unused TimeoutFuture import: bring RaindexClient into scope (e.g., add a use
statement referencing RaindexClient so the code that references RaindexClient at
lines ~391–393 compiles), and delete the unused use
gloo_timers::future::TimeoutFuture; line (or gate it behind a wasm-only cfg if
it will be used conditionally) to silence the unused-import warning.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (26)
crates/cli/src/commands/local_db/pipeline/bootstrap.rscrates/common/src/local_db/executor.rscrates/common/src/local_db/export.rscrates/common/src/local_db/pipeline/adapters/apply.rscrates/common/src/local_db/pipeline/adapters/bootstrap.rscrates/common/src/local_db/pipeline/adapters/tokens.rscrates/common/src/local_db/pipeline/adapters/window.rscrates/common/src/local_db/pipeline/engine.rscrates/common/src/local_db/query/executor.rscrates/common/src/raindex_client/add_orders.rscrates/common/src/raindex_client/local_db/executor.rscrates/common/src/raindex_client/local_db/mod.rscrates/common/src/raindex_client/local_db/orders.rscrates/common/src/raindex_client/local_db/pipeline/bootstrap.rscrates/common/src/raindex_client/local_db/pipeline/runner/mod.rscrates/common/src/raindex_client/local_db/pipeline/runner/scheduler/native.rscrates/common/src/raindex_client/local_db/query/clear_tables.rscrates/common/src/raindex_client/local_db/query/fetch_last_synced_block.rscrates/common/src/raindex_client/local_db/state.rscrates/common/src/raindex_client/local_db/vaults.rscrates/common/src/raindex_client/mod.rscrates/common/src/raindex_client/orders.rscrates/common/src/raindex_client/remove_orders.rscrates/common/src/raindex_client/trades.rscrates/common/src/raindex_client/vaults.rscrates/js_api/src/registry.rs
| fn db(&self) -> Option<LocalDb> { | ||
| self.db | ||
| .lock() | ||
| .ok() | ||
| .and_then(|guard| guard.as_ref().cloned()) | ||
| } |
There was a problem hiding this comment.
Do not silently degrade behavior on poisoned locks.
On native, lock poison currently maps to None/false/no-op (Line 93, Line 193, Line 205), which can route queries to subgraph or skip scheduler stop after a panic. Prefer recovering with into_inner() so local DB routing and cleanup remain deterministic.
Suggested fix
fn db(&self) -> Option<LocalDb> {
- self.db
- .lock()
- .ok()
- .and_then(|guard| guard.as_ref().cloned())
+ match self.db.lock() {
+ Ok(guard) => guard.as_ref().cloned(),
+ Err(poisoned) => poisoned.into_inner().as_ref().cloned(),
+ }
}
@@
- if let Ok(mut guard) = self.scheduler.lock() {
- if let Some(handle) = guard.take() {
- handle.stop();
- }
- }
+ let mut guard = self
+ .scheduler
+ .lock()
+ .unwrap_or_else(|poisoned| poisoned.into_inner());
+ if let Some(handle) = guard.take() {
+ handle.stop();
+ }
}
}
@@
- if let Ok(mut guard) = self.ready.write() {
- guard.insert(chain_id);
- }
+ let mut guard = self
+ .ready
+ .write()
+ .unwrap_or_else(|poisoned| poisoned.into_inner());
+ guard.insert(chain_id);
@@
- return self
- .ready
- .read()
- .map(|guard| guard.contains(&chain_id))
- .unwrap_or(false);
+ let guard = self
+ .ready
+ .read()
+ .unwrap_or_else(|poisoned| poisoned.into_inner());
+ return guard.contains(&chain_id);Based on learnings: when local DB is enabled and available in crates/common/src/raindex_client, it should be used exclusively without subgraph fallback, because mixing sources can cause inconsistencies.
Also applies to: 160-165, 193-206
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/common/src/raindex_client/local_db/state.rs` around lines 91 - 96, The
code currently swallows poisoned Mutex locks (e.g., in the db() accessor and the
other lock sites referenced) by mapping .lock().ok() to None/false; instead
recover the inner guard from a poisoned lock so local DB routing and cleanup
remain deterministic. Replace .lock().ok().and_then(|guard|
guard.as_ref().cloned()) with lock handling that matches on self.db.lock(): on
Ok(guard) use guard.as_ref().cloned(); on Err(poisoned) call
poisoned.into_inner() and then guard.as_ref().cloned(); apply the same pattern
to the other mutex accesses (the places around the other boolean/guard checks in
the file) so that poison does not silently fall back to subgraph or skip
scheduler stop. Ensure the logic still clones/returns LocalDb (or boolean) as
before after recovering the inner guard.
Dependent PRs
Motivation
Following the local DB refactor in #2488,
RaindexClientand its dependency graph (LocalDb,LocalDbState,LocalDbQueryExecutor) still usedRc/RefCelleverywhere, making them!Send + !Syncon native targets. This blocks usingRaindexClientacross threads in CLI/server contexts.Solution
1.
LocalDbQueryExecutortrait — conditionalSendboundThe trait now uses
async_trait(?Send)on WASM andasync_trait(withSend) on native, via#[cfg_attr].2.
LocalDb—Arc-based internals on nativeFnPtr<T>alias:Rc<T>on WASM,Arc<T>on nativeExecuteBatchFn, etc.) requireSend + Syncon nativeimpl LocalDbblocks with platform-appropriate bounds3.
LocalDbState— thread-safe fields on nativedbfield:Rc<RefCell<...>>on WASM,Arc<Mutex<...>>on nativeschedulerfield: same split.ok(),if let Ok(...),.unwrap_or(false))4.
ClientReftype aliasRc<RaindexClient>on WASM,Arc<RaindexClient>on nativeorders,vaults,trades,add_orders,remove_orders)5. WASM export isolation
clear_tables_wasmandget_sync_statusmoved into#[cfg(target_family = "wasm")] mod wasmblocksJsCallbackExecutormodule gated to WASM-only6. Test updates
LocalDbQueryExecutormock impls updated withasync_traitcfg gateRefCell→Mutex,Rc→Arcin test codeChecks
By submitting this for review, I'm confirming I've done the following:
Summary by CodeRabbit
Release Notes