-
Type:
Improvement
-
Resolution: Unresolved
-
Priority:
Major - P3
-
None
-
Affects Version/s: None
-
Component/s: BSON
Timestamp extends Long, so it exposes the whole 64-bit integer API: arithmetic, bitwise ops, signedness, raw low/high. A BSON Timestamp is just two uint32s, t (seconds since epoch) and i (increment), and most of that API makes no sense for it. It also gives odd results, e.g. ts.add(...) returns a Long (NODE-4098).
NODE-7069 (bson 7.3.0, js-bson #887) did the groundwork with no runtime changes. It split the inherited Long members into kept and deprecated (the kept ones are the TimestampKept allowlist in src/timestamp.ts), and also deprecated MAX_VALUE, fromInt, fromNumber and TimestampOverrides.
This ticket is the breaking part for bson v8: drop the Long base class, remove what 7069 deprecated, keep the rest.
The first attempt (js-bson #606, 2023) removed comparisons as well and was closed because ts1.greaterThan(ts2) had no good replacement. 7069 keeps comparisons, so that concern is resolved.
Use Case
As a user of the BSON library
I want Timestamp to only expose what makes sense for a (t, i) pair
So that I don't misuse it as a general 64-bit integer
Unknowns
- Should comparison methods accept only Timestamp? Today they take string | number | Long | Timestamp via Long. Probably Timestamp only; the driver never compares against anything else.
- Should the constructor throw on a negative or larger-than-uint64 bigint? Today it silently wraps (new Timestamp(-1n) gives t = i = 0xffffffff). I'd throw. Signed Long input can keep being reinterpreted as unsigned bits.
User Impact
- Breaking. Everything deprecated in
NODE-7069is gone: arithmetic and bitwise ops, toNumber/toInt/toBytes*, getHighBits/getLowBits and their unsigned variants, low/high/unsigned, isZero, MAX_VALUE, fromInt, fromNumber, etc. - Long.isLong(ts) and ts instanceof Long become false.
- Comparisons, toBigInt(), toString(), t, i, fromBits and fromString keep working.
- Long methods no longer accept a Timestamp.
Dependencies
NODE-7069(done)- Ships with bson v8 (NODE-6999)
- mongosh will break on upgrade: shell-api/src/helpers.ts duck-types Timestamps with 'getHighBits' in x, and shell-bson/src/shell-bson.ts re-exports fromInt, fromNumber and MAX_VALUE.
- The driver only uses greaterThan and _bsontype on Timestamps, so it shouldn't need changes. Confirm during the v8 bump.
Acceptance Criteria
Implementation Requirements
- Timestamp extends BSONValue instead of Long. Store t and i as two uint32 numbers behind read-only getters. No need to wrap a private Long, and this saves an allocation per Timestamp (every server response has $clusterTime and operationTime).
- Remove everything in the DEPRECATED block of src/timestamp.ts, plus MAX_VALUE, fromInt, fromNumber, the TimestampOverrides, LongWithoutOverrides and TimestampKept types, and LongWithoutOverridesClass (including the exports in src/bson.ts).
- Implement the kept members on Timestamp itself, with the same behavior as today (unsigned 64-bit order, i.e. compare t, then i):
- compare, equals/eq, notEquals/neq/ne, lessThan/lt, lessThanOrEqual/lte/le, greaterThan/gt, greaterThanOrEqual/gte/ge
- toBigInt() and toString(radix?), so toJSON() output stays the same
- Add toLong() returning an unsigned Long, for anyone who was using the removed Long methods.
- Keep fromBits, fromString, toJSON, toExtendedJSON, fromExtendedJSON, inspect, _bsontype and [bsonType].
- Constructor keeps its current overloads, including new Timestamp(otherTimestamp).
- Fix internal code that relies on the inheritance:
- serializeLong in src/parser/serializer.ts calls getLowBits()/getHighBits() on Timestamps
- BSON_TYPE_MAPPINGS.Timestamp in src/extended_json.ts reads o.low/o.high (keep low/high as a fallback for old duck-typed objects)
- Remove Timestamp from Long method parameter types in src/long.ts, and throw a BSONError if one is passed at runtime. Without that, Long.fromValue reads the missing low/high as 0, and you get things like {{Long.ZERO.eq(new Timestamp(
{ t: 100, i: 5 }
))}} returning true.
Testing Requirements
- Unit tests for every kept or new member. Comparisons should cover a differing t, the same t with a differing i, equal values, and uint32 edges.
- Constructor tests: rejects bad input (per Unknown 2), copies from another Timestamp, existing inputs still work.
- Long methods throw when passed a Timestamp.
- ts instanceof Long and Long.isLong(ts) are false.
- Turn the
NODE-7069deprecation checks in test/types/bson.test-d.ts into property-doesn't-exist checks. - Rewrite existing tests that rely on unsigned/low/high/MAX_VALUE to use t/i. Everything else, BSON corpus included, passes unchanged.
- Driver tests pass against the new bson. No regression in the bson benchmarks.
Documentation Requirements
- TBD
Follow-Up Requirements
- Give the mongosh team a heads up before bson v8 ships.