Special types: Timestamp/TimeDuration/ScheduleAt units are a silent footgun #23
Labels
No labels
area:defold
area:docs
area:infra
area:protocol
area:testing
kind:bug
kind:chore
phase:breadth
phase:core
phase:hardening
phase:slice
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
yuris/spacetimedb-defold#23
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
A module column of type
Timestamp,TimeDuration,ConnectionId,ScheduleAtorUuidarrives in the cache as whatever JSON the server wrote. The SDK decodes rows positionally and hands the raw value through, so consumers get a bare number or string and have to know the encoding to do anything with it.Identity is the exception and shows the pattern already exists:
handlers.IdentityTokenruns it throughprotocol.unwrap(stdb/connection.lua:222-223), sodb.identityis usable. Nothing else gets that treatment.The one that actually misleads people
Timestampis microseconds since the Unix epoch, not seconds and not milliseconds (SpacetimeDB v2.8.2,sdks/typescript/src/lib/timestamp.ts:18-29). Every other timestamp a Defold developer touches is seconds (socket.gettime,os.time) or milliseconds if they came from JavaScript. So the natural thing to write is:That does not error. It produces a date roughly 50,000 years out, or a number Lua formats without complaint. Worse, in Lua 5.1 a microsecond epoch value exceeds the exact-integer range of a 32-bit
intbuild and starts losing precision in arithmetic, which fails differently on different targets.TimeDurationhas the same unit and the same trap.ScheduleAtis a sum type —{ Interval = <TimeDuration> }or{ Time = <Timestamp> }— so it needs tag inspection before the unit conversion even applies.Proposed change
A small set of pure conversion helpers, in
stdb/protocol.luabesideunwrapanddecode_payloadwhere the other wire-encoding knowledge lives:protocol.timestamp_to_seconds(v)os.date/os.timeprotocol.duration_to_seconds(v)protocol.schedule_at(v)"interval"or"time", plus the converted valueprotocol.connection_id(v)unwrap's treatment of identityDeliberately not proposing wrapper objects or automatic per-column coercion. Automatic coercion would need the generated schema to carry column types, which it currently does not — it carries names, order and pk (
dev/tools/gen-schema.sh). Adding types to codegen to enable transparent conversion is a much larger change and a separate decision; explicit helpers cost four small pure functions and are trivially testable.Acceptance
Unit tests in
dev/test/test_protocol.luawith a real captured value, asserting the seconds conversion round-trips against a known date, plus bothScheduleAtvariants. Precision needs one explicit test: a current-era microsecond timestamp must survive the conversion without losing seconds resolution on a 32-bit Lua build.README gains a short table of the special types and their units, in the API reference next to the cache section. The unit is the whole point — a helper nobody knows about does not prevent the
os.datebug.Priority
Medium, rising to high the moment any module in use has a timestamp column. It is a silent-wrong-answer bug rather than a crash, which is the category this SDK has been careful about elsewhere (#8's dropped header, #17's silent new identity).