Timestamps
pgxsinkit uses a single, deliberate timestamp model. It can look surprising, but it is intentional and load-bearing for convergence.
The model
Section titled “The model”created_at_usandupdated_at_usare the authoritative time fields.- They are stored in PostgreSQL as
BIGINTmicroseconds since the Unix epoch. - They cross API and sync boundaries as decimal strings (e.g.
"1718900000000000"), to avoid JavaScript number-precision loss on 64-bit integers. - They are the sync truth. Human-readable timestamp projections can be added if operationally useful, but they are never what convergence is decided on.
Why microseconds, and why strings
Section titled “Why microseconds, and why strings”- Microseconds give enough resolution to order rapid successive writes without collisions.
- Decimal strings survive the JSON boundary intact. A 64-bit microsecond value exceeds
Number.MAX_SAFE_INTEGER, so sending it as a JSON number would silently corrupt it. Strings keep it exact from Postgres → server → client and back.
Where it shows up
Section titled “Where it shows up”- The write path returns the server
updated_at_usin each ack. - The client clears an optimistic overlay row only once the read path echoes a row whose
updated_at_usis at least as new as the acked value — this is how the optimistic write and the synced truth reconcile. See The write path.
What not to do
Section titled “What not to do”- Don’t treat these as millisecond JS timestamps — they are microseconds.
- Don’t parse them into
numberon the wire — keep them as strings until you intentionally convert. - Don’t introduce a separate “real” timestamp column and sync on that;
*_usis the truth.