Documentation
¶
Overview ¶
Package synckit is the Go client half of a bidirectional, last-write-wins sync protocol. It shares an exact wire contract with the server engine and with the protocol's other client implementations: a single POST carries both directions —
request : { since: <lastServerRev>, changes: [<local dirty Records>] }
response: { server_rev: <new cursor>, records: [<everything rev > since>],
conflicts: [<device changes that lost LWW>] }
The Record/Conflict/Meta/Request/Response types here are byte-compatible with the server's, and Wins() is a copy of the server's wins() LWW rule so a client can reason about conflicts identically. Deliberately minimal: this package does NOT own the apply-then-advance-cursor / dirty-tracking loop — that belongs to the consuming agent, which must only advance its `since` cursor after a successful, fully-applied Pull.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Wins ¶
Wins reports whether an incoming record should overwrite the current stored row under last-write-wins. IDENTICAL to the server engine's wins(): strictly-newer updated_at wins; on an exact updated_at tie a delete beats a non-delete (a tombstone is never silently lost to a same-instant edit); any other tie keeps the existing row (deterministic, no flapping).
Types ¶
type Conflict ¶
type Conflict struct {
Kind string `json:"kind"`
ID string `json:"id"`
DeviceUpdatedAt time.Time `json:"device_updated_at"`
ServerUpdatedAt time.Time `json:"server_updated_at"`
}
Conflict records a local change that lost the last-write-wins comparison (the server already had a strictly-newer version). Surfaced so the caller can log it; the client is expected to reconcile from the returned records.
type Record ¶
type Record struct {
Kind string `json:"kind"`
ID string `json:"id"`
Rev int64 `json:"rev"`
UpdatedAt time.Time `json:"updated_at"`
Deleted bool `json:"deleted"`
UpdatedBy string `json:"updated_by,omitempty"`
Fields map[string]any `json:"fields,omitempty"`
}
Record is one syncable row on the wire, in either direction.
- Kind identifies which logical table the row belongs to (e.g. "host", "key") so one stream can carry several kinds under a single `since`.
- Rev is the server-assigned monotonic version; clients send 0 on input (the server assigns it) and echo it back from pulled records.
- Fields holds the app data columns keyed by column name. On the wire it is whatever JSON produced (numbers as float64, bytea as base64 strings, …).
type Request ¶
Request is the sync POST body: the client's last server cursor plus its locally-dirty records to push.
type Response ¶
type Response struct {
ServerRev int64 `json:"server_rev"`
Records []Record `json:"records"`
Conflicts []Conflict `json:"conflicts"`
}
Response is the sync POST result: the new server cursor, every row with rev > since, and any pushed changes that lost LWW.
func Pull ¶
Pull performs one push+pull round-trip: it POSTs req as JSON to url and decodes a 2xx JSON body into a Response. A non-2xx status returns an error and an empty Response WITHOUT decoding a body, so the caller knows NOT to advance its `since` cursor. Applying the returned records/conflicts and advancing the cursor only after a fully-successful apply is the caller's job.