Documentation
¶
Overview ¶
Package syncstatus tracks per-space, per-object sync state from any-sync's StatusUpdater hooks and exposes it via the SyncStatusAPI surfaced on space.Space. Two subscription scopes are supported:
- Account-wide via space.Service.SubscribeStatus (one cb sees every space's rollup transitions; backed by Service's registry).
- Per-object via Space.SyncStatus().SubscribeObject (one cb per objectId; backed by per-Tracker registry).
The Service owns one Tracker per space (lazy, via For(spaceId)) and the account-wide subscriber registry. The Tracker holds the per-tree state machine and per-object subscriber registry. Each Tracker is wired into commonspace.Deps.SyncStatus when its space loads.
Index ¶
- Constants
- type NodeIdsFn
- type P2PStateFn
- type PeerCountsFn
- type Service
- func (s *Service) Close()
- func (s *Service) For(spaceId string) *Tracker
- func (s *Service) Refresh(spaceId string)
- func (s *Service) RefreshAll()
- func (s *Service) Run(ctx context.Context)
- func (s *Service) SetExcludedTreesFn(fn func(spaceId string) []string)
- func (s *Service) SetLocalPeerIdsFn(fn NodeIdsFn)
- func (s *Service) SetNodeIdsFn(fn NodeIdsFn)
- func (s *Service) SetP2PStateFn(fn P2PStateFn)
- func (s *Service) SetPeerCountsFn(fn PeerCountsFn)
- func (s *Service) SetTotalFn(fn TotalFn)
- func (s *Service) Status(spaceId string) space.SpaceSyncStatus
- func (s *Service) SubscribeStatus(cb func(space.SpaceSyncStatus)) func()
- func (s *Service) Tick()
- type TotalFn
- type Tracker
- func (t *Tracker) AddExcluded(treeIds ...string)
- func (t *Tracker) BulkSyncedFromPeer(peerId string)
- func (t *Tracker) Detail(objectId string) (state space.SyncState, pending []string, lastApplied time.Time, known bool)
- func (t *Tracker) HeadsApply(senderId, treeId string, heads []string, allAdded bool)
- func (t *Tracker) HeadsChange(treeId string, heads []string)
- func (t *Tracker) HeadsReceive(senderId, treeId string, heads []string)
- func (t *Tracker) Init(_ *app.App) error
- func (t *Tracker) LastSyncedAt() time.Time
- func (t *Tracker) Name() string
- func (t *Tracker) Object(objectId string) space.ObjectSyncStatus
- func (t *Tracker) ObjectReceive(senderId, treeId string, heads []string)
- func (t *Tracker) PendingCount() int
- func (t *Tracker) SpaceId() string
- func (t *Tracker) SubscribeObject(objectId string, cb func(space.ObjectSyncStatus)) func()
Constants ¶
const DefaultTickInterval = time.Second
DefaultTickInterval is the cadence the rollup loop runs at when Service.Run is called without a custom interval. Matches heart's spacesyncstatus loop and keeps event volume low while still feeling live in a UI.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type NodeIdsFn ¶
NodeIdsFn resolves the responsible-node id list for spaceId. Wired from anysyncx — the Service doesn't import nodeconf directly so tests can stub it.
type P2PStateFn ¶
P2PStateFn resolves the per-space local-network state. Wired from anysyncx over discovery possibility + the p2p peer store. nil ⇒ P2PStateUnknown.
type PeerCountsFn ¶
PeerCountsFn reports live-connection counts for spaceId: responsible sync nodes, local-network peers and global peers sharing the space. Wired from anysyncx (non-dialing pool.Pick reads). nil ⇒ all report 0.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the per-account sync-status registry. Owns one Tracker per space (lazy via For) plus the account-wide subscriber registry fed by Service.SubscribeStatus.
Construct once in anysyncx.New; expose via App.SyncStatus(). The Service is safe for concurrent use.
func NewService ¶
func NewService() *Service
NewService constructs an empty Service. Wire NodeIdsFn / TotalFn / excluded-trees via the setter methods before tracker construction; they're snapshotted into each tracker at construction time.
The rollup loop is not started here — call Run(ctx) to start dispatching account-wide events on the configured ticker. Tests can skip Run and call Tick() directly for determinism.
func (*Service) Close ¶
func (s *Service) Close()
Close drops every Tracker, stops the rollup loop, and rejects future For / Subscribe calls. Idempotent.
func (*Service) For ¶
For returns the Tracker for spaceId, constructing one on first access. The same Tracker is returned on every call until Close.
The space cache wires the returned Tracker into commonspace.Deps.SyncStatus when the space loads.
func (*Service) Refresh ¶
Refresh marks spaceId dirty from outside the tracker path — used by the p2p wiring when a local peer's space set or the discovery possibility changes, so subscribers get a presence event without a tree transition.
func (*Service) RefreshAll ¶
func (s *Service) RefreshAll()
RefreshAll marks every tracked space dirty. Used on account-wide presence changes (discovery possibility flips).
func (*Service) Run ¶
Run starts the rollup loop on a 1s ticker. The loop drains the dirty set, recomputes each space's rollup, and dispatches SpaceSyncStatus events to account-wide subscribers when the rollup transitioned from the last-emitted snapshot.
ctx controls the loop lifetime; cancelling it stops the loop. Close() also stops the loop via its own internal cancel. Calling Run more than once is a no-op — the existing loop keeps running.
func (*Service) SetExcludedTreesFn ¶
SetExcludedTreesFn wires the per-space non-user-visible tree id list (ACL / spaceIndex / settings / members system). nil ⇒ the tracker tracks every tree.
func (*Service) SetLocalPeerIdsFn ¶
SetLocalPeerIdsFn wires the connected direct-peer (LAN and global) resolver so those peers count as responsible senders. Pass the p2p peer store's LocalPeerIds method.
func (*Service) SetNodeIdsFn ¶
SetNodeIdsFn wires the responsible-node resolver. Pass the nodeconf.Service.NodeIds method bound to the running app.
func (*Service) SetP2PStateFn ¶
func (s *Service) SetP2PStateFn(fn P2PStateFn)
SetP2PStateFn wires the per-space local-network state resolver.
func (*Service) SetPeerCountsFn ¶
func (s *Service) SetPeerCountsFn(fn PeerCountsFn)
SetPeerCountsFn wires the live-connection counters (nodes + local peers). Pass a closure over pool.Pick from anysyncx.
func (*Service) SetTotalFn ¶
SetTotalFn wires the per-space regular-object count source. Pass a closure over spaceobjects.Store from the space layer.
func (*Service) Status ¶
func (s *Service) Status(spaceId string) space.SpaceSyncStatus
Status returns a snapshot of spaceId's rollup, composed inline on every call from the tracker state, peer counts, P2P state and the Total source. Cheap.
func (*Service) SubscribeStatus ¶
func (s *Service) SubscribeStatus(cb func(space.SpaceSyncStatus)) func()
SubscribeStatus registers cb for SpaceSyncStatus transitions. Account-wide; one cb sees every space.
type TotalFn ¶
TotalFn returns the count of regular objects known locally in spaceId. Wired from spaceobjects.Store via the space layer. nil means "not wired yet" — the rollup reports Total=0 in that case (matches a freshly-created space and is harmless for v1).
type Tracker ¶
type Tracker struct {
// contains filtered or unexported fields
}
Tracker holds per-space sync status: the per-tree state machine fed by any-sync's StatusUpdater hooks, plus the per-object subscriber registry. One Tracker per space, constructed lazily by Service.For and wired into commonspace.Deps.SyncStatus.
func (*Tracker) AddExcluded ¶
AddExcluded registers tree ids the tracker should ignore (ACL tree, spaceIndex object, settings tree, etc). Additive — call as system trees become known. Safe to call before or after the tracker starts seeing hooks; previously-recorded state for an id that's now excluded is dropped from the rollup math.
func (*Tracker) BulkSyncedFromPeer ¶
BulkSyncedFromPeer is the space-level convergence hook: when a responsible peer reports a fully-zero diff round (no new, no changed trees from our side), every locally-known tree is by definition in sync with that peer. Sweep the tracker: clear pending heads on every recorded tree, flip to Synced, anchor lastSync to now. Unknown-to-tracker trees inherit Synced via lastAllSyncedAt on the next Detail/Object read.
Non-responsible senders and empty peerIds are ignored — same rule HeadsApply uses, prevents a stray reply from collapsing the state machine.
func (*Tracker) Detail ¶
func (t *Tracker) Detail(objectId string) (state space.SyncState, pending []string, lastApplied time.Time, known bool)
Detail returns the full per-tree snapshot used by the debug API: state, a copy of the pending-heads slice, lastApplied, and whether the tracker has seen this id at all.
Unknown ids return known=false and zero values — debug callers pre-distinguish "we never saw a hook" from "we saw it and it converged" (the latter has lastApplied set).
func (*Tracker) HeadsApply ¶
HeadsApply drains heads from pending when the sender is a responsible node and allAdded is set. Empty pending flips the tree to Synced. Non-responsible senders are ignored in v1 (no tempSynced bookkeeping).
func (*Tracker) HeadsChange ¶
HeadsChange marks the tree as Syncing with pending=heads. Called from any-sync after a local write (synctree.AddContent).
func (*Tracker) HeadsReceive ¶
HeadsReceive is a no-op in v1 — receive happens pre-apply and we don't show "received but not applied" as a distinct state. Matches heart.
func (*Tracker) Init ¶
Init is the app.Component method invoked when the per-space commonspace app starts. We don't need anything from the per-space graph (responsible-node ids come from the account-level nodeconf, already bound at Service construction), so this is a no-op.
func (*Tracker) LastSyncedAt ¶
LastSyncedAt returns the most recent HeadsApply timestamp seen by the tracker (across all trees). Used in the SpaceSyncStatus rollup.
func (*Tracker) Name ¶
Name returns the component name expected by commonspace. Must match anysyncstatus.CName so the app graph wires the tree-sync handlers to this instance.
func (*Tracker) Object ¶
func (t *Tracker) Object(objectId string) space.ObjectSyncStatus
Object returns the snapshot for objectId. Unknown ids return State=Unknown; the tracker doesn't distinguish "never seen" from "no work needed" via the returned value — that's the rollup loop's job (which counts pending against Total).
func (*Tracker) ObjectReceive ¶
ObjectReceive registers a tree the tracker hadn't seen before (newly-pulled tree on cold restore). Doesn't change Synced/Total math directly — Total is sourced from the per-space `objects` collection — but ensures Object() returns sensible state.
func (*Tracker) PendingCount ¶
PendingCount returns the number of trees currently in Syncing. Read by the rollup loop to compute Synced/Total.
func (*Tracker) SubscribeObject ¶
func (t *Tracker) SubscribeObject(objectId string, cb func(space.ObjectSyncStatus)) func()
SubscribeObject registers cb for state flips on objectId. Cheap; the dispatcher delivers on every change, not on every hook (so a HeadsChange followed by a HeadsApply with no net transition fires once, not twice).