/** The fields ranking reads from a message or a local snapshot. */ export interface ClusterRankable { readonly nodeId: string; readonly version: string; } /** A candidate plus its observed load. */ export interface ClusterSpreadRankable extends ClusterRankable { /** Surfaces this node is currently believed to hold. */ readonly holdings: number; } /** * Compare two dotted numeric versions. * * Returns > 0 when `a` is newer, < 0 when `b` is newer, 0 when equal. Missing * components read as 0, so `1.2` and `1.2.0` are the same version. A * prerelease suffix (`1.2.0-rc.1`) is ordered BELOW the same release, matching * semver, because a release candidate must not preempt the release. */ export declare function compareVersions(a: string, b: string): number; /** * Version, then the per-surface stable hash, then nodeId. * * The nodeId is a last resort that can only be reached by a SHA-256 collision * on 256 bits; it is there so the ordering is provably total rather than * "total in practice". */ export declare function compareStableRank(a: ClusterRankable, b: ClusterRankable, surfaceId: string): number; /** The full ruled ordering: version, then fewest holdings, then stable hash. */ export declare function compareSpreadRank(a: ClusterSpreadRankable, b: ClusterSpreadRankable, surfaceId: string): number; /** True when `candidate` should hold `surfaceId` rather than `incumbent`. */ export declare function outranksForSurface(candidate: ClusterSpreadRankable, incumbent: ClusterSpreadRankable, surfaceId: string): boolean; /** True under the holdings-free ordering. Used where both sides must agree. */ export declare function outranksStably(candidate: ClusterRankable, incumbent: ClusterRankable, surfaceId: string): boolean; /** * True when `candidate` carries a STRICTLY NEWER version than `incumbent`. * * This, and only this, authorizes preempting a node that is already * consuming a surface. Holdings and hashes decide who wins an election among * peers with nothing to consume yet; they are deliberately not grounds for * taking a surface away from a node that is already serving it, because that * would let the ranking interrupt a healthy holder every time the observed load * shifted. Rebalancing exists for that, and it goes through a voluntary yield. */ export declare function isStrictlyNewerVersion(candidate: ClusterRankable, incumbent: ClusterRankable): boolean; /** * How far ahead a holder must be before it gives a surface up. * * TWO, not one, and this is the whole anti-oscillation argument. Yielding moves * one surface: the holder loses one and the taker gains one, so the gap closes * by exactly 2. From a gap of 2 the cluster lands on 0, balanced, and no * further yield triggers. From a gap of 1 it would land on -1, the new holder * would then be the overloaded one, and the pair would trade the same surface * back and forth forever. */ export declare const SURFACE_YIELD_GAP = 2; /** * True when a holder should voluntarily release a surface to a lighter node. * * Voluntary is the operative word. Rebalancing never preempts a sitting holder * from the outside, the holder decides, and it releases through the ordinary * ordered stop-then-RESIGN path, so consumption stops before anything else * starts. An external preemption would have to interrupt a working consumer * from a node whose view of the load may be a heartbeat out of date. */ export declare function shouldYieldSurface(holderHoldings: number, candidateHoldings: number): boolean; //# sourceMappingURL=ranking.d.ts.map