Changelog
Changelog
All notable changes to fdb-record-layer-go are recorded here. Format:
Keep a Changelog; versioning per RELEASE.md
(pre-1.0 v0.MINOR.PATCH).
This project is pre-1.0. The Go API may change across minor versions; the FDB wire
format stays compatible with Java fdb-record-layer-core 4.12.11.0 across every release (the
shared-cluster hard line — see RELEASE.md). Every entry’s Compatibility block answers the four
questions a user upgrading between two refs needs: wire format, SQL behaviour, FDB client option
semantics, and required dependency versions.
This changelog starts 2026-06-20; earlier history is in git log. There are no tagged
releases yet — cutting the first v0.x tag is the maintainer’s decision (RELEASE.md §Versioning).
[Unreleased]
Added
Record-layer metrics exporter (
pkg/recordlayer/rlmetrics):StoreTimer(the port of Java’sFDBStoreTimer) rendered in the Prometheus text exposition format underfdb_recordlayer_, with zero new dependencies — the record-layer counterpart topkg/fdbgo/fdbmetrics. Timed events are summaries in seconds, counts and byte totals are counters.recordlayer.Eventgains aKind(timed / count / size) porting Java’sEvent-vs-Count(isSize)split, andStoreTimergainsAddandKeysAndValuesfrom Java. Reachable from the SQL layer viaFDBDatabase.SetTimeror, fordatabase/sql-only deployments,sqldriver.EnableStoreTimer(clusterFile)— one timer per cluster-file key (per process), with no per-tenant label by design; seedocs/mt-saas.md§4 for the cardinality reasoning.Multi-tenant SaaS operator guide (
docs/mt-saas.md): the tenancy model (one database path per tenant, subspace-per-database, and why native FDB tenants are not used), the trust boundary andRESTRICT_DDL_TO_SESSION_DATABASE, the five per-statement quotas and how to arm them (none is DSN-settable), per-tenant observability, the pure-Go TLS certificate requirement, and the tenant-facing SQL contract. Every claim carries afile:linecitation; the page is onpkg/docscheck’slivingDocslist so its version anchors are drift-guarded.Statement-wide memory byte budget for the SQL executor: opt-in
OptMaxStatementMemoryBytesbounds every cardinality-growing buffer by bytes (not just the 100k-rowMaterializationLimit); breach → SQLSTATE54F01. Default0= unlimited (RFC-130).A documentation-consistency CI guard (
pkg/docscheck) that fails the build if a living doc drifts from theMODULE.bazel/go.modversion pins or reintroduces a known contradiction (RFC-131/132).A public FDB client option matrix (
pkg/fdbgo/fdb/OPTIONS.md) classifying everySet*option as honored /UnsupportedOptionError/ safe no-op, each with itslibfdb_c7.3.77 reference, plus a completeness guard that fails CI if an option is added without a matrix row (RFC-133).A panic-boundary release gate (RFC-134): a
norecovernogo analyzer fails the build if arecover()is added outside the documented panic→error boundary allowlist (docs/panic-audit.md§2), plus docscheck guards that keep the four input-boundary fuzz nets wired and the doc in lockstep with the allowlist. Makes the “untrusted input → error, never crash” discipline self-enforcing.
Changed
- FDB C++ wire-protocol baseline bumped 7.3.75 → 7.3.77 (RFC-152). Patch bump within the 7.3
line: no wire-format, error-code,
ClientKnobs, RYW, or serialization change (regeneratedpkg/fdbgo/wire/types/*_generated.godiffer only in the version-string header comment). The only client-relevant upstream delta is PR #12935 (apeer->disconnectarm inwaitValueOrSignalsoloadBalancefails an in-flight request immediately on peer disconnect instead of waiting out the failure-monitor lag); the pure-Go client already has this structurally (single-owner connection:readLoopEOF →failConnection→failAllPending), pinned byTestPeerDisconnect_FailsInFlightReplyImmediately. No production-code behaviour change. - SQL
LIMIT/OFFSETnow flows through a single uniformRecordQueryLimitPlan+ continuation envelope, including for nested derived tables (RFC-128).
Fixed
- Record-layer instrumentation was measuring the wrong things, or nothing at all.
Counter.Incrementwrote its amount into the cumulative-value field as well as the count, so every count event reported a bogus duration (Java’sCounter.incrementtouchescountalone). The scan events timed cursor construction rather than each record produced, which for a lazy cursor measures an allocation and reports one occurrence per scan regardless of size (Java instruments the cursor’sonNext). Scan events also sat on entry points the query engine does not use, leaving the executor’s record scans, aggregate/group index scans, vector scans and primary index scans uncounted. AndEventCommitwas recorded only byCommitWithVersionstamp, so every commit on the SQL path — autocommit and explicitBeginTxalike — went unrecorded. Instrumentation only; no wire-format, plan or result change. - Legacy Java store layouts are now fully readable, writable, and auto-upgraded — closes the
FormatVersion< 6 /omit_unsplit_record_suffixwire-compatibility gap that was previously a silent data-correctness bug (Go accepted an old-format store header but only understood the modern inline layout, so it would silently fail to see a legacy store’s record versions and unsplit records). Go now mirrors Java’sFDBRecordStore.useOldVersionFormat()end-to-end: record versions are read/written in the separateRecordVersionKey(8)subspace for stores belowSAVE_VERSION_WITH_RECORD(format 6), and unsplit records are read/written at the bare primary key (no0suffix) whenomit_unsplit_record_suffixis set — across load, scan,scanRecordKeys,recordExists, save, update, delete, anddeleteRecordsWhere. On open, Go also performs Java’s transactional format upgrade (checkRebuild/addConvertRecordVersions): it bumps the storedFormatVersion, setsomit_unsplit_record_suffixfor a non-splitting store created before format 5, and moves versions from subspace 8 to their inlinepk + -1location when upgrading a splitting store past format 6. Pinned by FDB integration tests that lay down each legacy layout and assert byte-level read/write/scan/delete and migration parity with Java. (Closes theTODO.md“no read path for format-version-<6 record versions / unsplit records” gap surfaced by the RFC-131 audit.) - SQL pagination no longer treats a non-terminal
StartContinuationas end-of-results (a latent early-truncation bug); exhaustion is decided offIsEnd(), not byte-emptiness (RFC-127). - Pure-Go FDB client:
Get/GetRangeread-conflict ranges are clamped to the data actually returned and filtered through the RYW overlay, matchinglibfdb_c(no under-conflict; RFC-121). go test ./...is clean from a fresh checkout: the Bazel-runfiles-only suites are build-tagged so a plaingo testno longer panics (RFC-129), and the heavy million-row stress benchmarks underpkg/relational/sqldriver/stressnow carry astressbuild tag, so a plaingo test ./...skips them instead of spinning up million/ten-million-row FDB workloads (they still run via Bazel’smanualstress target and the nightly stress workflow).- Pure-Go FDB client: three database-level transaction defaults that change read semantics are now
honored instead of silently dropped —
SetSnapshotRywDisable/Enable(a cumulative counter, matchinglibfdb_c),SetTransactionBypassUnreadable, andSetTransactionCausalReadRisky. They propagate to each new transaction viaapplyTxDefaultsand are replayed idempotently across retries (RFC-133).
Compatibility
- Wire format: unchanged for modern stores — records, indexes, versions, continuations, and split
records remain byte-identical to Java
fdb-record-layer-core4.12.11.0. Newly closed gap: Go now reads and writes legacy Java store layouts (FormatVersion< 6 record versions in theRecordVersionKey(8)subspace, andomit_unsplit_record_suffixbare-key unsplit records) and performs Java’s on-open format upgrade — previously a silent read gap (see Fixed). A Go client can now safely share a cluster with legacy Java stores in either direction. - SQL behaviour: net additions only (memory-budget option; the LIMIT-envelope and pagination fixes correct latent bugs, they don’t change correct-query results).
- FDB client option semantics: now documented option-by-option in
pkg/fdbgo/fdb/OPTIONS.md(honored /UnsupportedOptionError/ safe no-op, vslibfdb_c7.3.77). One behavioural change: three database-level defaults (snapshot_ryw_disable/enable,transaction_bypass_unreadable,transaction_causal_read_risky) that were previously silent no-ops now take effect on each new transaction — a caller that set them and relied on them being ignored will now see them applied (this is the faithfullibfdb_cbehaviour). The unsafe access/auth/quota family still fails loud withUnsupportedOptionError; no option’s wire behaviour changed (RFC-133). - Required versions: Java
fdb-record-layer-core4.12.11.0, FDB C++ client 7.3.77, Go 1.26.x (theMODULE.bazel/go.modpins; the CI doc-guard enforces docs match them).