* commitproxy: skip per-mutation lock check when no locks are held
Add anyExclusiveLockHeld_ flag on RangeLock, refreshed in
consumePendingRequest after each take/release, and an early return at
the top of rejectMutationsForReadLockOnRange that bypasses the
per-transaction x per-mutation loop when the flag is false. With the
knob enabled but no exclusive read locks active (the steady state
when no bulkload is running), the commit hot path now skips the entire
scan instead of constructing a KeyRange and walking the KeyRangeMap
for every mutation in every batch.
Add RangeLockFastPath / RangeLockSlowPath counters to ProxyMetrics so
operators can confirm the optimization is firing. Verified with
tests/fast/RangeLocking.toml and tests/fast/RangeLockCycle.toml; both
pass and both counters increment as expected.
* docs: document the no-locks-held fast path and ProxyMetrics counters
Extend documentation/sphinx/source/rangelock.rst with a "Steady-state cost
when no locks are held" subsection describing the anyExclusiveLockHeld_
flag introduced in this PR and the early return in
rejectMutationsForReadLockOnRange.
Also document the two new ProxyMetrics counters (RangeLockFastPath and
RangeLockSlowPath) so operators investigating commit-proxy performance
can find them without grepping the source. The counters are the
production observability hook for the silent-degradation case where the
flag fails to clear after a lock release.
* docs: explain how anyExclusiveLockHeld_ stays consistent across proxies
The previous doc commit described the fast-path flag and its counters
but skipped the correctness argument: the flag is per-proxy, never
directly synchronized, and convergence comes from the txnStateStore
mutation broadcast that already drives coreMap consistency.
A reviewer of the patch (or anyone debugging "are these proxies'
flags actually consistent?") will ask exactly this question. Spell
out the within-batch / across-batch / recovery cases plus the
asymmetric stuck-true vs stuck-false analysis (stuck-true is harmless
extra CPU and observable via RangeLockFastPath; stuck-false would
manifest as missing transaction_rejected_range_locked rejections,
which existing simulation tests already assert against).
* Formatting
* Potential fix for pull request finding
* docs: address Copilot review on rangelock.rst possessive escaping
Use the rST backslash-space escape after inline literals where a
possessive s follows, so Sphinx renders the possessives of "V" and
"consumePendingRequest" without a visible space before the apostrophe.
* docs: explain ENABLE_READ_LOCK_ON_RANGE knob in Knobs.h
Add a trailing comment block on the knob declaration explaining that
despite the name this is a write-exclusion lock (commit proxies reject
writes to a locked range, reads are unaffected). The name reflects
bulkloads perspective rather than describing what the lock blocks.
Mirrors the substantive trailing comments on neighbouring knobs like
MAX_READ_TRANSACTION_LIFE_VERSIONS so the pattern fits in.
submitParallelRestore enforced an empty-destination precheck for all
restore modes by reading the first row of each target range. This is
correct for rangefile restore but wrong for bulkload: bulkload owns
the target range via an exclusive range lock and the storage server
clears each shard before SST ingestion, so a non-empty destination is
expected. Gate the precheck on useRangeFileRestore so bulkload skips
it.
Without this fix, "fdbrestore start --mode bulkload" against a cluster
with existing data fails with restore_destination_not_empty (2370).
The previous workaround was a manual clearrange before invoking
restore, which at large scale (>=4TB) overwhelms storage servers with
tombstones and stalls the cluster for hours.
Also document the per-shard clear-then-ingest sequence in bulkload.rst
and clarify in bulkload-user.rst that the clearrange in the quickstart
example is illustrative, not required.
Validated end-to-end at 100M scale (2026-06-04): bulkload from S3,
backup, rangefile baseline restore into validation prefix, bulkload
main restore into normal keyspace, marker-key data verification all
passed.
* fdbcli: add rangelock command for range lock management
Wraps the existing range-lock management API (registerRangeLockOwner,
takeExclusiveReadLockOnRange, etc.) so SREs can inspect and release
locks left behind by failed bulkload jobs without writing a custom
client. Subcommands: register, unregister, owners, take, release,
release-all, list. The take subcommand prints a notice that locks
only take effect when knob_enable_read_lock_on_range is set on commit
proxies, since the client cannot probe server knobs.
* docs: document fdbcli rangelock command surface
Add a "Using fdbcli" subsection to documentation/sphinx/source/rangelock.rst
describing the new fdbcli command set introduced in this PR. Lists the seven
subcommands (register/unregister/owners/take/release/release-all/list),
flags the knob_enable_read_lock_on_range advisory that `rangelock take`
prints, and notes that the existing bulkload-specific commands remain in
place as a constrained subset.
Without this update, the doc continues to point readers at the C++
ManagementAPI as the only way to drive range locking, even though fdbcli
is now a faster path for operational use.
* docs: address PR review on bulkload subcommand prefixes
The bulkload-specific commands listed in the new "Using fdbcli"
subsection were written as `bulkload addlockowner / clearlock /
printlockowner`, which reads as if only the first carries the
`bulkload` prefix. In fdbcli all three are subcommands of `bulkload`
(see fdbcli/BulkLoadCommand.cpp lines 43, 45, 46), so the correct
user-visible form is `bulkload clearlock` and `bulkload printlockowner`
rather than the bare names.
Spell out the prefix on each command for clarity.
* fdbcli: address PR review on rangelock command
- Wrap every server-side rangelock call in try/catch and only print the
success message after the call succeeds. reportRangeLockError() maps
range_lock_reject / range_unlock_reject / range_lock_failed to
user-facing messages instead of letting raw FDB errors escape.
- Validate range bounds via normalKeys.contains(KeyRangeRef(begin, end))
in a new parseNormalKeyRange() helper, replacing the inline bound
arithmetic on take/release/list.
- Add empty-ownerId check on unregister/take/release/release-all,
matching the existing check on register.
- Clarify in RANGELOCK_LIST_USAGE that BEGIN_KEY and END_KEY must be
supplied together (or both omitted).
FoundationDB switched its TLS implementation from LibreSSL to OpenSSL
in 6.2.0 (PR #2646), but documentation/sphinx/source/tls.rst still
described the implementation as LibreSSL-based in four places — the
intro paragraph, the section title, the section body, and the
"Formats" paragraph. The README and CMake setup have referenced
OpenSSL for years; this brings the user-facing TLS guide in line.
The single remaining 'LibreSSL' mention in the 6.2.0 release notes is
intentionally preserved as historical context.
Refs #3022