Skip to content

Bump rocksdb-js for concurrent commit threads; fix commit-order races - #3026

Draft
kriszyp wants to merge 1 commit into
mainfrom
kris/rocksdb-bump-pool
Draft

kriszyp wants to merge 1 commit into
mainfrom
kris/rocksdb-bump-pool

Conversation

@kriszyp

@kriszyp kriszyp commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

Draft until a rocksdb-js release exists. TODO: set @harperfast/rocksdb-js in package.json / package-lock.json to the release that contains Make optimistic commit lock buckets and validation policy configurable (HarperFast/rocksdb-js#897), Stop verification-table slot collisions from answering FRESH for another key (HarperFast/rocksdb-js#901) and Run a database's async commits on up to four concurrent commit threads (HarperFast/rocksdb-js#902). The branch holds only an empty placeholder commit until then.

Prerequisite: Deliver subscription events for commits that finish out of timestamp order (harper#3029). Merge it first. With #902's concurrent commit threads, commits to one database routinely finish out of timestamp order, and #3029 fixes the subscription paths that dropped such commits. Those fixes have moved there from this PR, so this PR no longer closes #3024 or #2933.

⊙ Problem

Harper pins @harperfast/rocksdb-js 2.10.0. On it, a database's async commits run on one commit thread, which caps commit throughput at one core of work. In rocksdb-js #898's measurements, four workers committing 64-key transactions with transaction-log entries got 4.7k commits/s on that thread, against 9.7k/s with the concurrent commit threads in #902.

❓ Your call: keep the defaults from #897 and #902 (commitThreads: 4, occValidation: 'parallel'), and drop the planned occValidation: 'serial' default? Measured under concurrent commit threads, serial validation gives up the whole gain (4.5k vs 9.9k commits/s at 64 keys), and databases with large transactions do better by raising occLockBuckets.

💡 Solution

Bump the dependency once it is released. No Harper code change is needed for the bump itself.

✅ Verification

Pending the release. I ran Harper's resources suite locally against a build of the #902 branch, with the #3029 fixes, and it passed apart from three sourceApplyConflictRetry.test.js failures that also fail on unmodified main on this machine. CI on the bumped version is the remaining check.

🤖 Generated with Claude Code (Claude Opus 5.5); posted via @kriszyp.

Related PRs: #3029 overlaps (prerequisite; holds the subscriber fixes split out of this PR)
Complexity: medium

@kriszyp kriszyp added this to the v5.4 milestone Oct 5, 2026

@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 updates the subscription delivery logic to handle out-of-order resequencing and late-committing transactions, ensuring that merged writes are correctly delivered to subscribers. It introduces helper functions for tracking merged entries and stale events, updates the audit-store notification batching to manage RocksDB iterators, and adds comprehensive tests for late-patch and late-commit scenarios. Feedback was provided regarding the need to explicitly close RocksDB iterators to prevent resource leaks and the addition of null-entry guards when iterating over audit references.

Comment thread resources/transactionBroadcast.ts Outdated
Comment on lines +66 to +71
if (auditStore.reusableIterable && !databaseSubscriptions.activeCount && !databaseSubscriptions.notifyScheduled) {
// with rocksdb-js iterator we can and should not specify a start time so we just start at the end of the txn log
// and still match older version numbers that may commit in the future. But we have to start
// immediately so we are at the right position.
databaseSubscriptions.auditLogIterator = auditStore.getRange({});
}

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.

high

When recreating the reusable RocksDB iterator (auditLogIterator), any previously opened iterator on databaseSubscriptions should be explicitly closed to prevent leaking native RocksDB iterators and snapshots. Open snapshots pin old versions of data in memory/SSTs, which can prevent compaction from reclaiming deleted space and lead to write stalls.

Suggested change
if (auditStore.reusableIterable && !databaseSubscriptions.activeCount && !databaseSubscriptions.notifyScheduled) {
// with rocksdb-js iterator we can and should not specify a start time so we just start at the end of the txn log
// and still match older version numbers that may commit in the future. But we have to start
// immediately so we are at the right position.
databaseSubscriptions.auditLogIterator = auditStore.getRange({});
}
if (auditStore.reusableIterable && !databaseSubscriptions.activeCount && !databaseSubscriptions.notifyScheduled) {
if (databaseSubscriptions.auditLogIterator?.close) {
databaseSubscriptions.auditLogIterator.close();
}
databaseSubscriptions.auditLogIterator = auditStore.getRange({});
}
References
  1. When opening multiple database handles, only track pre-loop handles (the leak fix targets) in a cleanup array, and ensure that any per-iteration handles are closed immediately within their own try/finally blocks to prevent resource exhaustion.

Comment thread resources/Table.ts Outdated
Comment on lines +6918 to +6921
entry.additionalAuditRefs?.some(
(ref: { version: number; nodeId?: number }) =>
ref.version === txnLogKey && (ref.nodeId ?? 0) === (nodeId ?? 0)
)

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.

medium

When iterating over array elements that could potentially be null or undefined, include a null-entry guard to prevent runtime TypeErrors.

					entry.additionalAuditRefs?.some(
						(ref: { version: number; nodeId?: number }) => {
							if (!ref) return false;
							return ref.version === txnLogKey && (ref.nodeId ?? 0) === (nodeId ?? 0);
						}
					)
References
  1. When iterating over array elements that could potentially be null or undefined, include a null-entry guard (e.g., if (!entry) continue;) to prevent runtime TypeErrors.

TODO: set the @harperfast/rocksdb-js version in package.json and
package-lock.json to the release containing HarperFast/rocksdb-js#897,
#901 and #902 once it is published. The subscriber fixes this bump relies
on land separately in kris/subscriber-late-commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

Subscribers miss a patch that commits after a newer write to the same record

1 participant