MovableTreeGcCoordinator

class MovableTreeGcCoordinator<V>(state: StateFlow<MovableTree<V>>, cutFrontier: StateFlow<CutFrontier>, delivered: StateFlow<VersionVector>, applyCompaction: (Patch<MovableTree<V>>) -> Unit, scope: CoroutineScope) : ScopedCloseable

Coordinator that drives move-log GC for a MovableTree CRDT replicated via Quilter, gated by the eviction-safe causal-stability barrier (ADR-003 addendum v3, #262).

Role

The MovableTree move-log grows without bound unless causally-stable, superseded ops are periodically garbage-collected. A MoveOp on node n is eligible for GC once:

  1. Causally stable — op.seq ≤ stableCut[op.replica].

  2. Superseded — a later stable op on n is the winning placement; this op is not.

  3. Not a creation op (value != null) still referenced by any live op.

  4. Frontier-complete — delivered.dominates(frontierMax).

This coordinator observes Quilter.cutFrontier (for cut and frontier) and Quilter.deliveredLocal (this replica's contiguous delivered VV), then calls MovableTree.compact in a loop until nothing further qualifies. Each resulting MoveTreeCompact is broadcast as a Patch via applyCompaction so peers trim their own logs.

Loop-until-stable

Compaction runs in a loop until MovableTree.compact returns null. A single pass may unlock a second (e.g., removing a superseded move reveals another). Quilter.apply updates state synchronously, so state.value reflects each application before the next iteration.

Triggering

Re-evaluates on EITHER trigger: a cutFrontier change (cut advanced or frontier changed) OR a state change (a new move op appeared that may now be compactable). Without the state trigger, an op applied while the cut already covers it would never be re-evaluated (the StateFlow deduplicates unchanged cuts).

Parameters

state

live MovableTree state (updated by Quilter on every incoming delta).

cutFrontier

the atomically-published causal-stability cut + frontier from Quilter.cutFrontier.

delivered

this replica's contiguous delivered VV from Quilter.deliveredLocal.

applyCompaction

called with each compaction Patch; wire this to Quilter.apply so the delta propagates to all peers.

scope

the CoroutineScope for background coroutines.

See also

Constructors

Link copied to clipboard
constructor(state: StateFlow<MovableTree<V>>, cutFrontier: StateFlow<CutFrontier>, delivered: StateFlow<VersionVector>, applyCompaction: (Patch<MovableTree<V>>) -> Unit, scope: CoroutineScope)

Functions

Link copied to clipboard
override fun close()