Skip to content

Replace the FoundationDB tuple layer with an own encoding that supports BigDecimal - #15

Open
tobiasmanroth wants to merge 1 commit into
maxweber:mainfrom
tobiasmanroth:clojure-tuple-encoding-upstream
Open

Replace the FoundationDB tuple layer with an own encoding that supports BigDecimal#15
tobiasmanroth wants to merge 1 commit into
maxweber:mainfrom
tobiasmanroth:clojure-tuple-encoding-upstream

Conversation

@tobiasmanroth

Copy link
Copy Markdown
Contributor

Why

The FoundationDB Java tuple layer has no BigDecimal type code, and its encoder falls back to Number.longValue() for unknown Number subclasses — silently truncating BigDecimals. In our app this corrupted every :db.type/bigdec-style attribute: a discount of 0.5M was stored as 0.

Since dbval tuples are only ever read by Clojure on the JVM, the cross-language constraint that kept FoundationDB's type set minimal does not apply.

What

New dbval.tuple namespace implementing the tuple encoding in Clojure; dbval.db now uses it instead of com.apple.foundationdb.tuple.Tuple.

  • Byte-compatible with the FoundationDB layer for every type it supported (nil, bytes, strings, nested tuples, integers incl. bignums, float, double, boolean, UUID). Existing stores stay readable, no migration needed. BlobRef content hashes from the deref-values feature are byte arrays and encode unchanged.
  • New type code 0x23 for BigDecimal with an order-preserving encoding (sign marker, offset-binary adjusted exponent, mantissa digits, XOR-inverted for negatives), so index-range over bigdec attributes scans in numeric order. Values are canonicalized (trailing zeros stripped, integral values at scale 0), so numerically equal decimals encode identically — lookups and retractions match across scale representations (0.50M0.5M).
  • Unsupported value types now throw (Ratio, unknown Number subclasses, chars) instead of being silently truncated.
  • org.foundationdb/fdb-java moves from the library deps to the :dev alias, where it serves as the differential-testing oracle.

Testing

  • dbval.test.tuple: generative differential tests against fdb-java — random tuples must pack byte-identically and Tuple.range bounds must match; generative properties that BigDecimal byte order equals compareTo order and round-trips are numerically equal; unit tests for long/bignum boundary values (the generators caught that magnitudes of exactly 8 bytes beyond the long range still use the plain int type codes, not the bignum codes).
  • dbval.test.db: engine-level regression — transact 0.5M, read it back via entity/query/:avet lookup, numeric index-range, retraction with a different scale representation, and unsupported-type rejection.
  • Full suite on top of the deref-values work: 197 tests, 1193 assertions, 0 failures.
  • store-slatedb tests were not run locally (need a JDK 25 that is only inside our app container); that module's key handling is untouched — it stores opaque key bytes.

Notes

🤖 Generated with Claude Code

…ts BigDecimal

The FoundationDB Java tuple layer has no BigDecimal type code and its
encoder falls back to Number.longValue() for unknown Number subclasses,
silently truncating BigDecimal values: a 0.5M discount was stored as 0.

Since these tuples are only ever read by Clojure on the JVM, the
cross-language constraint that kept FoundationDB's type set minimal does
not apply. The new dbval.tuple namespace implements the encoding in
Clojure:

- Byte-compatible with the FoundationDB layer for every type it
  supported (nil, bytes, strings, nested tuples, integers including
  bignums, float, double, boolean, UUID), so existing stores stay
  readable. Verified generatively against fdb-java as a
  differential-testing oracle (now a :dev-only dependency).

- Adds type code 0x23 for BigDecimal with an order-preserving encoding
  (sign marker, offset-binary adjusted exponent, mantissa digits), so
  index range scans over bigdec attributes work in numeric order.
  Values are canonicalized (trailing zeros stripped, integral values at
  scale 0) so numerically equal decimals encode identically.

- Rejects unsupported value types (e.g. Ratio, unknown Number
  subclasses) with an exception instead of silently corrupting data.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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