SnapshotChunkEnvelopeOverBudget

data class SnapshotChunkEnvelopeOverBudget(val peer: NodeId, val reservedBytes: Int, val budgetBytes: Int) : RaftMetric

This leader cannot send peer a single snapshot chunk: the InstallSnapshot envelope alone costs reservedBytes, which leaves no room for state bytes inside the transport's budgetBytes payload budget (#2720). No chunk was sent, and peer cannot be caught up until one of the two numbers moves.

Why a refusal rather than a small chunk. The envelope carries the snapshot's ConfigPayload — a ClusterConfig of consumer-supplied NodeIds — on every chunk, so its cost is a property of the cluster's membership and does not shrink as the transfer proceeds. A one-byte chunk minted against a budget the envelope has already exhausted is refused at the transport, never acked, and re-sent forever: a silent permanent wedge with nothing naming why. Emitting this and sending nothing turns that into a diagnosis.

Emitted on every refusal, exactly like SnapshotRejectedSizeCeiling and for the same reason: it is a level to sample rather than an edge to count, and "is this still happening?" is the question that separates a transient budget dip — a mesh peer attaching over a tighter link lowers Seam.maxPayloadBytes, and the transfer resumes on its own when it leaves — from a standing misconfiguration. (The engine's matching warn is latched to once per node.)

What to do about a standing one. Raise the transport's payload budget above reservedBytes with room for a chunk, or shorten the NodeIds: the envelope is dominated by the config's node ids, and six twenty-character ids already cost more than the 256 B the engine's flat reserve used to assume.

A refusal caused by a joint configuration does not clear when the change commits. A joint configuration carries two ClusterConfigs and so costs roughly twice a simple one. The envelope is measured around the config of the snapshot being sent, which is stamped when that snapshot is cut and never re-stamped afterwards — so a snapshot cut in the middle of a membership change keeps its joint payload after the settled Simple config commits. Which snapshot is being sent depends on whether peer had a transfer in flight when the budget dropped, and so does the remedy:

  • No transfer in flight. Each attempt measures the snapshot currently stored, so the application publishing a new snapshot cut at or past the entry that settled the change clears it. For a cluster whose settled membership fits the budget, that is the only remedy needed.

  • A transfer in flight. The transfer keeps the snapshot it started with, and a newer one does not replace it. The refusal clears only when the budget recovers, or when leadership changes — which abandons the transfer, so the next leader starts from its own stored snapshot.

Constructors

Link copied to clipboard
constructor(peer: NodeId, reservedBytes: Int, budgetBytes: Int)

Properties

Link copied to clipboard

the transport's published maxPayloadBytes at the moment of the refusal.

Link copied to clipboard

the follower that cannot be caught up.

Link copied to clipboard

what the engine measured the envelope to cost, charged at the widest Longs it admits so the figure does not drift with log position.