One collection, one insert declaring a field as number, four values sent
through sapedb invoke against a real daemon, then read straight back out of the database
file with sapedb dump:
sent 9007199254740992 stored 9007199254740992 exact
sent 9007199254740993 stored 9007199254740992 CHANGED
sent 9223372036854775807 stored 9223372036854776000 CHANGED
sent 1234567890123456789 stored 1234567890123456800 CHANGED
Every write answered changed 1 and exit 0. There is no refusal, no warning, and nothing
in the log that would let anybody notice afterwards. The second line is the clearest: a value off by
one, silently.
number is one type and it is float64. matches in
internal/store/store.go accepts float64, float32,
int and int64, but the value has already become a float long before it gets
there: 38 call sites decode JSON with json.Unmarshal and exactly one uses
UseNumber, so a JSON number becomes a float64 at the door.
The key encoding is built the same way. internal/keys writes tagNumber
followed by the eight bytes of the float's bits, so an indexed number is float-shaped on disk as well
as in memory. Exactness is not lost at the last moment; there is nowhere in the current path where it
still exists.
The triage question on this board is whether a thing loses data. This one does, and the values it loses are not exotic: a Snowflake or twitter-shaped id, an account number, a nanosecond timestamp, any integer identifier a caller did not think of as a float. A caller who stores an id and reads back a neighbouring id has been told nothing went wrong.
This is the part that decides the order, and it comes from
SAPE-2's own policy rather than from anybody's preference.
COMPATIBILITY.md section 3 lets a 1.x loosen a rule and forbids it from
tightening one — a declaration valid in 1.0 may not be refused in 1.1.
So the question is not which fix is better. It is which fix has a deadline. The refusal does. If 1.0.0 ships without it, silent rounding becomes a promise this project has to keep.
failed — the shape
ISS-21 already cost this project once.COMPATIBILITY.md under what a declaration means,
because after the tag this is frozen either way — as a refusal, or as documented rounding.number means for values that already round-trip. Nothing under
253 moves.