Skip to content

Conversation

@findolor
Copy link
Collaborator

@findolor findolor commented Dec 29, 2025

Motivation

See issues:

The Local DB sync scheduler previously ran all configured networks in a single sync cycle. This meant one slow or failing network (RPC timeouts, rate limiting, etc.) could block healthy networks from progressing, resulting in poor UX and preventing timely updates on healthy networks.

Solution

Refactored the Local DB sync scheduler to run each network on an independent sync cycle:

Rust changes (crates/common/src/raindex_client/local_db/):

  • Added NetworkRunnerConfig to filter global settings per network and build network-specific targets
  • Introduced NetworkSyncStatus struct with network-aware status fields (networkKey, chainId, status, schedulerState, error)
  • Added SchedulerState enum (Leader/NotLeader) to indicate leadership status per network
  • Refactored scheduler to spawn independent loops per network after a one-time schema initialization
  • Updated SchedulerHandle to track network keys and use synchronous stop (no longer async)
  • Added from_config constructor to ClientRunner for per-network configuration
  • Enhanced leadership module with with_network_key constructor for per-network lock management

Webapp changes (packages/webapp/):

  • Updated localDbStatus.ts store to maintain a Map<string, NetworkSyncStatus> for per-network status
  • Added updateNetworkStatus function to update individual network statuses
  • Created aggregateStatus derived store that computes overall status from per-network statuses
  • Updated layout to use the new updateNetworkStatus callback pattern

Key behaviors:

  • Each network runs its own scheduler loop independently
  • Schema bootstrap happens once before any network loops start
  • Status callbacks emit per-network status with scheduler state
  • A slow/failing network no longer blocks others from syncing
  • UI shows aggregate status (failure > syncing > active priority)

Checks

By submitting this for review, I'm confirming I've done the following:

  • made this PR as small as possible
  • unit-tested any new functionality
  • linked any relevant issues or PRs
  • included screenshots (if this involves a front-end change)

fix #2378

Summary by CodeRabbit

  • New Features

    • Multi-network orchestration with per-network runner configuration and per-network status reporting (sync status + scheduler/leader state) surfaced to the UI.
  • Refactor

    • Runner outcomes now distinguish leader vs not-leader paths and expose network/chain identifiers.
    • Scheduler stop made synchronous and scheduler now manages multiple network keys.
  • Other

    • Frontend store API switched to per-network status map and structured update function.

✏️ Tip: You can customize this high-level summary in your review settings.

- Each network runs in its own independent loop
- Per-network Web Locks: "local-db-sync-engine-{network_key}"
- Schema init runs once before spawning network loops
- Add RunOutcome enum (Report/NotLeader)
- Add NetworkSyncStatus for per-network status reporting
- Syncing status only emitted after confirmed leadership
- Fire-and-forget stop (sync, not async)
- Add integration test for slow network independence
- Change to map-based networkStatuses store
- Add updateNetworkStatus() helper
- Add aggregateStatus derived store (failure > syncing > active)
- Update +layout.svelte to use new callback
- Fix Sidebar tests for new store API
@findolor findolor self-assigned this Dec 29, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 29, 2025

Walkthrough

Implements per-network independent Local DB sync loops and per-network status reporting. Adds RunOutcome, NetworkSyncStatus, SchedulerState, NetworkRunnerConfig, network-aware leadership lock names; refactors scheduler to spawn per-network loops, makes scheduler stop synchronous, and adapts webapp stores to aggregate per-network statuses.

Changes

Cohort / File(s) Summary
Outcome & Status Types
crates/common/src/local_db/pipeline/runner/mod.rs, crates/common/src/raindex_client/local_db/mod.rs
Adds RunOutcome enum (Report(RunReport) / NotLeader), SchedulerState enum and NetworkSyncStatus struct with constructors and serde/wasm traits.
Runner Config & Targets
crates/common/src/raindex_client/local_db/pipeline/runner/config.rs
New NetworkRunnerConfig to derive network-scoped ParsedRunnerSettings, filter orderbooks/syncs by network_key, derive chain_id, and build RunnerTargets; includes unit tests.
Leadership (network-aware)
crates/common/src/raindex_client/local_db/pipeline/runner/leadership.rs
DefaultLeadership gains optional network_key, with_network_key() constructor, lock_name() helper; replaces static lock name prefix usage; attempt_web_lock accepts a lock_name.
Client Runner (control flow)
crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
Adds pub mod config; ClientRunner now carries network_key, chain_id, has_provisioned_dumps; run() returns Result<RunOutcome, LocalDbError>; new accessors and from_config constructor; provisioning-driven flow replaces bootstrapping flag.
Scheduler & Multi-network orchestration
crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
SchedulerRunner trait and DefaultClientRunner updated to return RunOutcome and expose network_key/chain_id; SchedulerHandle tracks multiple network_keys, stop() is synchronous; start spawns per-network loops and emits NetworkSyncStatus via callbacks.
RaindexClient stop behavior
crates/common/src/raindex_client/local_db/mod.rs
stop_local_db_scheduler() signature changed from async to sync; scheduler handle stop calls made synchronous and call sites/tests updated.
Frontend stores & UI wiring
packages/webapp/src/lib/stores/localDbStatus.ts, packages/webapp/src/routes/+layout.svelte, packages/webapp/src/__tests__/Sidebar.test.ts
Replaces single status store with networkStatuses Map, adds updateNetworkStatus() and aggregateStatus derived store; UI and tests updated to call updateNetworkStatus({...}).

Sequence Diagram(s)

sequenceDiagram
    participant Scheduler as Scheduler
    participant NetLoop as Per-Network Loop
    participant Runner as ClientRunner
    participant Leadership as Leadership
    participant DB as LocalDb
    participant UI as Webapp Store

    rect rgb(200,220,240)
    note over Scheduler: startup
    Scheduler->>Scheduler: parse global settings & build NetworkRunnerConfig per network
    Scheduler->>DB: initialize DB schema (once)
    end

    rect rgb(220,240,220)
    note over NetLoop: independent per-network cycle
    loop per-network sync
        NetLoop->>Runner: instantiate runner for network
        Runner->>Leadership: attempt acquire leadership (network lock)
        alt acquired
            Leadership-->>Runner: Leader
            Runner->>DB: fetch manifests & provision dumps
            Runner->>DB: execute sync cycle
            Runner-->>NetLoop: RunOutcome::Report(RunReport)
        else not leader
            Leadership-->>Runner: NotLeader
            Runner-->>NetLoop: RunOutcome::NotLeader
        end
        NetLoop->>UI: emit NetworkSyncStatus(networkKey, chainId, status, schedulerState, error?)
        NetLoop->>NetLoop: sleep until next scheduled cycle
    end
    end

    rect rgb(240,220,200)
    note over Scheduler: stop
    Scheduler->>NetLoop: stop all per-network loops (sync)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • #2378: Implements per-network independent sync loops, per-network status reporting, and leadership/scheduler state tracking matching the issue objectives.
  • rainlanguage/rain.orderbook#2379: Introduces per-network status types and emissions that align with UI per-network status requirements.

Possibly related PRs

  • PR #2344: Touches runner/scheduler result models and run signatures in the same modules.
  • PR #2302: Overlaps on local DB scheduler start/stop behavior and status emission types.
  • PR #2160: Related frontend store/layout wiring for local DB sync status integration.

Suggested labels

webapp

Suggested reviewers

  • hardyjosh
  • 0xgleb

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.83% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: implementing per-network independent sync cycles for Local DB, which matches the primary objectives and most significant refactoring in the PR.
Linked Issues check ✅ Passed All coding requirements from issue #2378 are met: per-network independent loops [#2378], per-network status with scheduler state [#2378], network-scoped manifest/dump provisioning [#2378], aggregate status for UI [#2378], and deterministic ordering [#2378].
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing per-network independent sync cycles. No unrelated modifications or refactoring outside the stated objectives is present.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 2025-12-28-per-network-sync-cycles

📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0e3c5a and c761af5.

📒 Files selected for processing (1)
  • crates/common/src/raindex_client/local_db/mod.rs
🧰 Additional context used
📓 Path-based instructions (3)
crates/**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

crates/**/*.rs: For Rust crates in crates/*, run lints using nix develop -c cargo clippy --workspace --all-targets --all-features -D warnings
For Rust crates in crates/*, run tests using nix develop -c cargo test --workspace or --package <crate>

Files:

  • crates/common/src/raindex_client/local_db/mod.rs
**/crates/**

📄 CodeRabbit inference engine (AGENTS.md)

Rust workspace organized as crates/* with subdirectories: cli, common, bindings, js_api, quote, subgraph, settings, math, integration_tests

Files:

  • crates/common/src/raindex_client/local_db/mod.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Rust: format code with nix develop -c cargo fmt --all
Rust: lint with nix develop -c rainix-rs-static (preconfigured flags included)
Rust: crates and modules use snake_case; types use PascalCase

Files:

  • crates/common/src/raindex_client/local_db/mod.rs
🧠 Learnings (5)
📓 Common learnings
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2202
File: crates/common/src/raindex_client/local_db/sync.rs:33-34
Timestamp: 2025-10-14T07:51:55.148Z
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.
📚 Learning: 2025-10-14T07:51:55.148Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2202
File: crates/common/src/raindex_client/local_db/sync.rs:33-34
Timestamp: 2025-10-14T07:51:55.148Z
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.

Applied to files:

  • crates/common/src/raindex_client/local_db/mod.rs
📚 Learning: 2025-10-06T14:41:41.909Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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.

Applied to files:

  • crates/common/src/raindex_client/local_db/mod.rs
📚 Learning: 2025-12-03T10:40:25.429Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2344
File: crates/common/src/local_db/pipeline/runner/mod.rs:18-31
Timestamp: 2025-12-03T10:40:25.429Z
Learning: In `crates/common/src/local_db/pipeline/runner/mod.rs`, the `TargetSuccess` struct does not need separate `ob_id` or `orderbook_key` fields because the contained `SyncOutcome` already includes orderbook identification information such as chain_id and orderbook_address. This avoids redundant data duplication.

Applied to files:

  • crates/common/src/raindex_client/local_db/mod.rs
📚 Learning: 2025-10-06T11:13:29.956Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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/mod.rs
⏰ 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). (2)
  • GitHub Check: build-tauri (ubuntu-22.04, true)
  • GitHub Check: Deploy-Docs-Preview
🔇 Additional comments (7)
crates/common/src/raindex_client/local_db/mod.rs (7)

132-138: LGTM!

The SchedulerState enum is well-structured with appropriate derives for WASM interop. The camelCase serde naming aligns with JavaScript conventions.


140-198: LGTM!

The NetworkSyncStatus struct and its constructors are well-designed:

  • active() correctly accepts a scheduler_state parameter since a network can be active while being leader or not.
  • syncing() and failure() appropriately hardcode SchedulerState::Leader since only the leader performs sync operations and would encounter failures.
  • The skip_serializing_if for the optional error field keeps JSON payloads clean.

255-257: LGTM!

The synchronous handle.stop() call correctly stops the existing scheduler before replacing it with a new one. This aligns with the PR's objective to make the stop operation synchronous.


267-280: LGTM!

The method correctly implements synchronous stop semantics. The pattern of taking the handle from the RefCell before stopping ensures proper cleanup and prevents the handle from being accessed after stop.


553-553: Verify the necessity of the additional timeout.

An additional 10ms timeout was added here. While this likely allows time for the scheduler's async operations to emit status updates before verification, consider adding a brief comment explaining why this delay is needed to help future maintainers understand the timing requirements.


516-517: LGTM!

The test correctly reflects the synchronous stop_local_db_scheduler() API change, using .expect() for the Result return type.


568-568: LGTM!

Consistent with other test updates for the synchronous stop API.


Comment @coderabbitai help to get the list of available commands and usage tips.

@findolor findolor requested review from 0xgleb and hardyjosh December 29, 2025 11:04
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f716241 and 0fd5189.

📒 Files selected for processing (9)
  • crates/common/src/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/config.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/leadership.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
  • packages/webapp/src/__tests__/Sidebar.test.ts
  • packages/webapp/src/lib/stores/localDbStatus.ts
  • packages/webapp/src/routes/+layout.svelte
🧰 Additional context used
📓 Path-based instructions (10)
crates/**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

crates/**/*.rs: For Rust crates in crates/*, run lints using nix develop -c cargo clippy --workspace --all-targets --all-features -D warnings
For Rust crates in crates/*, run tests using nix develop -c cargo test --workspace or --package <crate>

Files:

  • crates/common/src/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/config.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/leadership.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
**/crates/**

📄 CodeRabbit inference engine (AGENTS.md)

Rust workspace organized as crates/* with subdirectories: cli, common, bindings, js_api, quote, subgraph, settings, math, integration_tests

Files:

  • crates/common/src/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/config.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/leadership.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Rust: format code with nix develop -c cargo fmt --all
Rust: lint with nix develop -c rainix-rs-static (preconfigured flags included)
Rust: crates and modules use snake_case; types use PascalCase

Files:

  • crates/common/src/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/config.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/leadership.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
packages/webapp/**/*.{svelte,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

For Webapp (packages/webapp), run lints and format checks using nix develop -c npm run svelte-lint-format-check -w @rainlanguage/webapp

Files:

  • packages/webapp/src/lib/stores/localDbStatus.ts
  • packages/webapp/src/routes/+layout.svelte
  • packages/webapp/src/__tests__/Sidebar.test.ts
packages/webapp/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

For Webapp (packages/webapp), run tests using nix develop -c npm run test -w @rainlanguage/webapp

Files:

  • packages/webapp/src/lib/stores/localDbStatus.ts
  • packages/webapp/src/__tests__/Sidebar.test.ts
packages/{webapp,ui-components}/**/*.{svelte,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

If you modify frontend code or functionality affecting the frontend, you MUST provide a screenshot of the built webapp reflecting your change.

Files:

  • packages/webapp/src/lib/stores/localDbStatus.ts
  • packages/webapp/src/routes/+layout.svelte
  • packages/webapp/src/__tests__/Sidebar.test.ts
packages/**/*.{js,ts,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

JavaScript/Svelte organized as packages/* including webapp, ui-components, and orderbook (wasm wrapper published to npm)

Files:

  • packages/webapp/src/lib/stores/localDbStatus.ts
  • packages/webapp/src/routes/+layout.svelte
  • packages/webapp/src/__tests__/Sidebar.test.ts
**/*.{ts,tsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,svelte}: TypeScript/Svelte: format with nix develop -c npm run format
TypeScript/Svelte: lint with nix develop -c npm run lint
TypeScript/Svelte: type-check with nix develop -c npm run check

Files:

  • packages/webapp/src/lib/stores/localDbStatus.ts
  • packages/webapp/src/routes/+layout.svelte
  • packages/webapp/src/__tests__/Sidebar.test.ts
**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Svelte components should use PascalCase.svelte naming convention; other files use kebab or snake case as appropriate

Files:

  • packages/webapp/src/routes/+layout.svelte
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (AGENTS.md)

TypeScript/Svelte: run tests with nix develop -c npm run test (Vitest). Name test files *.test.ts or *.spec.ts

Files:

  • packages/webapp/src/__tests__/Sidebar.test.ts
🧠 Learnings (35)
📓 Common learnings
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2202
File: crates/common/src/raindex_client/local_db/sync.rs:33-34
Timestamp: 2025-10-14T07:51:55.148Z
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.
📚 Learning: 2025-12-03T10:40:25.429Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2344
File: crates/common/src/local_db/pipeline/runner/mod.rs:18-31
Timestamp: 2025-12-03T10:40:25.429Z
Learning: In `crates/common/src/local_db/pipeline/runner/mod.rs`, the `TargetSuccess` struct does not need separate `ob_id` or `orderbook_key` fields because the contained `SyncOutcome` already includes orderbook identification information such as chain_id and orderbook_address. This avoids redundant data duplication.

Applied to files:

  • crates/common/src/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/config.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-04-03T09:45:04.200Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1477
File: crates/settings/src/remote_networks.rs:40-75
Timestamp: 2025-04-03T09:45:04.200Z
Learning: The RemoteNetworksCfg implementation in crates/settings/src/remote_networks.rs intentionally fetches remote networks sequentially rather than in parallel.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/config.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-06-18T19:24:40.518Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/config.rs
📚 Learning: 2025-06-10T12:04:54.107Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1916
File: packages/ui-components/src/lib/__fixtures__/settings-12-11-24.json:182-195
Timestamp: 2025-06-10T12:04:54.107Z
Learning: In test fixture files like `packages/ui-components/src/lib/__fixtures__/settings-12-11-24.json`, network configuration inconsistencies (such as matchain using Polygon's RPC, chainId, and currency while having its own network key) are acceptable since they are used for testing purposes only.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/config.rs
  • packages/webapp/src/__tests__/Sidebar.test.ts
📚 Learning: 2025-06-10T12:08:23.128Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1916
File: packages/webapp/src/lib/services/resetActiveNetworkRef.ts:18-20
Timestamp: 2025-06-10T12:08:23.128Z
Learning: In the new NewConfig system that replaced ConfigSource in the rain.orderbook project, the settings store guarantees that $settings.orderbook.networks (and similar nested fields) will always be defined, so null/undefined safety checks are not needed when accessing these fields.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/config.rs
📚 Learning: 2025-10-18T10:38:41.273Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2237
File: crates/common/src/raindex_client/local_db/sync.rs:79-89
Timestamp: 2025-10-18T10:38:41.273Z
Learning: In `crates/common/src/raindex_client/local_db/sync.rs`, the sync_database method currently only supports indexing a single orderbook per chain ID, which is why `.first()` is used to select the orderbook configuration. Multi-orderbook support per chain ID is planned for future PRs.

Applied to files:

  • packages/webapp/src/lib/stores/localDbStatus.ts
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-10-14T07:51:55.148Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2202
File: crates/common/src/raindex_client/local_db/sync.rs:33-34
Timestamp: 2025-10-14T07:51:55.148Z
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.

Applied to files:

  • packages/webapp/src/lib/stores/localDbStatus.ts
  • crates/common/src/raindex_client/local_db/pipeline/runner/leadership.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-10-29T13:29:29.578Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2278
File: packages/webapp/src/lib/services/startLocalDbSync.ts:23-29
Timestamp: 2025-10-29T13:29:29.578Z
Learning: In packages/webapp/src/lib/services/startLocalDbSync.ts, the hard-coded default orderbook address '0x2f209e5b67A33B8fE96E28f24628dF6Da301c8eB' is a temporary placeholder that will be removed when new sync logic is introduced in the Raindex client, as documented in the NOTE comment.

Applied to files:

  • packages/webapp/src/lib/stores/localDbStatus.ts
  • packages/webapp/src/routes/+layout.svelte
📚 Learning: 2025-06-11T11:27:14.391Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1917
File: tauri-app/src/lib/stores/order.ts:10-15
Timestamp: 2025-06-11T11:27:14.391Z
Learning: In this codebase, Svelte writable/derived stores (e.g., `subgraph` in `tauri-app/src/lib/stores/settings.ts`) expose a custom asynchronous `.load()` helper that retrieves the current value, so calls like `await subgraph.load()` are valid.

Applied to files:

  • packages/webapp/src/lib/stores/localDbStatus.ts
📚 Learning: 2025-06-11T11:22:24.903Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1917
File: tauri-app/src/routes/orders/[network]-[orderHash]/+page.svelte:26-37
Timestamp: 2025-06-11T11:22:24.903Z
Learning: In the tauri-app codebase, the `settings` Svelte store is always initialized with a complete `Config` object whose `orderbook` field (and nested maps such as `orderbooks`, `subgraphs`, and `networks`) is guaranteed to exist, so optional chaining on those paths is unnecessary.

Applied to files:

  • packages/webapp/src/lib/stores/localDbStatus.ts
📚 Learning: 2025-05-19T17:16:54.732Z
Learnt from: hardingjam
Repo: rainlanguage/rain.orderbook PR: 1831
File: packages/ui-components/src/__tests__/transactionStore.test.ts:3-5
Timestamp: 2025-05-19T17:16:54.732Z
Learning: In the rain.orderbook project, there is an ongoing migration of transaction handling from the legacy transactionStore to a new TransactionManager. TransactionStatusMessage has been moved to lib/types/transaction.ts, while TransactionErrorMessage is still in transactionStore but will be moved once other transactions are migrated.

Applied to files:

  • packages/webapp/src/lib/stores/localDbStatus.ts
📚 Learning: 2025-10-06T10:23:57.479Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2111
File: packages/webapp/src/routes/db-playground/+page.svelte:149-153
Timestamp: 2025-10-06T10:23:57.479Z
Learning: The db-playground page at packages/webapp/src/routes/db-playground/+page.svelte is used for testing purposes, and production-level concerns (such as invalid RPC tokens) are acceptable in this context.

Applied to files:

  • packages/webapp/src/routes/+layout.svelte
📚 Learning: 2025-03-31T10:16:53.544Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1469
File: packages/ui-components/src/__tests__/CodeMirrorDotrain.test.ts:75-98
Timestamp: 2025-03-31T10:16:53.544Z
Learning: In the rainlanguage/rain.orderbook project, direct manipulation of Svelte's internal state (component.$$.ctx) in tests is an acceptable approach for testing component behavior, as demonstrated in the CodeMirrorDotrain tests.

Applied to files:

  • packages/webapp/src/routes/+layout.svelte
  • packages/webapp/src/__tests__/Sidebar.test.ts
📚 Learning: 2025-04-08T12:53:12.526Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1588
File: packages/orderbook/test/js_api/gui.test.ts:2037-2057
Timestamp: 2025-04-08T12:53:12.526Z
Learning: In the remote network test for gui.test.ts, asserting that getCurrentDeployment() succeeds is sufficient validation that remote networks were properly fetched, as this operation would fail if the networks weren't correctly processed.

Applied to files:

  • packages/webapp/src/__tests__/Sidebar.test.ts
📚 Learning: 2025-09-02T08:04:44.814Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2117
File: packages/ui-components/src/__tests__/VaultIdInformation.test.ts:9-13
Timestamp: 2025-09-02T08:04:44.814Z
Learning: In packages/ui-components/src/__tests__/VaultIdInformation.test.ts and similar test files in the rain.orderbook project, the passthrough vi.mock('rainlanguage/orderbook', async (importOriginal) => { return { ...(await importOriginal()) }; }); block is required for tests to run properly, even when not overriding any exports. This is needed due to the specific Vitest configuration or test environment setup in the project.

Applied to files:

  • packages/webapp/src/__tests__/Sidebar.test.ts
📚 Learning: 2025-06-30T14:17:16.626Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1926
File: packages/ui-components/src/lib/__mocks__/stores.ts:13-17
Timestamp: 2025-06-30T14:17:16.626Z
Learning: User findolor reports that vi.mock(import('rainlanguage/orderbook'), async (importOriginal) => { ... }) syntax works in their testing environment, despite official Vitest documentation indicating the first argument should be a string. This suggests there may be specific Vitest versions or configurations that support dynamic import() as the first argument to vi.mock().

Applied to files:

  • packages/webapp/src/__tests__/Sidebar.test.ts
📚 Learning: 2025-06-17T14:55:22.914Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1936
File: packages/ui-components/vite.config.ts:21-23
Timestamp: 2025-06-17T14:55:22.914Z
Learning: In the rain.orderbook project, the Vite configuration uses `'import.meta.vitest': 'undefined'` (as a string) combined with conditional `if (import.meta.vitest)` checks for in-source testing. The mock files are excluded from test execution using `exclude: ['src/lib/__mocks__/**/*.ts']`. This configuration successfully allows dev builds to work without `vi` undefined errors, despite the theoretical expectation that the string "undefined" would be truthy and cause issues.

Applied to files:

  • packages/webapp/src/__tests__/Sidebar.test.ts
📚 Learning: 2025-04-15T12:35:56.008Z
Learnt from: hardingjam
Repo: rainlanguage/rain.orderbook PR: 1669
File: packages/webapp/src/routes/+layout.ts:82-261
Timestamp: 2025-04-15T12:35:56.008Z
Learning: When testing Svelte applications, focus on application logic rather than testing Svelte's built-in reactive store system. Testing framework functionality (like Svelte's store reactivity) is generally unnecessary in application tests.

Applied to files:

  • packages/webapp/src/__tests__/Sidebar.test.ts
📚 Learning: 2025-07-09T12:35:45.699Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1974
File: packages/ui-components/src/__tests__/DeploymentSteps.test.ts:123-126
Timestamp: 2025-07-09T12:35:45.699Z
Learning: In packages/ui-components/src/__tests__/DeploymentSteps.test.ts, findolor prefers to keep mock initializations (like setSelectToken) in individual test cases rather than consolidating them into shared beforeEach blocks, even when it results in duplication.

Applied to files:

  • packages/webapp/src/__tests__/Sidebar.test.ts
📚 Learning: 2025-04-07T08:18:36.473Z
Learnt from: hardingjam
Repo: rainlanguage/rain.orderbook PR: 1565
File: packages/webapp/src/routes/deploy/layout.test.ts:12-29
Timestamp: 2025-04-07T08:18:36.473Z
Learning: In test files for this project, hardingjam prefers to use custom mocks (such as for localStorage) rather than relying on environment-provided implementations, as this allows for spying on individual methods and having precise control over implementation details for more robust testing.

Applied to files:

  • packages/webapp/src/__tests__/Sidebar.test.ts
📚 Learning: 2025-03-26T15:00:17.997Z
Learnt from: hardingjam
Repo: rainlanguage/rain.orderbook PR: 1516
File: packages/webapp/src/routes/deploy/[strategyName]/layout.test.ts:0-0
Timestamp: 2025-03-26T15:00:17.997Z
Learning: For Rain Orderbook projects, there's a preference not to include tests for SvelteKit methods (like parent function rejections) in test files, as these are considered framework responsibilities rather than application code that needs testing.

Applied to files:

  • packages/webapp/src/__tests__/Sidebar.test.ts
📚 Learning: 2025-03-24T12:27:07.862Z
Learnt from: hardingjam
Repo: rainlanguage/rain.orderbook PR: 1494
File: packages/ui-components/src/__tests__/WalletProvider.test.ts:18-28
Timestamp: 2025-03-24T12:27:07.862Z
Learning: In the WalletProvider tests, verifying that setAccountContext is called with the correct store is sufficient. Additional testing of Svelte's store implementation (like subscribing to verify the store value) is unnecessary as it would just be testing the framework itself.

Applied to files:

  • packages/webapp/src/__tests__/Sidebar.test.ts
📚 Learning: 2025-05-14T05:13:59.713Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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/src/raindex_client/local_db/pipeline/runner/leadership.rs
📚 Learning: 2025-07-18T10:31:05.498Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/leadership.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-04-30T09:28:36.960Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/leadership.rs
📚 Learning: 2025-05-27T06:16:35.738Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1874
File: tauri-app/src-tauri/src/transaction_status.rs:69-127
Timestamp: 2025-05-27T06:16:35.738Z
Learning: In tauri-app/src-tauri/src/transaction_status.rs, directly accessing internal RwLock state in tests using `notice.0.read().unwrap()` is acceptable and does not require encapsulation through public getter methods.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/leadership.rs
📚 Learning: 2025-10-06T11:13:29.956Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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/pipeline/runner/leadership.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-10-06T14:41:41.909Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-06-17T16:21:24.384Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1903
File: crates/settings/src/yaml/orderbook.rs:371-377
Timestamp: 2025-06-17T16:21:24.384Z
Learning: In crates/settings/src/yaml/orderbook.rs tests, the user findolor considers RPC ordering in Vec<Url> assertions to be intentional and not a test brittleness issue. The ordering of RPCs in tests should be preserved as specified.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
📚 Learning: 2025-07-21T16:34:04.947Z
Learnt from: 0xgleb
Repo: rainlanguage/rain.orderbook PR: 1911
File: crates/common/src/raindex_client/orders.rs:720-720
Timestamp: 2025-07-21T16:34:04.947Z
Learning: In the rainlanguage/rain.orderbook codebase, user 0xgleb prefers using `.unwrap()` in test code rather than `.expect()` with descriptive messages, considering the direct unwrap approach acceptable for test contexts where failures should be fast and clear.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
📚 Learning: 2025-05-20T10:20:08.206Z
Learnt from: 0xgleb
Repo: rainlanguage/rain.orderbook PR: 1859
File: crates/quote/src/quote_debug.rs:472-492
Timestamp: 2025-05-20T10:20:08.206Z
Learning: In the Rain Orderbook codebase, the `#[tokio::test(flavor = "multi_thread")]` annotation is specifically needed for tests that use `LocalEvm`, not just for consistency across all async tests.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-10-06T14:13:18.531Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2155
File: crates/common/src/raindex_client/trades.rs:133-152
Timestamp: 2025-10-06T14:13:18.531Z
Learning: In the rain.orderbook codebase, the `page` parameter in `RaindexOrder::get_trades_list` method (in crates/common/src/raindex_client/trades.rs) is kept for backwards compatibility with subgraph logic, but the LocalDb fast-path intentionally returns all trades without implementing pagination.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-07-15T08:01:38.534Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1994
File: crates/common/src/raindex_client/vaults.rs:282-292
Timestamp: 2025-07-15T08:01:38.534Z
Learning: In the rainlanguage/rain.orderbook codebase, findolor prefers to avoid concurrency optimizations like using `futures::future::try_join_all` for parallel processing of balance changes, considering such optimizations "not that critical at the moment" when the performance impact is minimal.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-10-06T11:44:07.888Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2145
File: crates/common/src/raindex_client/local_db/query/create_tables/query.sql:71-72
Timestamp: 2025-10-06T11:44:07.888Z
Learning: The local DB feature in the rain.orderbook codebase is not live yet (as of PR #2145), so schema migrations for existing databases are not required when modifying table structures in `crates/common/src/raindex_client/local_db/query/create_tables/query.sql`.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
🧬 Code graph analysis (5)
crates/common/src/raindex_client/local_db/pipeline/runner/config.rs (1)
crates/common/src/local_db/pipeline/runner/utils.rs (2)
  • build_runner_targets (57-100)
  • parse_runner_settings (30-39)
packages/webapp/src/lib/stores/localDbStatus.ts (1)
crates/common/src/raindex_client/local_db/mod.rs (4)
  • failure (189-197)
  • failure (222-224)
  • syncing (179-187)
  • syncing (218-220)
packages/webapp/src/__tests__/Sidebar.test.ts (1)
packages/webapp/src/lib/stores/localDbStatus.ts (2)
  • networkStatuses (4-4)
  • updateNetworkStatus (6-11)
crates/common/src/raindex_client/local_db/pipeline/runner/leadership.rs (1)
crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs (6)
  • new (326-340)
  • new (568-575)
  • network_key (45-45)
  • network_key (57-59)
  • network_key (381-383)
  • network_key (598-600)
crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs (2)
crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs (11)
  • network_key (102-104)
  • chain_id (106-108)
  • new (284-287)
  • new (352-354)
  • new (382-386)
  • new (613-618)
  • new (706-711)
  • new (736-738)
  • new (818-824)
  • new (871-876)
  • from_config (81-100)
crates/common/src/raindex_client/local_db/pipeline/runner/config.rs (1)
  • from_global_settings (17-61)
⏰ 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). (18)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-rs-artifacts, true)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-static)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-wasm-artifacts)
  • GitHub Check: standard-tests (ubuntu-latest, ob-rs-test, true)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-rs-static)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-artifacts)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-wasm-browser-test)
  • GitHub Check: standard-tests (ubuntu-latest, test-js-bindings)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-wasm-test)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-test)
  • GitHub Check: test
  • GitHub Check: git-clean
  • GitHub Check: test
  • GitHub Check: test
  • GitHub Check: Deploy-Docs-Preview
  • GitHub Check: build-tauri (ubuntu-22.04, true)
  • GitHub Check: Deploy-Preview-Push
🔇 Additional comments (23)
packages/webapp/src/lib/stores/localDbStatus.ts (4)

1-2: LGTM: Imports are correct.

The imports properly bring in the necessary types from the orderbook package and Svelte store utilities.


4-4: LGTM: Store initialization is correct.

The networkStatuses store is properly initialized as an empty Map with correct typing.


6-11: LGTM: Correct Map update pattern for Svelte reactivity.

The function properly creates a new Map instance after updating, which is essential for triggering Svelte's reactivity system. This is the correct pattern for mutable data structures in Svelte stores.


33-33: LGTM: Export alias maintains backward compatibility.

Exporting localDbStatus as an alias for aggregateStatus allows existing code to continue using the familiar name while the internal implementation has changed to support per-network tracking.

packages/webapp/src/__tests__/Sidebar.test.ts (3)

5-5: LGTM: Import updated correctly.

The test now imports the new networkStatuses and updateNetworkStatus exports, aligning with the refactored store API.


58-58: LGTM: Test cleanup updated correctly.

The cleanup now properly resets networkStatuses to an empty Map, ensuring test isolation with the new store structure.


142-148: LGTM: Test updated to use new API correctly.

The test properly uses updateNetworkStatus with a complete NetworkSyncStatus payload including all required fields (networkKey, chainId, status, schedulerState, error).

packages/webapp/src/routes/+layout.svelte (3)

22-22: LGTM: Import updated correctly.

The import change from localDbStatus to updateNetworkStatus aligns with the new callback-based pattern where the Rust scheduler invokes the callback with per-network status updates.


42-42: LGTM: Callback pattern implemented correctly.

Passing updateNetworkStatus to startLocalDbScheduler enables the Rust scheduler to emit per-network status updates back to the webapp, as specified in the PR objectives.


45-61: LGTM: Error handling updated correctly.

The error handling properly uses updateNetworkStatus with appropriate field values. Using 'unknown' for networkKey and 0 for chainId as sentinel values is safe—these values are stored internally in the localDbStatus store but never exposed to UI components. The aggregateStatus derived store only processes the status field, and LocalDbStatusCard consumes only the status and error fields. These sentinel values remain isolated from components that might reference networkKey or chainId.

crates/common/src/local_db/pipeline/runner/mod.rs (1)

39-44: LGTM! Clean addition of RunOutcome enum.

The RunOutcome enum elegantly extends the runner's return type to distinguish between actual run results and leadership acquisition failures. This design cleanly supports the per-network independent sync cycles objective without breaking the existing RunReport structure.

crates/common/src/raindex_client/local_db/pipeline/runner/config.rs (1)

68-185: Solid test coverage for NetworkRunnerConfig.

The tests thoroughly cover the filtering logic, error handling for unknown networks, and target construction. The test fixture sample_settings_yaml() provides realistic configuration for validation.

crates/common/src/raindex_client/local_db/pipeline/runner/leadership.rs (2)

56-79: Well-designed per-network leadership with clear lock naming.

The extension to support per-network Web Locks is clean:

  • network_key field enables per-network lock isolation
  • lock_name() generates deterministic, collision-free lock names
  • The cfg_attr(not(any(target_family = "wasm", test)), allow(dead_code)) annotation correctly handles the field's conditional usage

This directly addresses the PR objective of ensuring each network runs independently.


258-290: Comprehensive tests for per-network leadership.

The new tests thoroughly cover the network key handling and lock name generation, including the important case that different networks produce different lock names to ensure isolation.

crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs (3)

81-100: Clean from_config constructor for per-network runners.

The from_config constructor properly initializes network-specific fields (network_key, chain_id) from NetworkRunnerConfig, enabling per-network runners to be created with their scoped settings and targets.


110-169: Correct RunOutcome integration in run() method.

The run() method properly:

  1. Returns RunOutcome::NotLeader when leadership acquisition fails (line 117)
  2. Wraps manifest failures and execution results in RunOutcome::Report
  3. Uses has_provisioned_dumps to avoid redundant downloads on subsequent runs

The control flow ensures leadership is always checked first before any work is performed.


1148-1153: Useful unwrap_report helper for tests.

The unwrap_report helper cleanly extracts RunReport from RunOutcome::Report and panics on NotLeader, making test assertions more readable.

crates/common/src/raindex_client/local_db/mod.rs (2)

132-198: Well-designed per-network status types.

The new types effectively model per-network sync status:

  • SchedulerState cleanly distinguishes Leader vs NotLeader
  • NetworkSyncStatus aggregates all relevant per-network info
  • Constructor helpers (active, syncing, failure) provide convenient creation patterns

The assumption that syncing() and failure() always have SchedulerState::Leader is correct—you can only be syncing or failing if you're the leader.


267-280: Simplified synchronous stop.

Changing stop_local_db_scheduler from async to sync simplifies the API and aligns with the fire-and-forget stop pattern via the stop flag.

crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs (4)

82-180: Solid multi-network orchestration in start().

The refactored start() function correctly implements the per-network independent sync cycles:

  1. Deterministic ordering: Network keys are collected and sorted (lines 89-96)
  2. One-time schema init: Bootstrap runs once before spawning loops (lines 118-119)
  3. Graceful degradation: Per-network setup failures emit status and continue to other networks (lines 135-163)
  4. Independent loops: Each network gets its own spawn_network_loop call

This directly addresses the PR objective that "a slow/failing network does not block healthy networks."


196-279: Correct per-network loop implementation with leadership tracking.

The run_network_loop properly implements the per-network scheduling:

  1. Syncing emission timing: Only emits "syncing" status after confirming leadership in a previous cycle (lines 214-219), preventing spurious syncing states
  2. RunOutcome handling: Correctly distinguishes Report (was leader) from NotLeader (wasn't leader)
  3. Error handling: On error (lines 260-269), was_leader_last_cycle = true is correct because ClientRunner::run() only errors after leadership is acquired

This fulfills the requirement that "status callbacks emit per-network statuses including scheduler/leadership state."


607-646: Excellent integration test for independent network loops.

The slow_network_does_not_block_fast_networks test is a key acceptance criterion verification. It confirms that:

  • Fast networks complete multiple cycles while slow networks are still processing
  • Networks truly run independently via separate spawned loops

This directly validates the PR's core objective.


66-80: SchedulerHandle correctly exposes network keys.

The network_keys field and accessor enable callers to know which networks are being managed by the scheduler, supporting UI components that need to display per-network status.

Comment on lines +35 to +44
let chain_id = filtered_orderbooks
.values()
.next()
.map(|ob| ob.network.chain_id)
.ok_or_else(|| {
LocalDbError::CustomError(format!(
"could not determine chain_id for network: {}",
network_key
))
})?;
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Unreachable error branch in chain_id extraction.

The ok_or_else closure on lines 39-44 is unreachable because filtered_orderbooks is guaranteed non-empty after the check at lines 28-33. While this doesn't cause bugs, it's dead code.

🔎 Proposed simplification
-        let chain_id = filtered_orderbooks
-            .values()
-            .next()
-            .map(|ob| ob.network.chain_id)
-            .ok_or_else(|| {
-                LocalDbError::CustomError(format!(
-                    "could not determine chain_id for network: {}",
-                    network_key
-                ))
-            })?;
+        // Safe unwrap: filtered_orderbooks is guaranteed non-empty after the check above
+        let chain_id = filtered_orderbooks
+            .values()
+            .next()
+            .map(|ob| ob.network.chain_id)
+            .expect("filtered_orderbooks is non-empty");
📝 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.

Suggested change
let chain_id = filtered_orderbooks
.values()
.next()
.map(|ob| ob.network.chain_id)
.ok_or_else(|| {
LocalDbError::CustomError(format!(
"could not determine chain_id for network: {}",
network_key
))
})?;
// Safe unwrap: filtered_orderbooks is guaranteed non-empty after the check above
let chain_id = filtered_orderbooks
.values()
.next()
.map(|ob| ob.network.chain_id)
.expect("filtered_orderbooks is non-empty");
🤖 Prompt for AI Agents
In crates/common/src/raindex_client/local_db/pipeline/runner/config.rs around
lines 35 to 44, the ok_or_else closure used to produce a LocalDbError is
unreachable because filtered_orderbooks was already checked to be non-empty
earlier; remove the dead ok_or_else and directly extract the chain_id from the
first value (e.g. .values().next().map(|ob| ob.network.chain_id).unwrap() or
.values().next().expect("...").network.chain_id) or otherwise use expect with a
clear message referencing network_key to preserve intent.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can keep for now. we have another PR that improves more stuff

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@findolor, sounds good! I'll leave this as-is for now. Looking forward to the improvements in the upcoming PR. 👍


🧠 Learnings used
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2344
File: crates/common/src/local_db/pipeline/runner/mod.rs:18-31
Timestamp: 2025-12-03T10:40:25.429Z
Learning: In `crates/common/src/local_db/pipeline/runner/mod.rs`, the `TargetSuccess` struct does not need separate `ob_id` or `orderbook_key` fields because the contained `SyncOutcome` already includes orderbook identification information such as chain_id and orderbook_address. This avoids redundant data duplication.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2202
File: crates/common/src/raindex_client/local_db/sync.rs:33-34
Timestamp: 2025-10-14T07:51:55.148Z
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.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2237
File: crates/common/src/raindex_client/local_db/sync.rs:79-89
Timestamp: 2025-10-18T10:38:41.273Z
Learning: In `crates/common/src/raindex_client/local_db/sync.rs`, the sync_database method currently only supports indexing a single orderbook per chain ID, which is why `.first()` is used to select the orderbook configuration. Multi-orderbook support per chain ID is planned for future PRs.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1477
File: crates/settings/src/remote_networks.rs:40-75
Timestamp: 2025-04-03T09:45:04.200Z
Learning: The RemoteNetworksCfg implementation in crates/settings/src/remote_networks.rs intentionally fetches remote networks sequentially rather than in parallel.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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
Repo: rainlanguage/rain.orderbook PR: 1956
File: crates/common/src/raindex_client/mod.rs:116-135
Timestamp: 2025-07-04T10:26:24.289Z
Learning: In crates/common/src/raindex_client/mod.rs, the get_multi_subgraph_args method intentionally treats Some(empty vector) the same as None for chain_ids parameter. Both cases should return all networks to support UI behavior where no selection or empty selection means "show all networks". Only when specific chain IDs are provided should the results be filtered.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1994
File: crates/common/src/raindex_client/vaults.rs:282-292
Timestamp: 2025-07-15T08:01:38.534Z
Learning: In the rainlanguage/rain.orderbook codebase, findolor prefers to avoid concurrency optimizations like using `futures::future::try_join_all` for parallel processing of balance changes, considering such optimizations "not that critical at the moment" when the performance impact is minimal.

Learnt from: 0xgleb
Repo: rainlanguage/rain.orderbook PR: 1911
File: crates/common/src/raindex_client/orders.rs:720-720
Timestamp: 2025-07-21T16:34:04.947Z
Learning: In the rainlanguage/rain.orderbook codebase, user 0xgleb prefers using `.unwrap()` in test code rather than `.expect()` with descriptive messages, considering the direct unwrap approach acceptable for test contexts where failures should be fast and clear.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1956
File: crates/common/src/raindex_client/add_orders.rs:132-132
Timestamp: 2025-07-04T10:24:29.750Z
Learning: In the rain.orderbook codebase, findolor prefers to use `unwrap_or("")` for optional frontmatter in dotrain parsing, allowing empty strings to be handled by downstream validation in `NewConfig::try_from_yaml()` rather than adding explicit early validation checks at the extraction point.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1941
File: crates/settings/src/yaml/orderbook.rs:112-123
Timestamp: 2025-06-20T07:26:50.488Z
Learning: In crates/settings/src/yaml/orderbook.rs, the user prefers to keep the get_network_by_chain_id implementation simple with O(n) lookup rather than adding caching complexity for performance optimization, even if the method might be called frequently.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1938
File: crates/settings/src/yaml/orderbook.rs:180-199
Timestamp: 2025-06-18T18:24:32.049Z
Learning: In crates/settings/src/yaml/orderbook.rs, the user prefers to avoid refactoring duplicate search logic between get_orderbook_by_address and get_orderbook_by_network_key when there are only 2 functions, indicating they would consider it if more similar functions are added in the future.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1903
File: crates/cli/src/commands/order/calldata.rs:47-57
Timestamp: 2025-06-17T16:46:19.035Z
Learning: In the CLI command `crates/cli/src/commands/order/calldata.rs`, the user prefers to let lower-level errors from `try_into_call()` bubble up when the RPCs list is empty, rather than adding early validation checks with custom error messages.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1975
File: crates/js_api/src/config.rs:44-45
Timestamp: 2025-07-09T07:42:48.458Z
Learning: User findolor prefers to defer code improvements and refactoring suggestions to future PRs when they are not directly related to the current PR's scope, even when the suggestions are valid improvements.

Comment on lines +13 to 31
export const aggregateStatus = derived(networkStatuses, ($map) => {
if ($map.size === 0) {
return { status: 'active' as LocalDbStatus, error: undefined };
}

const statuses = Array.from($map.values());

const failure = statuses.find((s) => s.status === 'failure');
if (failure) {
return { status: 'failure' as LocalDbStatus, error: failure.error };
}

const syncing = statuses.find((s) => s.status === 'syncing');
if (syncing) {
return { status: 'syncing' as LocalDbStatus, error: undefined };
}

return { status: 'active' as LocalDbStatus, error: undefined };
});
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

LGTM: Aggregation logic is correct.

The derived store correctly implements the failure > syncing > active priority order as specified in the PR objectives. The logic properly handles the empty map case and finds the first matching status.

Optional: Consider removing explicit type assertions if LocalDbStatus is a union type

If LocalDbStatus is defined as a union type like 'active' | 'syncing' | 'failure', the type assertions may be unnecessary:

 export const aggregateStatus = derived(networkStatuses, ($map) => {
 	if ($map.size === 0) {
-		return { status: 'active' as LocalDbStatus, error: undefined };
+		return { status: 'active', error: undefined };
 	}
 
 	const statuses = Array.from($map.values());
 
 	const failure = statuses.find((s) => s.status === 'failure');
 	if (failure) {
-		return { status: 'failure' as LocalDbStatus, error: failure.error };
+		return { status: 'failure', error: failure.error };
 	}
 
 	const syncing = statuses.find((s) => s.status === 'syncing');
 	if (syncing) {
-		return { status: 'syncing' as LocalDbStatus, error: undefined };
+		return { status: 'syncing', error: undefined };
 	}
 
-	return { status: 'active' as LocalDbStatus, error: undefined };
+	return { status: 'active', error: undefined };
 });

This only applies if TypeScript can infer the type correctly without the assertions.

📝 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.

Suggested change
export const aggregateStatus = derived(networkStatuses, ($map) => {
if ($map.size === 0) {
return { status: 'active' as LocalDbStatus, error: undefined };
}
const statuses = Array.from($map.values());
const failure = statuses.find((s) => s.status === 'failure');
if (failure) {
return { status: 'failure' as LocalDbStatus, error: failure.error };
}
const syncing = statuses.find((s) => s.status === 'syncing');
if (syncing) {
return { status: 'syncing' as LocalDbStatus, error: undefined };
}
return { status: 'active' as LocalDbStatus, error: undefined };
});
export const aggregateStatus = derived(networkStatuses, ($map) => {
if ($map.size === 0) {
return { status: 'active', error: undefined };
}
const statuses = Array.from($map.values());
const failure = statuses.find((s) => s.status === 'failure');
if (failure) {
return { status: 'failure', error: failure.error };
}
const syncing = statuses.find((s) => s.status === 'syncing');
if (syncing) {
return { status: 'syncing', error: undefined };
}
return { status: 'active', error: undefined };
});
🤖 Prompt for AI Agents
In packages/webapp/src/lib/stores/localDbStatus.ts around lines 13 to 31, remove
the unnecessary explicit "as LocalDbStatus" type assertions on returned status
values (e.g., 'active' as LocalDbStatus, 'failure' as LocalDbStatus, 'syncing'
as LocalDbStatus) so TypeScript can infer the union type directly; update the
return expressions to return plain string literals and ensure the LocalDbStatus
union is imported/visible in this file so no type errors appear.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's fine we can keep this as it is

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@findolor, sounds good! The explicit type assertions provide clarity and are perfectly valid. 👍


🧠 Learnings used
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1917
File: tauri-app/src/lib/stores/order.ts:10-15
Timestamp: 2025-06-11T11:27:14.391Z
Learning: In this codebase, Svelte writable/derived stores (e.g., `subgraph` in `tauri-app/src/lib/stores/settings.ts`) expose a custom asynchronous `.load()` helper that retrieves the current value, so calls like `await subgraph.load()` are valid.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2278
File: packages/webapp/src/lib/services/startLocalDbSync.ts:23-29
Timestamp: 2025-10-29T13:29:29.578Z
Learning: In packages/webapp/src/lib/services/startLocalDbSync.ts, the hard-coded default orderbook address '0x2f209e5b67A33B8fE96E28f24628dF6Da301c8eB' is a temporary placeholder that will be removed when new sync logic is introduced in the Raindex client, as documented in the NOTE comment.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2202
File: crates/common/src/raindex_client/local_db/sync.rs:33-34
Timestamp: 2025-10-14T07:51:55.148Z
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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0fd5189 and f0e3c5a.

📒 Files selected for processing (1)
  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
🧰 Additional context used
📓 Path-based instructions (3)
crates/**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

crates/**/*.rs: For Rust crates in crates/*, run lints using nix develop -c cargo clippy --workspace --all-targets --all-features -D warnings
For Rust crates in crates/*, run tests using nix develop -c cargo test --workspace or --package <crate>

Files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
**/crates/**

📄 CodeRabbit inference engine (AGENTS.md)

Rust workspace organized as crates/* with subdirectories: cli, common, bindings, js_api, quote, subgraph, settings, math, integration_tests

Files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Rust: format code with nix develop -c cargo fmt --all
Rust: lint with nix develop -c rainix-rs-static (preconfigured flags included)
Rust: crates and modules use snake_case; types use PascalCase

Files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
🧠 Learnings (6)
📓 Common learnings
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2202
File: crates/common/src/raindex_client/local_db/sync.rs:33-34
Timestamp: 2025-10-14T07:51:55.148Z
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.
📚 Learning: 2025-12-03T10:40:25.429Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2344
File: crates/common/src/local_db/pipeline/runner/mod.rs:18-31
Timestamp: 2025-12-03T10:40:25.429Z
Learning: In `crates/common/src/local_db/pipeline/runner/mod.rs`, the `TargetSuccess` struct does not need separate `ob_id` or `orderbook_key` fields because the contained `SyncOutcome` already includes orderbook identification information such as chain_id and orderbook_address. This avoids redundant data duplication.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-10-14T07:51:55.148Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2202
File: crates/common/src/raindex_client/local_db/sync.rs:33-34
Timestamp: 2025-10-14T07:51:55.148Z
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.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-10-06T11:13:29.956Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 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/pipeline/runner/scheduler.rs
📚 Learning: 2025-10-18T10:38:41.273Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2237
File: crates/common/src/raindex_client/local_db/sync.rs:79-89
Timestamp: 2025-10-18T10:38:41.273Z
Learning: In `crates/common/src/raindex_client/local_db/sync.rs`, the sync_database method currently only supports indexing a single orderbook per chain ID, which is why `.first()` is used to select the orderbook configuration. Multi-orderbook support per chain ID is planned for future PRs.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
📚 Learning: 2025-04-03T09:45:04.200Z
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1477
File: crates/settings/src/remote_networks.rs:40-75
Timestamp: 2025-04-03T09:45:04.200Z
Learning: The RemoteNetworksCfg implementation in crates/settings/src/remote_networks.rs intentionally fetches remote networks sequentially rather than in parallel.

Applied to files:

  • crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs
⏰ 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). (16)
  • GitHub Check: build-tauri (ubuntu-22.04, true)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-legal)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-wasm-test)
  • GitHub Check: standard-tests (ubuntu-latest, ob-rs-test, true)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-wasm-artifacts)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-static)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-artifacts)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-rs-artifacts, true)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-rs-static)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-sol-test)
  • GitHub Check: git-clean
  • GitHub Check: test
  • GitHub Check: test
  • GitHub Check: test
  • GitHub Check: Deploy-Preview-Push
  • GitHub Check: Deploy-Docs-Preview
🔇 Additional comments (8)
crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs (8)

1-37: LGTM!

Imports and type definitions are well-organized. The use of Rc<Cell<bool>> for the stop flag is appropriate for the single-threaded WASM context.


66-80: LGTM!

The SchedulerHandle design is clean. The synchronous stop() method appropriately sets a flag that running loops check, and exposing network_keys provides useful visibility into the tracked networks.


82-130: Well-structured multi-network orchestration.

The start function correctly:

  • Extracts unique network keys and sorts them for deterministic ordering
  • Performs one-time schema bootstrap before spawning per-network loops
  • Shares a single stop_flag across all network loops for unified control

One minor observation: when bootstrap fails (line 125), chain_id: 0 is used as a placeholder since the failure is global and no network-specific chain ID is available yet. This is acceptable but consider if a sentinel value like u32::MAX or making chain_id optional in error states would be clearer.


131-174: LGTM!

The per-network setup correctly:

  • Creates network-specific leadership locks via with_network_key
  • Continues to the next network on configuration or runner creation failures, ensuring healthy networks proceed independently
  • Uses the appropriate chain_id (0 for config errors, actual value for runner errors)

This aligns well with the PR objective that "slow/failing networks no longer block others."


281-287: LGTM!

The emit_network_status helper is straightforward. Silently ignoring serialization and callback errors is acceptable for status emissions since these are informational and shouldn't interrupt the sync loop.


318-389: LGTM!

The RecordingRunner with its outcome queue pattern and DelayedRunner with configurable delays provide excellent test infrastructure for verifying the per-network scheduling behavior. The flexible outcome encoding (Some(true) = failure, Some(false) = success, None = NotLeader) enables precise control over test scenarios.


608-648: Excellent test coverage for the core PR objective.

The slow_network_does_not_block_fast_networks test directly validates the main motivation of this PR - that a slow/failing network doesn't block healthy networks. The test:

  • Uses DelayedRunner to simulate a slow network (100ms delay)
  • Verifies fast network completes more cycles than slow network midway through
  • Confirms both networks eventually complete cycles

This provides strong confidence that the per-network independent sync cycles work as intended.


57-63: No issues identified. The network_key() and chain_id() trait method implementations correctly delegate to the inherent methods defined on ClientRunner in runner/mod.rs (lines 102-107). Due to Rust's method resolution order, self.network_key() and self.chain_id() resolve to the inherent methods, not the trait methods, avoiding any recursion. The inherent methods access the struct fields directly (self.network_key.as_deref() and self.chain_id).

Comment on lines +196 to 279
async fn run_network_loop<R>(
mut runner: R,
db: LocalDb,
callback: Option<Rc<Function>>,
stop_flag: Rc<Cell<bool>>,
interval_ms: u32,
) where
R: SchedulerRunner + 'static,
{
let network_key = runner.network_key().unwrap_or("unknown").to_string();
let chain_id = runner.chain_id().unwrap_or(0);
let mut was_leader_last_cycle = false;

spawn_local(async move {
let mut runner = runner;
emit_status(status_callback.as_deref(), LocalDbStatusSnapshot::active());
loop {
if stop_flag_task.get() {
break;
}
loop {
if stop_flag.get() {
break;
}

if was_leader_last_cycle {
emit_network_status(
callback.as_deref(),
NetworkSyncStatus::syncing(network_key.clone(), chain_id),
);
}

emit_status(status_callback.as_deref(), LocalDbStatusSnapshot::syncing());
match runner.run_once(&db).await {
Ok(report) => {
match runner.run_once(&db).await {
Ok(outcome) => match outcome {
RunOutcome::Report(report) => {
was_leader_last_cycle = true;
if report.failures.is_empty() {
emit_status(status_callback.as_deref(), LocalDbStatusSnapshot::active());
emit_network_status(
callback.as_deref(),
NetworkSyncStatus::active(
network_key.clone(),
chain_id,
SchedulerState::Leader,
),
);
} else {
let first = &report.failures[0];
let msg = format!(
"ob {:#x} ({}) failed at {:?}: {}",
"ob {:#x} failed at {:?}: {}",
first.ob_id.orderbook_address,
first.ob_id.chain_id,
first.stage,
first.error.to_readable_msg()
);
emit_status(
status_callback.as_deref(),
LocalDbStatusSnapshot::failure(msg),
emit_network_status(
callback.as_deref(),
NetworkSyncStatus::failure(network_key.clone(), chain_id, msg),
);
}
}
Err(err) => emit_status(
status_callback.as_deref(),
LocalDbStatusSnapshot::failure(err.to_readable_msg()),
),
}

if stop_flag_task.get() {
break;
RunOutcome::NotLeader => {
was_leader_last_cycle = false;
emit_network_status(
callback.as_deref(),
NetworkSyncStatus::active(
network_key.clone(),
chain_id,
SchedulerState::NotLeader,
),
);
}
},
Err(err) => {
was_leader_last_cycle = true;
emit_network_status(
callback.as_deref(),
NetworkSyncStatus::failure(
network_key.clone(),
chain_id,
err.to_readable_msg(),
),
);
}

TimeoutFuture::new(interval_ms).await;
}

let _ = done_tx.send(());
});
if stop_flag.get() {
break;
}

SchedulerHandle {
stop_flag,
done: Some(done_rx),
TimeoutFuture::new(interval_ms).await;
}
}
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Solid per-network loop implementation with one observation.

The loop correctly handles:

  • Stop flag checks at two points for prompt termination
  • RunOutcome variants (Report with/without failures, NotLeader)
  • Error handling that continues the loop
  • Leadership state tracking via was_leader_last_cycle

Note: On the first iteration, no "syncing" status is emitted before run_once (lines 214-219 only emit if was_leader_last_cycle is true). This means the UI won't show a "syncing" indicator until after the first successful cycle completes. Consider if emitting an initial "syncing" status is desirable for immediate UI feedback.

🔎 Optional: Emit initial syncing status
     let network_key = runner.network_key().unwrap_or("unknown").to_string();
     let chain_id = runner.chain_id().unwrap_or(0);
     let mut was_leader_last_cycle = false;
+
+    // Emit initial syncing status for immediate UI feedback
+    emit_network_status(
+        callback.as_deref(),
+        NetworkSyncStatus::syncing(network_key.clone(), chain_id),
+    );
 
     loop {
         if stop_flag.get() {
             break;
         }
 
-        if was_leader_last_cycle {
-            emit_network_status(
-                callback.as_deref(),
-                NetworkSyncStatus::syncing(network_key.clone(), chain_id),
-            );
-        }
+        if was_leader_last_cycle {
+            emit_network_status(
+                callback.as_deref(),
+                NetworkSyncStatus::syncing(network_key.clone(), chain_id),
+            );
+        }
🤖 Prompt for AI Agents
In crates/common/src/raindex_client/local_db/pipeline/runner/scheduler.rs around
lines 196-279, the loop only emits a "syncing" status when was_leader_last_cycle
is true, so nothing is emitted on the very first iteration; to fix, emit an
initial NetworkSyncStatus::syncing before the first run or initialize
was_leader_last_cycle to true so the existing conditional triggers on the first
loop iteration, ensuring immediate UI feedback—either add a one-time
emit_network_status(callback.as_deref(),
NetworkSyncStatus::syncing(network_key.clone(), chain_id)) right before the loop
starts, or set let mut was_leader_last_cycle = true and keep the rest of the
loop unchanged.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is further enhanced in #2381

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch details about PR #2381 to understand the enhancements
gh pr view 2381 --repo rainlanguage/rain.orderbook --json title,body,state,url

Length of output: 4068


@findolor, thanks for the heads-up! I can see that PR #2381 provides a much more comprehensive solution with granular per-network/per-orderbook sync status, typed SyncPhase enum (FetchingLogs, Decoding, Persisting, etc.), and a fully implemented ClientStatusBus for structured status updates. That definitely addresses the initial status visibility concern and goes well beyond it with detailed phase tracking and error reporting. 👍

The enhanced UI with the expandable status cards and modal looks great for troubleshooting!


🧬 Code Graph Analysis Results

[crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs:81-100]

pub fn from_config(
        config: NetworkRunnerConfig,
        environment: RunnerEnvironment<B, W, E, T, A, S>,
        leadership: L,
    ) -> Result<Self, LocalDbError> {
        let base_targets = config.build_targets()?;

        Ok(Self {
            network_key: Some(config.network_key),
            chain_id: Some(config.chain_id),
            settings: config.settings,
            base_targets,
            manifest_map: ManifestMap::new(),
            manifests_loaded: false,
            has_provisioned_dumps: false,
            environment,
            leadership,
            leadership_guard: None,
        })
    }

[crates/common/src/raindex_client/local_db/pipeline/runner/config.rs:17-61]

pub fn from_global_settings(
        global: &ParsedRunnerSettings,
        network_key: &str,
    ) -> Result<Self, LocalDbError> {
        let filtered_orderbooks: HashMap<String, OrderbookCfg> = global
            .orderbooks
            .iter()
            .filter(|(_, ob)| ob.network.key == network_key)
            .map(|(k, v)| (k.clone(), v.clone()))
            .collect();

        if filtered_orderbooks.is_empty() {
            return Err(LocalDbError::CustomError(format!(
                "no orderbooks found for network: {}",
                network_key
            )));
        }

        let chain_id = filtered_orderbooks
            .values()
            .next()
            .map(|ob| ob.network.chain_id)
            .ok_or_else(|| {
                LocalDbError::CustomError(format!(
                    "could not determine chain_id for network: {}",
                    network_key
                ))
            })?;

        let filtered_syncs: HashMap<String, LocalDbSyncCfg> = global
            .syncs
            .iter()
            .filter(|(k, _)| *k == network_key)
            .map(|(k, v)| (k.clone(), v.clone()))
            .collect();

        Ok(Self {
            network_key: network_key.to_string(),
            chain_id,
            settings: ParsedRunnerSettings {
                orderbooks: filtered_orderbooks,
                syncs: filtered_syncs,
            },
        })
    }

[crates/common/src/raindex_client/local_db/pipeline/runner/environment.rs:13-39]

pub fn default_environment() -> RunnerEnvironment<
    ClientBootstrapAdapter,
    DefaultWindowPipeline,
    DefaultEventsPipeline,
    DefaultTokensPipeline,
    DefaultApplyPipeline,
    ClientStatusBus,
> {
    RunnerEnvironment::new(
        default_manifest_fetcher(),
        default_dump_downloader(),
        Arc::new(|target: &RunnerTarget| {
            let events =
                DefaultEventsPipeline::with_regular_rpcs(target.inputs.metadata_rpcs.clone())?;
            let tokens = DefaultTokensPipeline::new(target.inputs.metadata_rpcs.clone())?;

            Ok(EnginePipelines::new(
                ClientBootstrapAdapter::new(),
                DefaultWindowPipeline::new(),
                events,
                tokens,
                DefaultApplyPipeline::new(),
                ClientStatusBus::new(),
            ))
        }),
    )
}

[crates/common/src/raindex_client/local_db/pipeline/runner/utils.rs:30-39]

pub fn parse_runner_settings(settings_yaml: &str) -> Result<ParsedRunnerSettings, LocalDbError> {
    let orderbook_yaml = OrderbookYaml::new(
        vec![settings_yaml.to_owned()],
        OrderbookYamlValidation::default(),
    )?;
    let orderbooks = orderbook_yaml.get_orderbooks()?;
    let syncs = orderbook_yaml.get_local_db_syncs()?;

    Ok(ParsedRunnerSettings { orderbooks, syncs })
}

[crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs:102-104]

pub fn network_key(&self) -> Option<&str> {
        self.network_key.as_deref()
    }

[crates/common/src/raindex_client/local_db/pipeline/runner/mod.rs:106-108]

pub fn chain_id(&self) -> Option<u32> {
        self.chain_id
    }

🧠 Learnings used
Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2202
File: crates/common/src/raindex_client/local_db/sync.rs:33-34
Timestamp: 2025-10-14T07:51:55.148Z
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.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2237
File: crates/common/src/raindex_client/local_db/sync.rs:79-89
Timestamp: 2025-10-18T10:38:41.273Z
Learning: In `crates/common/src/raindex_client/local_db/sync.rs`, the sync_database method currently only supports indexing a single orderbook per chain ID, which is why `.first()` is used to select the orderbook configuration. Multi-orderbook support per chain ID is planned for future PRs.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1994
File: crates/common/src/raindex_client/vaults.rs:282-292
Timestamp: 2025-07-15T08:01:38.534Z
Learning: In the rainlanguage/rain.orderbook codebase, findolor prefers to avoid concurrency optimizations like using `futures::future::try_join_all` for parallel processing of balance changes, considering such optimizations "not that critical at the moment" when the performance impact is minimal.

Learnt from: brusherru
Repo: rainlanguage/rain.orderbook PR: 2044
File: tauri-app/src/routes/orders/[chainId]-[orderbook]-[orderHash]/+page.svelte:76-78
Timestamp: 2025-07-31T19:03:56.594Z
Learning: In the rainlanguage/rain.orderbook project, when the Tauri app has issues preventing proper testing, the team prefers to defer Tauri-specific implementations to separate PRs rather than including untested code, especially for financial operations like withdrawals.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1956
File: crates/common/src/raindex_client/orders.rs:609-609
Timestamp: 2025-07-04T10:27:22.544Z
Learning: In the rainlanguage/rain.orderbook codebase, user findolor prefers not to implement overflow protection for trades count casting (usize to u16) at this time, considering it unnecessary for the current scope since the practical risk of orders having 65,535+ trades is extremely low.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1956
File: crates/common/src/fuzz/mod.rs:64-64
Timestamp: 2025-07-04T09:02:57.301Z
Learning: In rainlanguage/rain.orderbook, user findolor prefers to limit type consistency changes to only the parts directly related to the current work scope. For example, when updating chain_id fields from u64 to u32 in fuzz-related code, unrelated files like tauri-app wallet commands can remain as u64 if they serve different purposes and aren't part of the current changes.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1872
File: packages/webapp/src/lib/services/handleVaultWithdraw.ts:50-54
Timestamp: 2025-06-07T05:19:04.767Z
Learning: In the rainlanguage/rain.orderbook codebase, the team prefers pragmatic code approaches over strict TypeScript patterns when the current implementation is sufficient for their use case, such as using `if (result.error)` instead of `if ('error' in result)` for discriminated union type checking.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1938
File: crates/js_api/src/raindex/mod.rs:102-118
Timestamp: 2025-06-18T12:56:44.290Z
Learning: In the rainlanguage/rain.orderbook codebase, it's acceptable to scaffold unused enum variants in initial implementation PRs when they will be implemented in future PRs, as confirmed by findolor.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 1938
File: crates/js_api/src/raindex/orders.rs:1-11
Timestamp: 2025-06-18T12:54:47.807Z
Learning: In the rain.orderbook repository, findolor prefers to keep unused imports in place when they will be used in upcoming follow-up PRs, rather than removing and re-adding them during incremental development.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2155
File: crates/common/src/raindex_client/trades.rs:133-152
Timestamp: 2025-10-06T14:13:18.531Z
Learning: In the rain.orderbook codebase, the `page` parameter in `RaindexOrder::get_trades_list` method (in crates/common/src/raindex_client/trades.rs) is kept for backwards compatibility with subgraph logic, but the LocalDb fast-path intentionally returns all trades without implementing pagination.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2001
File: crates/common/src/raindex_client/order_quotes.rs:62-69
Timestamp: 2025-07-16T14:33:13.457Z
Learning: In the rainlanguage/rain.orderbook codebase, findolor considers hardcoded decimal values (18 and 36) in order quote formatting logic to be acceptable for their use case, even when dynamic token decimals could theoretically provide more accurate formatting for different tokens.

Learnt from: findolor
Repo: rainlanguage/rain.orderbook PR: 2099
File: crates/common/src/raindex_client/sqlite_web/fetch.rs:128-133
Timestamp: 2025-08-26T11:19:30.219Z
Learning: In the rain.orderbook codebase, deterministic ordering with tie-breakers (transactionIndex, logIndex) within the same block is not required when sorting events. Block-level sorting is sufficient for the project's needs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Local DB sync: implement per-network independent sync cycles + per-network status reporting

3 participants