Skip to content

Fix init-rocksdb skipping updates for pinned prerelease/revision versions - #734

Open
cb1kenobi wants to merge 3 commits into
mainfrom
fix/prerelease-version-check
Open

Fix init-rocksdb skipping updates for pinned prerelease/revision versions#734
cb1kenobi wants to merge 3 commits into
mainfrom
fix/prerelease-version-check

Conversation

@cb1kenobi

Copy link
Copy Markdown
Member

Summary

Pinning a downstream revision such as 11.1.2-1 in package.json (rocksdb.version) while 11.1.2 was already installed left the old version in place instead of downloading the pinned one.

Root cause: semver sorts a prerelease/revision suffix below the bare release — 11.1.2-1 < 11.1.2. The second check in scripts/init-rocksdb/main.ts is a "don't downgrade" guard (so ROCKSDB_VERSION=latest won't clobber a newer manually-pinned build). With a pinned 11.1.2-1, that guard saw semver.lte('11.1.2-1', '11.1.2') === true and skipped the download, treating the requested version as a downgrade. Note the first check (semver.eq) was already correct — it's not where the skip happened.

What changed

Split the version decision into two intent-named pure predicates in a new scripts/init-rocksdb/version-check.ts:

  • installedSatisfiesPin — the exact pin is already installed (full version match including the suffix, compatible runtime).
  • prebuildIsRedundant — the downgrade guard, now applied only when resolving latest (or an unset version). An explicit pin always installs exactly what was requested.

main.ts now uses these instead of inline semver calls.

Where to look

  • scripts/init-rocksdb/version-check.ts — the new logic and the reasoning (see the doc comments on why a pin bypasses the downgrade guard).
  • The behavior change: for an exact pin, the downgrade guard no longer applies, so a "sideways"/lower-sorting move like 11.1.211.1.2-1 now downloads. The latest path is unchanged.

Also fixed (latent)

The old first check ran semver.eq(currentVersion.version, 'latest'), which throws Invalid Version when ROCKSDB_VERSION=latest and a prebuild is already installed. Gating it on "is an exact pin" avoids that.

Tests

test/init-rocksdb-version-check.test.ts — 12 cases covering pin detection, exact-match/mismatch (incl. the 11.1.2 vs 11.1.2-1 regression), runtime matching, the latest downgrade guard, and an end-to-end assertion that pinning 11.1.2-1 over an installed 11.1.2 proceeds to download. Extracted as pure functions specifically so this is testable without network or process.exit.

Notes for the reviewer

  • Two related, unaddressed items I left out of scope (flag if you want them folded in): (1) getPrebuild sorts release tags with semver.rcompare, so if both v11.1.2 and v11.1.2-1 exist, the latest default still picks 11.1.2 over the "prerelease" -1; (2) the version.h fallback in get-current-version.ts can't represent a -N suffix (only MAJOR.MINOR.PATCH), so an old prebuild without rocksdb.json reports the bare version.
  • Cross-model review was not run (the cross-model tooling wasn't available in this environment); worth a second set of eyes on the latest-vs-pin semantics.

🤖 Generated with Claude Code (model: Claude Opus 4.8)

@cb1kenobi
cb1kenobi requested a review from kriszyp July 29, 2026 15:50

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the RocksDB version checking logic by extracting it into a dedicated helper module (version-check.ts) and adding comprehensive unit tests. The feedback highlights potential runtime crashes when passing unvalidated version strings to semver.eq and semver.lte, recommending validation using semver.valid and suggesting corresponding test cases to ensure robust error handling.

Comment thread scripts/init-rocksdb/version-check.ts
Comment thread scripts/init-rocksdb/version-check.ts
Comment thread test/init-rocksdb-version-check.test.ts
Comment thread test/init-rocksdb-version-check.test.ts
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

get-sync.bench.ts

getSync() > random keys - small key size (100 records)

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 lmdb 1 24.34K ops/sec 41.08 39.62 617.582 0.113 121,717
🥈 rocksdb 2 11.74K ops/sec 85.14 82.79 31,012.643 1.22 58,724

getSync() > sequential keys - small key size (100 records)

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 lmdb 1 28.61K ops/sec 34.95 33.80 506.852 0.102 143,066
🥈 rocksdb 2 11.40K ops/sec 87.73 83.16 588.008 0.054 56,995

ranges.bench.ts

getRange() > small range (100 records, 50 range)

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 lmdb 1 25.44K ops/sec 39.30 35.88 1,846.118 0.296 127,212
🥈 rocksdb 2 16.59K ops/sec 60.26 51.58 1,094.854 0.124 82,974

realistic-load.bench.ts

Realistic write load with workers > write variable records with transaction log

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 rocksdb 1 374.16 ops/sec 2,672.669 93.28 51,084.024 14.34 755
🥈 lmdb 2 26.81 ops/sec 37,292.707 462.927 1,169,240.586 135.735 64.00

transaction-log.bench.ts

Transaction log > read 100 iterators while write log with 100 byte records

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 rocksdb 1 38.17K ops/sec 26.20 12.15 410.508 0.213 190,843
🥈 lmdb 2 440.25 ops/sec 2,271.436 216.127 25,028.013 1.52 2,202

Transaction log > read one entry from random position from log with 1000 100 byte records

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 rocksdb 1 719.74K ops/sec 1.39 1.22 473.404 0.063 3,598,717
🥈 lmdb 2 445.65K ops/sec 2.24 1.09 8,667.698 0.981 2,228,270

worker-put-sync.bench.ts

putSync() > random keys - small key size (100 records, 10 workers)

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 rocksdb 1 847.24 ops/sec 1,180.303 1,026.534 2,537.802 0.380 1,695
🥈 lmdb 2 1.16 ops/sec 861,006.047 810,313.499 891,957.951 2.21 10.00

worker-transaction-log.bench.ts

Transaction log with workers > write log with 100 byte records

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 rocksdb 1 23.94K ops/sec 41.78 28.63 467.817 0.568 47,871
🥈 lmdb 2 789.59 ops/sec 1,266.48 44.82 14,536.563 6.03 1,582

Results from commit a6276f3

@cb1kenobi
cb1kenobi marked this pull request as ready for review July 29, 2026 16:38
cb1kenobi and others added 3 commits July 29, 2026 12:04
…ions

Pinning a downstream revision such as `11.1.2-1` in package.json while
`11.1.2` was installed left the old version in place. semver sorts a
prerelease/revision suffix *below* the bare release (`11.1.2-1 < 11.1.2`), so
the "don't downgrade" guard that protects the `latest` path refused to install
the explicitly-pinned version, thinking it was a downgrade.

Split the decision into two intent-named predicates (new version-check.ts):
- `installedSatisfiesPin` — an exact pin is already installed (full version
  match incl. suffix, compatible runtime).
- `prebuildIsRedundant` — the downgrade guard, now applied ONLY for `latest`
  (or unset). An explicit pin always installs exactly what was requested.

This also removes a latent crash: the old first check ran
`semver.eq(current, 'latest')`, which throws on a non-version string when
`ROCKSDB_VERSION=latest` and a prebuild is already installed.

Extracted as pure functions so the version-selection logic is unit tested
without network or process.exit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Guard installedSatisfiesPin / prebuildIsRedundant with semver.valid before
comparing: semver.eq/lte throw TypeError on a non-semver string (a corrupted
rocksdb.json or a garbage ROCKSDB_VERSION). Invalid input now returns false —
falling through to a (re)download or a clear "prebuild not found" error rather
than crashing. Adds tests covering invalid-input handling for both.

Addresses gemini-code-assist review feedback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The RocksDB prebuild may carry a downstream revision suffix (e.g. 11.1.2-1)
that package.json pins but RocksDB's own version.h cannot encode
(GetRocksVersionAsString reports 11.1.2). Compare RocksDBVersion.MatchesPackagePin
against the base MAJOR.MINOR.PATCH, stripping any -N suffix, so a revision pin
does not spuriously fail the native version check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cb1kenobi
cb1kenobi force-pushed the fix/prerelease-version-check branch from 2f80721 to c886f2b Compare July 29, 2026 18:30
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.

1 participant