Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

world_sync

Grafts sync-durability status and per-item conflict resolution onto the world namespace. In a live world every /source write is synced to the backend; a write that has not yet reached the backend is a pending write, and a write that collides with a change another collaborator made since you last saw the path is a conflict. This module surfaces both and lets you resolve conflicts one path at a time.

world.syncStatus()

Returns a snapshot of the world's sync state:

local s = world.syncStatus()
-- s.subscribed      : bool: attached to a live world
-- s.content_synced  : bool: incoming: the world's content, and every change to it the stream delivered, is applied to this client
-- s.outgoing_synced : bool: outgoing: every /source change this session made reached the world
-- s.uploads_outstanding : number: upload jobs queued, in flight, or held for another attempt
-- s.uploads_in_flight : number: the outstanding upload jobs a flush has taken and is carrying to the world
-- s.progress        : { applied: number, expected: number?, unapplied: number }
-- s.pending_writes  : number: local /source writes and removals not yet round-tripped whose durable record stored
-- s.unrecorded_writes : { string }: owed /source paths whose durable record did not store, which a restart loses
-- s.owed_paths      : { string }: /source paths written or removed that the manifest does not reflect yet (the list vfs.pendingWrites() returns)
-- s.unsaved_writes  : { string }: /source paths whose write or removal the server never took
-- s.held_uploads    : { { path, reason } }: owed changes whose upload failed and is being tried again
-- s.uploads_abandoned : number — uploads the queue stopped carrying
-- s.conflicts       : { { path, base_sha, local_sha, backend_sha, isBinary } }

Content sync runs in two directions, and the status answers each apart.

content_synced is the incoming direction: the world's content has been applied to this client, and so has every change to it that the world's stream has delivered since. A survey uses it to tell "still syncing" from "empty world": a freshly attached session reads content_synced = false until the backend content has landed. Later, while changes other sessions made are still being applied here, it reads false again, and progress.unapplied counts the delivered changes still waiting; once it reads true with outgoing_synced true as well, /source here lists what the world holds. It reads nothing about this session's own changes reaching the world.

outgoing_synced is the outgoing direction: every /source change this session made has reached the world. It reads false while any write or removal is queued, uploading, held for another attempt, not yet reflected by the manifest, kept in a durable record (pending_writes, unrecorded_writes, a conflict among them), or refused by the server. It is the field that answers "is my work saved?". Both booleans read false while no world is subscribed.

uploads_outstanding counts the upload jobs outgoing_synced is still waiting on, each once, and uploads_in_flight the ones among them a flush has taken and is carrying to the world: a flush runs apart from the frame loop, so those are sent without waiting for a frame, while the rest wait for a frame, a blob transfer or another attempt.

pending_writes moves as the queue drains, so it reads the same for a queue working through a backlog and for one that has given content up. unsaved_writes and uploads_abandoned separate them: the first names the /source paths whose bytes are on this client only, the second counts the uploads the queue stopped carrying after they failed on it repeatedly while the connection reported itself live. A path listed there stays on this client until another session's reconcile carries its durable record.

held_uploads names the owed changes whose last upload attempt failed and went back to the queue, each with the reason that attempt gave: a blob transfer that ran past its deadline, a refused connection. The queue tries each again on its own schedule until it lands or is given up on. A held upload holds nothing else up: a stage or commit of other paths goes ahead, and a bulk stage passes over it. A call that names the held path waits on it and, when it does not land, reports the path and the reason. Removing the path ends the hold, since the removal is what the path owes from then on.

unrecorded_writes names the owed /source paths whose durable record this machine could not store, because the pending-write store or the local blob store could not be written. This session still carries them to the world; a restart before they arrive loses them. pending_writes counts the owed records that did store, so it leaves these out.

world.resolveConflict(path, mode, content?)

Resolves one path this session owes the world: a conflicted one, or one whose write or removal the server refused or the queue gave up on (listed in unsaved_writes). Waiting never carries such a change, and while one is held a world.swap or world.checkout away from the world refuses, naming it; "discard" drops it, "take-local" makes it again. mode is one of:

  • "merge" — for text, runs a 3-way merge of the common ancestor, your bytes, and the current backend bytes, and returns { merged, clean }. A clean merge produces ready-to-apply text; a conflicted merge returns the text with <<<<<<< / ======= / >>>>>>> markers to resolve. Merge is unavailable for binary content or a path with no common ancestor.
  • "apply" — writes content (the finalized bytes you pass) to the path and clears the conflict.
  • "take-local" — keeps your bytes.
  • "take-backend" — keeps the backend's bytes.
  • "discard" — drops your pending bytes, keeping the backend's.

Retained conflict bytes are also readable under /zero/runtime/sync/conflicts/, so you can inspect a conflict before choosing how to resolve it.