Conversation
wasm32-unknown-unknown has no std clock: SystemTime::now() panics with "time not implemented on this platform". Two sites read it directly and both sit in the commit path, so the first write from an embedded build panicked: - WriteTxn::commit builds CommitHistoryMeta, whose timestamp is the history entry's unix_seconds. - The age-based retention policy computes its pruning threshold from the clock on every commit. Route both through crate::clock::unix_seconds(): js_sys on the OPFS build, std elsewhere (wasm32-wasip1 included), and 0 for a wasm build without the JS bindings — the history ordering tolerates that instead of panicking. The btree allocation-cost test keeps its loop on every target but runs the wall-clock bound only where a clock exists. wasm-smoke/ is a separate crate because the pagedb dev-dependencies (tokio rt-multi-thread, tempfile) do not compile for wasm32, and Cargo builds every dev-dependency for a crate's test targets. Its two tests fail with the panic above before this change and pass after; the new `wasm-tests` CI job runs them under node, so the compile-only wasm job is no longer the last line of defence.
There was a problem hiding this comment.
Requesting changes. The fix is at the right layer, and your red/green claim holds. Two defects block merge.
Verified
| Claim | Result |
|---|---|
| Red before the fix | Reproduced. With history.rs and commit.rs reverted to main, both smoke tests fail with time not implemented on this platform. |
| Green after | Reproduced. Both tests pass under wasm-bindgen-test-runner 0.2.126. |
| Runner matches the lockfile | Yes. cargo metadata resolves wasm-bindgen 0.2.126. |
Blockers
src/clock.rs:24: the0fallback turnsRetainPolicy::AgeintoUnboundedwithout an error. See the inline note.- Same error class, outside this diff:
src/pager/core.rs:1047callstokio::time::sleepin the page-read retry loop.Db::open_observerkeepsobserver_retry_count, which defaults to 3.Db::open_existing_with_counterpart_kekalso keeps it, because it skips the mode gate inopen_with_mode.- So on wasm32, an AEAD error on a page read through either handle reaches the sleep. It does not return
Corruption. - The sleep needs a Tokio time driver. The smoke runtime is built without
enable_time(). An embedder that drives futures throughwasm-bindgen-futureshas no Tokio runtime at all. - I confirmed this by reading the code. I did not run it on wasm.
- Your PR says it removes the wasm time panics from the commit path. The read path has the same panic. Fix it here, and add a smoke test that reads a corrupted page and expects
PagedbError::Corruption.
Should-fix
These are inline. Each one is raised once.
| pub(crate) fn unix_seconds() -> u64 { | ||
| 0 |
There was a problem hiding this comment.
Blocker. 0 silently disables age retention.
- The prune loop stops at the first row where
unix_seconds >= threshold(history.rs:192). - With
now = 0,thresholdis0, so no row is ever pruned. RetainPolicy::Agethen acts asUnbounded, and history grows without limit. Nothing reports it.
The doc comment says the ordering tolerates 0. The ordering does, but the policy does not.
Correct end state: a build with no clock refuses RetainPolicy::Age at open with a typed PagedbError. The error names the policy and the missing clock. Unbounded and Count keep working, with timestamp 0.
| /// The age-based retention policy computes a threshold from the clock on | ||
| /// every commit; pruning must not panic either. | ||
| #[wasm_bindgen_test] | ||
| fn commit_under_age_retention_prunes_without_a_panic() { |
There was a problem hiding this comment.
This test passes whether or not pruning runs. It asserts only that nothing panics.
- It runs with
opfs, so the0branch inclock.rsnever executes on any CI job. - Commit twice with
RetainPolicy::Age, and assert on the retained history. The recorded timestamp must be non-zero, and old rows must be pruned. - Add a no-
opfscase that asserts the typed refusal from theclock.rsfinding.
| //! | ||
| //! `wasm32-unknown-unknown` has no std clock. `WriteTxn::commit` reads the | ||
| //! clock for the commit-history entry, and the age-based retention policy | ||
| //! reads it for the pruning threshold. Before the fix both called |
There was a problem hiding this comment.
"Before the fix both called SystemTime::now() directly" describes code that no longer exists once this merges. State the invariant the test protects: a commit must not read the std clock on wasm32-unknown-unknown.
| ] | ||
|
|
||
| [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies] | ||
| wasm-bindgen-test = "0.3" |
There was a problem hiding this comment.
No code in the pagedb crate uses wasm-bindgen-test. Only wasm-smoke does, and it declares the dependency itself. Remove this block and its Cargo.lock entries.
|
|
||
| ### Fixed | ||
|
|
||
| - **Commits no longer need a wall clock.** `WriteTxn::commit` and the age-based retention threshold read `SystemTime::now()` directly, which panics on `wasm32-unknown-unknown` ("time not implemented on this platform") — the first write from an embedded build failed. Both go through `clock::unix_seconds()` now: `js_sys::Date::now()` on the OPFS build, std elsewhere, and `0` for a wasm build without the JS bindings instead of a panic. `wasm-smoke/` commits once per policy under node so the target stays covered. |
There was a problem hiding this comment.
Remove this hunk. Changelog entries are maintainer-owned in this repo. The same hunk was removed from the other open PRs. The entry also names clock::unix_seconds(), which is a private module and not public API.
|
FYI: superseded by #44. Thanks for the review — both blockers and all four should-fix items are addressed there:
On why this arrives as a new PR: this PR's head branch lives in One gate is disclosed rather than clean: |
|
Closing as superseded by #44, which carries this work rebased onto current |
Problem
wasm32-unknown-unknownhas no std clock. Two sites read it directly, and both sit in the commit path, so the first write from an embedded build panicked:WriteTxn::commitbuildsCommitHistoryMeta, whose timestamp is the history entry'sunix_seconds(src/txn/write/commit.rs:113).src/txn/db/catalog/history.rs:151).Observed under
wasm-bindgen-test:A consumer hits this today: NodeDB-Lite's WASM test
array::create_put_slice_roundtripfails at its first write for this reason.Change
clock::unix_seconds()owns the target split:js_sys::Date::now()on the OPFS build, std everywhere else (wasm32-wasip1included), and0for a wasm build without the JS bindings — the history ordering tolerates that instead of panicking.wasm-smoke/is a separate crate because the pagedb dev-dependencies (tokiort-multi-thread, tempfile) do not compile for wasm32, and Cargo builds every dev-dependency for a crate's test targets. Two tests: a commit, and a commit under age retention.wasm-testsCI job runs them under node, so the compile-only wasm job is no longer the last line of defence.Crash point
Per CONTRIBUTING: the timestamp is metadata on the commit-history entry; the commit point is the A/B header write and is unchanged. A crash before the history write leaves the previous entry, after it the new one. Recovery does not read the clock.
Evidence
time not implemented on this platformcargo checknative /--target wasm32-unknown-unknown --features opfs/--target wasm32-wasip1unix_secondscallers (c2g, exact)Fixes #40.