Replace the FoundationDB tuple layer with an own encoding that supports BigDecimal - #15
Open
tobiasmanroth wants to merge 1 commit into
Open
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The FoundationDB Java tuple layer has no BigDecimal type code, and its encoder falls back to
Number.longValue()for unknownNumbersubclasses — silently truncating BigDecimals. In our app this corrupted every:db.type/bigdec-style attribute: a discount of0.5Mwas stored as0.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.tuplenamespace implementing the tuple encoding in Clojure;dbval.dbnow uses it instead ofcom.apple.foundationdb.tuple.Tuple.0x23for BigDecimal with an order-preserving encoding (sign marker, offset-binary adjusted exponent, mantissa digits, XOR-inverted for negatives), soindex-rangeover 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.50M↔0.5M).Ratio, unknownNumbersubclasses, chars) instead of being silently truncated.org.foundationdb/fdb-javamoves from the library deps to the:devalias, where it serves as the differential-testing oracle.Testing
dbval.test.tuple: generative differential tests against fdb-java — random tuples must pack byte-identically andTuple.rangebounds must match; generative properties that BigDecimal byte order equalscompareToorder 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 — transact0.5M, read it back via entity/query/:avetlookup, numericindex-range, retraction with a different scale representation, and unsupported-type rejection.store-slatedbtests 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
0.50Mnow reads back as0.5M(and5E+2Mas500M) — numerically equal, canonical form.🤖 Generated with Claude Code