Skip to content

Conversation

@findolor
Copy link
Collaborator

@findolor findolor commented Dec 3, 2025

Motivation

See issues:

We had a problem where during the database initialization, we had to wait a little bit. Because the leadership logic was working in the same worker, we were getting an error until the database was ready. This also means that during any ongoing queries, the page wasn't loading instantly.

This PR aims to fix that by splitting the leadership and the database logic in two different workers. So the leadership selection can be made instantly regarding the database status.

Solution

fix #21

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)

Summary by CodeRabbit

  • Refactor

    • Introduced a coordinator/DB-worker architecture for more reliable worker and DB orchestration and centralized message routing.
  • New Features

    • Dual runtime modes for coordinator vs DB-only hosting.
    • Embedded DB worker support and standardized worker <-> main messaging with structured query-result delivery.
    • Default query timeout configuration exposed.
  • Tests

    • Expanded tests for coordinator/DB worker interactions, leadership flows, timeouts, and failure scenarios.
  • Breaking Changes

    • Automatic database schema creation at startup removed; create tables manually.

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

@findolor findolor self-assigned this Dec 3, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 3, 2025

Walkthrough

Introduces a coordinator/DB-worker architecture with WorkerConfig, CoordinatorState, DbWorkerState, channel-based leadership and worker messaging, DB job queuing/execution, runtime-mode dispatch (Coordinator vs DbOnly), and added LeaderReady channel message plus worker-template embedding for query timeout and embedded worker.

Changes

Cohort / File(s) Change Summary
Core Coordination Refactor
packages/sqlite-web-core/src/coordination.rs
Added WorkerConfig, CoordinatorState, DbWorkerState; channel creation, leadership acquisition via navigator.locks, leader probing, DB worker spawn/lifecycle, DB job queue/processor, unified message routing (main/db/channel), structured error/query-result helpers, and tests.
Message Enum Extension
packages/sqlite-web-core/src/messages.rs
Added LeaderReady variant to ChannelMessage (serialized as leader-ready) with (de)serialization tests.
Worker Runtime Dispatch
packages/sqlite-web/src/worker.rs
Introduced WorkerRuntime modes (Coordinator, DbOnly); added start_coordinator_runtime and start_db_only_runtime; centralized main-thread message handler; __SQLITE_DB_ONLY flag handling; updated message routing and tests.
Worker Template Generation
packages/sqlite-web/src/worker_template.rs
Inject __SQLITE_QUERY_TIMEOUT_MS and encoded __SQLITE_EMBEDDED_WORKER into generated worker prefix; tests updated to assert presence.
Packaging / Dependency
pkg/package.json
svelte-test/package.json
EOF newline formatting change in pkg/package.json; bumped @rainlanguage/sqlite-web dependency in svelte-test/package.json from alpha.7 → alpha.8.
Test App UI
svelte-test/src/routes/sql/+page.svelte
Removed automatic CREATE TABLE IF NOT EXISTS users schema setup; minor UI formatting changes.
Tests
packages/sqlite-web/src/tests.rs
Updated test to expect __SQLITE_QUERY_TIMEOUT_MS in generated worker template.

Sequence Diagram(s)

sequenceDiagram
    participant Main as Main Thread
    participant Coord as Coordinator
    participant Lock as navigator.locks
    participant Chan as BroadcastChannel
    participant DBW as DB Worker

    Main->>Coord: initialize & setup_channel_listener()
    Coord->>Chan: subscribe
    Coord->>Coord: start_leader_probe()
    Coord->>Lock: request lock (try_become_leader)

    alt Lock Acquired
        Lock->>Coord: on_lock_granted
        Coord->>Coord: promote to Leader
        Coord->>Chan: broadcast "leader-ready"
        Coord->>DBW: spawn_db_worker()
        DBW->>DBW: init SQLiteDatabase
        DBW->>Coord: post worker-ready
        Coord->>Coord: mark db_worker_ready
    else Lock Not Acquired
        Coord->>Coord: remain Follower
        Coord->>Chan: wait for "leader-ready"
    end

    Main->>Coord: forward_query_to_db(origin, request)
    Coord->>Coord: enqueue or forward based on role
    Coord->>DBW: post QueryRequest
    DBW->>DBW: exec_on_db
    DBW->>Coord: post QueryResponse (query-result)
    Coord->>Main: deliver result (direct or forwarded)
Loading
sequenceDiagram
    participant Main as Main Thread
    participant Worker as Worker (DB-Only)
    participant DB as SQLiteDatabase

    Main->>Worker: init (DB-only)
    Worker->>Worker: detect __SQLITE_DB_ONLY
    Worker->>Worker: create DbWorkerState
    Worker->>DB: initialize DB
    Worker->>Main: send worker-ready

    Main->>Worker: send WorkerMessage (query)
    Worker->>Worker: enqueue DbJob
    Worker->>DB: exec_on_db(job)
    Worker->>Main: send QueryResponse (query-result)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review leadership/lock acquisition and closure lifecycle in coordination.rs.
  • Verify message routing origins (Local vs Forwarded) and serialization shapes.
  • Inspect DB worker queueing, restart/retry behavior, and error propagation.
  • Validate runtime-mode branching and main-thread handler wiring in worker.rs.

Possibly related PRs

Suggested reviewers

  • hardyjosh
  • 0xgleb

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.76% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Linked Issues check ❓ Inconclusive The PR implements the worker architecture split but only partially addresses issue #21's requirements for explicit readiness signaling, immediate initialization errors, and configurable timeouts. Verify that SQLiteWasmDatabase.ready() hook is exposed in the JS API, InitializationPending error is returned immediately when no leader exists, and follower timeout is configurable via WorkerConfig.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main architectural change: splitting leadership and database logic into two separate workers.
Out of Scope Changes check ✅ Passed All changes directly support the two-worker architecture split and coordinating leadership selection independently of database initialization.
✨ 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 leadership-selection

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69d9b9b and fd8c2f7.

⛔ Files ignored due to path filters (1)
  • svelte-test/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • packages/sqlite-web-core/src/coordination.rs (4 hunks)
  • packages/sqlite-web/src/worker_template.rs (2 hunks)
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/worker.rs : Keep worker.rs as the main worker entry point invoked by worker_main()
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 9
File: packages/sqlite-web-core/src/coordination.rs:52-65
Timestamp: 2025-09-15T06:11:31.781Z
Learning: In packages/sqlite-web-core/src/coordination.rs, the pattern of using `db.borrow_mut().take()` followed by async database operations and then putting the database back with `*db.borrow_mut() = Some(database)` is safe in their use case and doesn't cause concurrent access issues or "Database not initialized" errors.
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/worker_template.rs : Ensure the worker JavaScript generated by worker_template.rs embeds the WASM (base64) and does not fetch external WASM files

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/embedded_worker.js : Do not manually edit embedded_worker.js; it is generated by the build and should be treated as read-only

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/worker.rs : Keep worker.rs as the main worker entry point invoked by worker_main()

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/messages.rs : Use structured message types for worker communication as defined in messages.rs

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/database_functions.rs : Integrate and expose rain.math.float-backed custom database functions for SQLite queries

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/lib.rs : Expose an async, promise-based query interface from the SQLiteWasmDatabase public API

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-15T06:11:31.781Z
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 9
File: packages/sqlite-web-core/src/coordination.rs:52-65
Timestamp: 2025-09-15T06:11:31.781Z
Learning: In packages/sqlite-web-core/src/coordination.rs, the pattern of using `db.borrow_mut().take()` followed by async database operations and then putting the database back with `*db.borrow_mut() = Some(database)` is safe in their use case and doesn't cause concurrent access issues or "Database not initialized" errors.

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-08-27T05:55:40.481Z
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 5
File: packages/sqlite-web-core/src/database_functions/bigint_sum.rs:76-93
Timestamp: 2025-08-27T05:55:40.481Z
Learning: In the sqlite-web-core codebase, for SQLite aggregate function context initialization, the maintainer prefers using byte-scanning to detect zero-initialized memory rather than using explicit initialization flags or Option wrappers.

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Maintain self-contained workers with no external WASM file dependencies across the project

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-19T07:06:20.173Z
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 14
File: svelte-test/tests/database-functions/float-is-zero.test.ts:39-57
Timestamp: 2025-09-19T07:06:20.173Z
Learning: PRAGMA function_list is not available in the WASM SQLite test environment used by svelte-test, so availability testing should rely on direct function calls rather than PRAGMA queries.

Applied to files:

  • packages/sqlite-web/src/worker_template.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: test
  • GitHub Check: test
🔇 Additional comments (8)
packages/sqlite-web/src/worker_template.rs (1)

37-44: Tests correctly assert new timeout and embedded-worker globals.

The additional assertions for __SQLITE_QUERY_TIMEOUT_MS and __SQLITE_EMBEDDED_WORKER solidly pin the template contract and guard future regressions.

packages/sqlite-web-core/src/coordination.rs (7)

32-82: Config extraction cleanly separates follower and query timeouts.

WorkerConfig plus worker_config_from_global give you a required, trimmed db_name and two independently configurable timeouts with sane defaults, while ignoring invalid/negative/non-finite values. This directly supports separating leader-election timeout from query forwarding behavior.


90-124: DB worker hooks + queueing correctly serialize DB access.

The DbWorkerHooks abstraction, DbWorkerState’s db_queue/db_processing flags, and the async loop in start_queue_processor ensure only one job runs at a time while still allowing jobs to be queued freely. The exec_on_db implementation uses the established db.borrow_mut().take() / restore pattern and is exercised by the serialization test with the busy_flag, which would catch accidental concurrency. Based on learnings, this pattern is considered safe in this codebase.

Also applies to: 683-769, 901-919, 1241-1302


302-355: DB worker spawn path and preamble align with the embedded-worker contract.

spawn_db_worker reading __SQLITE_EMBEDDED_WORKER as a string, combining it with build_worker_preamble (including __SQLITE_DB_ONLY, db name, and both timeouts), and instantiating via an in-memory blob matches the template behavior from worker_template.rs and keeps the DB worker self-contained with no external WASM fetches. Errors on missing/invalid script are surfaced clearly via worker-error messages.


361-414: DB worker readiness and failure handling look robust.

On WorkerReady, you flip db_worker_ready, mark the leader ready, reset restart attempts, broadcast LeaderReady, and call signal_ready_once, which neatly ties DB readiness to the public “ready” signal. On worker errors, handle_db_worker_failure resets readiness flags, terminates the worker, drains and fails all pending DB requests, and caps respawns using MAX_DB_WORKER_RESPAWNS, with behavior validated by the failure and retry-limit tests.


416-467: ExecuteQuery routing now fails fast on init and uses a dedicated query timeout.

Both leader and follower branches now return WORKER_ERROR_TYPE_INITIALIZATION_PENDING immediately when the DB worker or leader isn’t ready, instead of waiting on the follower timeout. Once a leader is ready, follower queries are forwarded over the channel with per-query tracking and a separate query_timeout_ms watchdog, while DB results are routed back via db_pending/follower_pending and handle_db_query_result. This matches the PR objective to avoid the 5s startup hang and to make the query timeout independently tunable.

Also applies to: 470-537, 541-666


771-786: Structured error and result messaging is consistent across paths.

make_structured_error and make_query_result_message produce a uniform { type, message } error payload wrapped in a query-result envelope, while parse_worker_error_payload and error_payload_to_string handle both well-formed and minimal worker-error messages gracefully. send_query_result_to_main and deliver_db_result centralize delivery through post_worker_message, keeping main-thread consumption consistent.

Also applies to: 792-806, 808-826, 828-845, 847-886, 888-895


986-1007: Tests give strong coverage of config, leadership, failure, and queue behavior.

The added tests validate custom/default timeouts, leader/ready broadcasts, lock-acquisition failure semantics, DB worker failure propagation and respawn limit, and DB queue serialization via injectable hooks. Together they exercise the critical coordinator/DB-worker interactions introduced in this PR and substantially reduce regression risk.

Also applies to: 1009-1049, 1051-1101, 1103-1144, 1146-1213, 1215-1238, 1241-1302


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

Copy link

@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: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6515667 and f14bd70.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • svelte-test/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • packages/sqlite-web-core/src/coordination.rs (4 hunks)
  • packages/sqlite-web-core/src/messages.rs (2 hunks)
  • packages/sqlite-web-core/src/worker.rs (1 hunks)
  • packages/sqlite-web/src/worker_template.rs (2 hunks)
  • pkg/package.json (1 hunks)
  • svelte-test/package.json (1 hunks)
  • svelte-test/src/routes/sql/+page.svelte (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/sqlite-web-core/src/messages.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Use structured message types for worker communication as defined in messages.rs

Files:

  • packages/sqlite-web-core/src/messages.rs
packages/sqlite-web-core/src/worker.rs

📄 CodeRabbit inference engine (CLAUDE.md)

Keep worker.rs as the main worker entry point invoked by worker_main()

Files:

  • packages/sqlite-web-core/src/worker.rs
svelte-test/package.json

📄 CodeRabbit inference engine (CLAUDE.md)

In the Svelte test app, depend on the sqlite-web package via the locally packed tarball produced by npm pack

Files:

  • svelte-test/package.json
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/worker.rs : Keep worker.rs as the main worker entry point invoked by worker_main()
📚 Learning: 2025-09-17T08:04:44.062Z
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 6
File: svelte-test/tests/database-functions/float-sum.test.ts:78-86
Timestamp: 2025-09-17T08:04:44.062Z
Learning: In svelte-test/tests/database-functions/float-sum.test.ts, the schema defines `amount TEXT NOT NULL` but the NULL value handling tests attempt to insert NULL values, creating a constraint violation that prevents the tests from passing.

Applied to files:

  • svelte-test/src/routes/sql/+page.svelte
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/worker_template.rs : Ensure the worker JavaScript generated by worker_template.rs embeds the WASM (base64) and does not fetch external WASM files

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/worker.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/embedded_worker.js : Do not manually edit embedded_worker.js; it is generated by the build and should be treated as read-only

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/worker.rs
  • svelte-test/package.json
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/worker.rs : Keep worker.rs as the main worker entry point invoked by worker_main()

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/worker.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/messages.rs : Use structured message types for worker communication as defined in messages.rs

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/messages.rs
  • packages/sqlite-web-core/src/worker.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Maintain self-contained workers with no external WASM file dependencies across the project

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/worker.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/database_functions.rs : Integrate and expose rain.math.float-backed custom database functions for SQLite queries

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/lib.rs : Expose an async, promise-based query interface from the SQLiteWasmDatabase public API

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-19T07:06:20.173Z
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 14
File: svelte-test/tests/database-functions/float-is-zero.test.ts:39-57
Timestamp: 2025-09-19T07:06:20.173Z
Learning: PRAGMA function_list is not available in the WASM SQLite test environment used by svelte-test, so availability testing should rely on direct function calls rather than PRAGMA queries.

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-15T06:11:31.781Z
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 9
File: packages/sqlite-web-core/src/coordination.rs:52-65
Timestamp: 2025-09-15T06:11:31.781Z
Learning: In packages/sqlite-web-core/src/coordination.rs, the pattern of using `db.borrow_mut().take()` followed by async database operations and then putting the database back with `*db.borrow_mut() = Some(database)` is safe in their use case and doesn't cause concurrent access issues or "Database not initialized" errors.

Applied to files:

  • packages/sqlite-web-core/src/worker.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to svelte-test/package.json : In the Svelte test app, depend on the sqlite-web package via the locally packed tarball produced by npm pack

Applied to files:

  • svelte-test/package.json
📚 Learning: 2025-08-27T05:59:04.254Z
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 5
File: svelte-test/tests/database-functions/bigint-sum.test.ts:2-7
Timestamp: 2025-08-27T05:59:04.254Z
Learning: In the sqlite-web repository, the user prefers to keep .js extensions when importing TypeScript files in test files, even in Vitest environments. This is acceptable and should not be flagged as an issue.

Applied to files:

  • svelte-test/package.json
📚 Learning: 2025-08-21T15:09:22.737Z
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 2
File: flake.nix:59-68
Timestamp: 2025-08-21T15:09:22.737Z
Learning: In the sqlite-web project, the build-submodules task in flake.nix successfully builds the lib/rain.math.float submodule with `forge build` even if foundry.toml is not immediately visible in the expected location. The build process works as intended according to the project maintainer.

Applied to files:

  • svelte-test/package.json
🧬 Code graph analysis (2)
packages/sqlite-web-core/src/worker.rs (1)
packages/sqlite-web-core/src/coordination.rs (5)
  • send_worker_error (743-751)
  • worker_config_from_global (33-70)
  • new (112-128)
  • new (617-624)
  • js_sys (893-894)
packages/sqlite-web-core/src/coordination.rs (3)
packages/sqlite-web-core/src/util.rs (4)
  • sanitize_identifier (5-19)
  • js_value_to_string (39-60)
  • value (44-44)
  • value (50-50)
packages/sqlite-web-core/src/worker.rs (1)
  • serde_wasm_bindgen (34-34)
packages/sqlite-web/src/worker.rs (1)
  • serde_wasm_bindgen (57-57)
🔇 Additional comments (31)
pkg/package.json (1)

15-15: Trailing newline cleanup looks good.

Minor formatting improvement with no functional impact.

svelte-test/src/routes/sql/+page.svelte (1)

76-79: Minor indentation changes look fine.

The formatting adjustments in the query section header are consistent with the rest of the file structure.

svelte-test/package.json (1)

44-44: Version bump to alpha.8 aligns with the new coordinator architecture.

The dependency correctly uses the local tarball format as per coding guidelines.

packages/sqlite-web/src/worker_template.rs (2)

7-15: Embedding worker body twice is intentional for DB worker spawning.

The embedded_worker.js is included both as a JSON-encoded string (for self.__SQLITE_EMBEDDED_WORKER) and as the executable body. This enables the coordinator to spawn embedded DB workers from the stored blob while also executing as the main worker. The approach aligns with the self-contained worker requirement.


36-39: Test coverage for the new embedded worker global is adequate.

packages/sqlite-web-core/src/worker.rs (7)

13-20: Clean runtime enum and thread-local storage design.

The WorkerRuntime enum with Coordinator and DbOnly variants cleanly separates the two runtime modes. Using thread_local! with const initialization is idiomatic for WASM single-threaded contexts.


22-28: Correct fallback handling for missing flag.

The is_db_only_mode() function safely defaults to false when the global is missing or not a boolean.


30-55: Well-structured message dispatch with proper error handling.

The handler correctly:

  • Parses incoming messages into structured WorkerMessage types (per coding guidelines)
  • Routes messages to the appropriate runtime via pattern matching
  • Reports parsing errors back to the main thread
  • Uses Closure::forget() appropriately for persistent WASM event handlers

57-69: Coordinator runtime initialization sequence is well-ordered.

The startup correctly initializes state and channel communication before installing the message handler, ensuring the runtime is ready to process messages.


71-79: DB-only runtime startup is appropriately simpler.

The initialization correctly skips leadership coordination, focusing only on database setup.


82-91: Entry point correctly maintains worker.rs as main entry invoked by worker_main().

The main() function cleanly separates configuration retrieval from runtime selection, following the coding guidelines. Based on learnings, this keeps worker.rs as the main worker entry point.


101-116: Tests adequately cover the db_only_mode flag behavior.

The tests verify both the default (false) and explicit (true) cases, with proper cleanup using Reflect::delete_property.

packages/sqlite-web-core/src/messages.rs (2)

25-29: LeaderReady variant follows established patterns.

The new LeaderReady variant correctly mirrors the NewLeader structure with consistent serde attributes (leader-ready tag, leaderId camelCase field). This enables the coordinator to signal when the leader is ready, supporting the PR's goal of decoupling leadership from database initialization.


222-227: Test coverage for LeaderReady serialization is adequate.

The roundtrip test verifies correct JSON structure and deserialization. The assert_serialization_roundtrip helper ensures both directions work correctly.

packages/sqlite-web-core/src/coordination.rs (17)

1-31: LGTM!

The imports are appropriate for the coordinator functionality, and the LeadershipRole enum and WorkerConfig struct are well-defined with clear semantics.


33-70: LGTM!

The configuration extraction logic properly validates inputs with sensible defaults. The test fallback for db_name and the finite/non-negative check for follower_timeout_ms are appropriate.


72-104: LGTM!

The data structures are well-designed. DbRequestOrigin cleanly separates local vs forwarded query tracking, and the use of Rc<RefCell<...>> is appropriate for the single-threaded WASM async context.


111-141: LGTM!

The initialization and channel listener setup follow standard patterns for WASM. The Closure::forget() pattern is appropriate for long-lived event handlers.


143-201: LGTM!

The leader probe logic correctly handles both finite and infinite timeouts, with proper polling intervals and timeout error reporting.


203-238: LGTM!

The Web Locks API is used correctly for leader election. The never-resolving promise pattern (Promise::new(&mut |_, _| {})) ensures the lock is held for the lifetime of the leader worker.


240-297: LGTM!

The lock grant handler and DB worker spawning logic are well-implemented. The Blob + URL pattern for worker creation with injected configuration is appropriate, and the URL is correctly revoked after use.


299-347: LGTM!

The DB worker event handling and failure recovery are well-designed. The failure handler properly resets state, terminates the failed worker, fails pending queries, and attempts automatic recovery through respawn.


403-472: LGTM!

The channel message handling is comprehensive. The optimization where followers can also respond to LeaderPing with cached LeaderReady information helps reduce leader discovery time.


474-538: LGTM!

The query forwarding logic is well-implemented with proper ID generation (avoiding 0 via wrapping_add(1).max(1)) and comprehensive error handling that properly routes failures back to their origin.


540-614: LGTM!

The helper methods are well-implemented. fail_origin properly routes errors based on query origin, and signal_ready_once correctly implements idempotent ready signaling.


616-694: LGTM!

The DbWorkerState implementation correctly handles serial query processing. The queue processor pattern using Cell<bool> for the processing flag is appropriate for the single-threaded WASM context, and the post-loop check (lines 689-691) handles items enqueued during processing. Based on learnings, the db.borrow_mut().take() pattern is safe in this codebase.


696-770: LGTM!

The messaging helper functions provide a clean abstraction for worker communication with consistent error handling and message formatting.


772-841: LGTM!

The query result message construction is well-structured, and the test double for deliver_db_result (lines 828-841) enables unit testing without actual worker message posting.


843-884: LGTM!

The exec_on_db implementations are well-designed. The production version correctly handles the database lifecycle, and the test double provides useful concurrent access detection for testing the queue serialization.


886-923: LGTM!

The sleep_ms implementation robustly handles different execution contexts (worker vs window) with an immediate fallback if no timer API is available.


925-1206: LGTM!

The tests provide good coverage of key scenarios including leader election, ping responses based on readiness state, failure recovery, and queue serialization. The test isolation techniques (stubbing navigator.locks.request, using global flags for fake DB) are appropriate.

@findolor findolor requested review from 0xgleb and hardyjosh December 3, 2025 09:43
Copy link

@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: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f14bd70 and 69d9b9b.

⛔ Files ignored due to path filters (1)
  • svelte-test/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • packages/sqlite-web-core/src/coordination.rs (4 hunks)
  • packages/sqlite-web/src/tests.rs (1 hunks)
  • packages/sqlite-web/src/worker_template.rs (2 hunks)
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/worker.rs : Keep worker.rs as the main worker entry point invoked by worker_main()
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 9
File: packages/sqlite-web-core/src/coordination.rs:52-65
Timestamp: 2025-09-15T06:11:31.781Z
Learning: In packages/sqlite-web-core/src/coordination.rs, the pattern of using `db.borrow_mut().take()` followed by async database operations and then putting the database back with `*db.borrow_mut() = Some(database)` is safe in their use case and doesn't cause concurrent access issues or "Database not initialized" errors.
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/messages.rs : Use structured message types for worker communication as defined in messages.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/worker_template.rs : Ensure the worker JavaScript generated by worker_template.rs embeds the WASM (base64) and does not fetch external WASM files

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web/src/tests.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/embedded_worker.js : Do not manually edit embedded_worker.js; it is generated by the build and should be treated as read-only

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web/src/tests.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/worker.rs : Keep worker.rs as the main worker entry point invoked by worker_main()

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web/src/tests.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/messages.rs : Use structured message types for worker communication as defined in messages.rs

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web/src/tests.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web-core/src/database_functions.rs : Integrate and expose rain.math.float-backed custom database functions for SQLite queries

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web/src/tests.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Maintain self-contained workers with no external WASM file dependencies across the project

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-17T06:55:20.178Z
Learnt from: CR
Repo: rainlanguage/sqlite-web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-17T06:55:20.178Z
Learning: Applies to packages/sqlite-web/lib.rs : Expose an async, promise-based query interface from the SQLiteWasmDatabase public API

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web/src/tests.rs
  • packages/sqlite-web-core/src/coordination.rs
📚 Learning: 2025-09-19T07:06:20.173Z
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 14
File: svelte-test/tests/database-functions/float-is-zero.test.ts:39-57
Timestamp: 2025-09-19T07:06:20.173Z
Learning: PRAGMA function_list is not available in the WASM SQLite test environment used by svelte-test, so availability testing should rely on direct function calls rather than PRAGMA queries.

Applied to files:

  • packages/sqlite-web/src/worker_template.rs
  • packages/sqlite-web/src/tests.rs
📚 Learning: 2025-09-15T10:01:17.743Z
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 12
File: svelte-test/tests/fixtures/test-helpers.ts:11-11
Timestamp: 2025-09-15T10:01:17.743Z
Learning: In the sqlite-web project, test database names in createTestDatabase function do not need timestamp suffixes for uniqueness. The maintainer has confirmed that test isolation via unique naming is not required.

Applied to files:

  • packages/sqlite-web/src/tests.rs
📚 Learning: 2025-09-15T06:11:31.781Z
Learnt from: findolor
Repo: rainlanguage/sqlite-web PR: 9
File: packages/sqlite-web-core/src/coordination.rs:52-65
Timestamp: 2025-09-15T06:11:31.781Z
Learning: In packages/sqlite-web-core/src/coordination.rs, the pattern of using `db.borrow_mut().take()` followed by async database operations and then putting the database back with `*db.borrow_mut() = Some(database)` is safe in their use case and doesn't cause concurrent access issues or "Database not initialized" errors.

Applied to files:

  • packages/sqlite-web-core/src/coordination.rs
🧬 Code graph analysis (1)
packages/sqlite-web-core/src/coordination.rs (3)
packages/sqlite-web-core/src/util.rs (4)
  • sanitize_identifier (5-19)
  • js_value_to_string (39-60)
  • value (44-44)
  • value (50-50)
packages/sqlite-web-core/src/worker.rs (1)
  • serde_wasm_bindgen (34-34)
packages/sqlite-web/src/worker.rs (1)
  • serde_wasm_bindgen (57-57)
⏰ 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: test
  • GitHub Check: test
🔇 Additional comments (7)
packages/sqlite-web/src/tests.rs (1)

136-139: LGTM! Test correctly validates query timeout embedding.

The assertion properly verifies that the new __SQLITE_QUERY_TIMEOUT_MS configuration is embedded in the generated worker template, consistent with the broader PR changes introducing separate query timeout handling.

packages/sqlite-web-core/src/coordination.rs (5)

30-81: LGTM! Configuration extraction is well-structured.

The WorkerConfig and worker_config_from_global properly extract configuration from JS globals with appropriate validation (finite, non-negative checks) and sensible defaults (5s follower timeout, 30s query timeout). The separate query_timeout_ms field addresses the past review concern about query timeout reusing the leader election timeout.


393-445: LGTM! Query handling correctly uses separate timeouts.

The query routing logic properly:

  • Returns InitializationPending immediately when the DB worker or leader isn't ready (lines 401-406, 411-416), providing fast feedback instead of blocking
  • Uses query_timeout_ms for follower query forwarding (line 423), addressing the past review concern about separating leader election timeout from query execution timeout
  • Tracks pending requests appropriately for both local and forwarded queries

This implementation aligns with issue #21's objective of detecting "no leader yet" and rejecting immediately with a dedicated initialization error.

Based on learnings, this addresses the past feedback about timeout separation.


660-746: LGTM! DB worker queue ensures serial execution.

The DbWorkerState implementation:

  • Uses a queue with a db_processing flag (lines 717-719) to ensure queries execute serially, preventing concurrent access issues
  • Employs the db.borrow_mut().take() pattern in exec_on_db (lines 883-891), which is documented as safe in this codebase per learnings
  • Provides a hook-based design enabling testability, as demonstrated in the comprehensive test at lines 1193-1254

Based on learnings, the db.borrow_mut().take() pattern is safe in this use case.


769-871: LGTM! Comprehensive messaging infrastructure.

The worker messaging functions provide:

  • Structured error payloads with type discrimination (lines 824-838), enabling downstream handling of WORKER_ERROR_TYPE_INITIALIZATION_PENDING vs generic errors
  • Consistent query result format (lines 840-863) with nullable result/error fields
  • Proper error propagation through serialization/posting failures

This supports the issue #21 objective of providing explicit initialization errors rather than generic timeouts.


937-1254: Excellent test coverage of coordinator and DB worker scenarios.

The test suite comprehensively validates:

  • Configuration extraction with custom and default values (lines 964-984)
  • Leadership messaging flow including NewLeader and LeaderReady (lines 987-1026)
  • Conditional responses based on DB readiness state (lines 1029-1078)
  • Lock acquisition failure handling (lines 1081-1121)
  • DB worker failure recovery, including pending request cleanup and state reset (lines 1124-1190)
  • Serial query execution without concurrency violations (lines 1193-1254)

The use of hook-based testing (lines 1196-1217) is particularly good, allowing verification of serial execution without requiring a real database.

packages/sqlite-web/src/worker_template.rs (1)

7-12: The 30-second default query timeout is appropriate.

The 30-second timeout aligns with industry standards for SQLite WASM applications. Web research shows typical query timeout ranges from 5–60 seconds depending on use case, with 30 seconds being a common choice for interactive web applications. The implementation supports custom timeouts via __SQLITE_QUERY_TIMEOUT_MS, allowing adjustment if needed for specific use cases.

Comment on lines 376 to 391
fn handle_db_worker_failure(self: &Rc<Self>, error: String) {
*self.db_worker_ready.borrow_mut() = false;
*self.leader_ready.borrow_mut() = false;
*self.ready_signaled.borrow_mut() = false;
if let Some(worker) = self.db_worker.borrow_mut().take() {
worker.terminate();
}
let _ = send_worker_error_message(&error);
let pending = self.db_pending.borrow_mut().drain().collect::<Vec<_>>();
for (_, origin) in pending {
self.fail_origin(origin, error.clone());
}
if let Err(err) = self.spawn_db_worker() {
let _ = send_worker_error_message(&js_value_to_string(&err));
}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider limiting DB worker respawn attempts.

The handle_db_worker_failure function properly cleans up state and pending requests, then immediately attempts to respawn the DB worker (line 388). If the spawn or initialization consistently fails (e.g., due to configuration or OPFS issues), this could create an infinite respawn loop.

Consider adding:

  1. A respawn attempt counter
  2. Exponential backoff between attempts
  3. A maximum retry limit before giving up and keeping the worker in a failed state

This would improve resilience for transient failures while avoiding resource exhaustion on persistent errors.

@findolor findolor merged commit f58d94d into main Dec 22, 2025
3 checks passed
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.

Handle SQLite WASM worker readiness so first queries don’t hit the 5s follower timeout

4 participants