Skip to content

Default the RocksDB WriteBufferManager on at 1/3 of block cache#1251

Merged
kriszyp merged 2 commits into
mainfrom
kris/rocks-wbm-defaults
Jun 11, 2026
Merged

Default the RocksDB WriteBufferManager on at 1/3 of block cache#1251
kriszyp merged 2 commits into
mainfrom
kris/rocks-wbm-defaults

Conversation

@kriszyp

@kriszyp kriszyp commented Jun 11, 2026

Copy link
Copy Markdown
Member

Summary

Makes the RocksDB WriteBufferManager (WBM) on by default in core (it was opt-in). Defaults, applied in openRocksDatabase:

  • writeBufferManagerSize = blockCacheSize / 3 (floored)
  • writeBufferManagerCostToCache = true
  • writeBufferManagerAllowStall = true
  • An explicit size of 0 still disables the WBM; any explicitly configured value/boolean is honored.

The block-cache + WBM defaulting was extracted out of openRocksDatabase into a pure resolveRocksMemoryConfig helper (utility/rocksMemoryConfig.ts) so the branching is unit-testable without opening a database or hitting the process-global RocksDatabase.config.

Purpose

Bound total memtable memory across column families and apply write backpressure (allowStall) so bulk ingest can't outrun the memtable flush/conflict-check window. This is the throughput/memory half of HarperFast/harper-pro#348 (the data-safety floor — not silently dropping on retry-cap exhaustion — is a separate follow-up PR).

Where to look

  • utility/rocksMemoryConfig.ts — the defaulting rules (3-way size logic: unset → default, 0 → disabled, >0 → used).
  • resources/databases.ts — now just calls the helper.

Notes for the reviewer

  • Fabric/host-manager interaction: host-manager's compose template already sets all four STORAGE_ROCKS_* env vars explicitly (block cache 15%, WBM 5%, costToCache true, allowStall false). Those explicit values are typed and honored over these new defaults, so Fabric keeps its Phase-1 allowStall: false — no regression.
  • Deliberate call (open for input): I did not add a hardcoded fallback for availableMemory being Infinity/NaN. availableMemory = min(constrainedMemory ?? Infinity, totalmem()) is always finite because os.totalmem() is, so guarding it would be validating an unreachable state. Negative configured sizes already disable safely via the > 0 gate. Flag if you'd prefer a defensive guard anyway.

Cross-model review (Codex + Gemini/agy) run pre-PR; float-flooring, strict-assert, and edge tests were added in response.

🤖 Generated with Claude Code — model: Claude Sonnet 4.6

kriszyp and others added 2 commits June 11, 2026 13:27
Enable the WriteBufferManager by default (size = blockCacheSize / 3, costToCache
and allowStall true), bounding total memtable memory across column families and
applying write backpressure so bulk ingest can't outrun the memtable
flush/conflict-check window. An explicit size of 0 still disables it.

Extracts the block-cache/WBM defaulting out of openRocksDatabase into a pure
resolveRocksMemoryConfig helper so the branching is unit-testable without opening
a database. Part of HarperFast/harper-pro#348.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Floor the computed block-cache and WriteBufferManager sizes (RocksDB expects
integer byte counts; the percentage/third math produced fractions). Switch the
test to node:assert/strict per Harper conventions and add integer-flooring cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kriszyp kriszyp requested a review from cb1kenobi June 11, 2026 19:40
@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Reviewed; no blockers found.

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

Copy link
Copy Markdown

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 memory configuration logic by extracting it from resources/databases.ts into a dedicated, pure utility function resolveRocksMemoryConfig in utility/rocksMemoryConfig.ts, accompanied by a comprehensive suite of unit tests. The feedback recommends moving defensive runtime checks for configuration parameters like configuredBlockCacheSize and configuredWriteBufferManagerSize into the Joi validation schema to ensure proper validation and keep the resolver code clean.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +33 to +39
// Block cache: an explicit positive number wins, otherwise 25% of available memory. Floored
// because RocksDB expects integer byte counts and the percentage math produces fractions.
const blockCacheSize = Math.floor(
typeof configuredBlockCacheSize === 'number' && configuredBlockCacheSize > 0
? configuredBlockCacheSize
: availableMemory * 0.25
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Instead of adding defensive runtime checks for configuredBlockCacheSize in the resolver, ensure this configuration parameter is explicitly defined and validated in the Joi validation schema. This prevents invalid values like Infinity or non-numbers from bypassing validation while keeping the implementation free of redundant guards.

References
  1. Ensure all new configuration parameters are explicitly defined in the Joi validation schema, as they may otherwise bypass validation if allowUnknown: true is enabled, potentially leading to runtime errors.
  2. Avoid adding defensive checks or guards for states, properties, or methods that are guaranteed to exist or cannot occur in practice.

Comment on lines +40 to +44
// WriteBufferManager size: an explicit number is honored (0 disables); any other value
// (unset/misconfigured) defaults to 1/3 of the block cache. Floored for the same reason.
const writeBufferManagerSize = Math.floor(
typeof configuredWriteBufferManagerSize === 'number' ? configuredWriteBufferManagerSize : blockCacheSize / 3
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Instead of adding inline defensive checks for configuredWriteBufferManagerSize to handle negative, NaN, or Infinity values, ensure that this parameter is explicitly validated in the Joi schema. This avoids redundant runtime guards in the resolver code.

References
  1. Ensure all new configuration parameters are explicitly defined in the Joi validation schema, as they may otherwise bypass validation if allowUnknown: true is enabled, potentially leading to runtime errors.
  2. Avoid adding defensive checks or guards for states, properties, or methods that are guaranteed to exist or cannot occur in practice.

@kriszyp kriszyp marked this pull request as ready for review June 11, 2026 20:00
@kriszyp

kriszyp commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

Thanks for the review! On the suggestion to move the configuredBlockCacheSize / writeBufferManagerSize checks into the Joi schema: I'm keeping the type-enforcement in resolveRocksMemoryConfig deliberately. These values already flow through configUtils.castConfigValue (which yields proper numbers/booleans/null), and the resolver's typeof guards are defaulting logic rather than validation — they're what implement the "unset → derive from block cache, explicit 0 → disable" behavior, which is clearer co-located with the defaults it feeds. This also matches the existing block-cache resolution pattern. Open to revisiting if we want stricter rejection of malformed config at the schema layer as a separate change.

— Claude Sonnet 4.6

@kriszyp kriszyp merged commit 6911888 into main Jun 11, 2026
52 checks passed
@kriszyp kriszyp deleted the kris/rocks-wbm-defaults branch June 11, 2026 22:42
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.

2 participants