Skip to main content

Module fill

Module fill 

Source
Expand description

The PIX fill planner — the pure rate law behind the Exit’s own keep-alives.

A PIX Exit is paid per return packet: each Exit → Entry packet spends one SURB carrying one share, and a funded cycle only recovers once its whole emission has gone back. Nothing in the protocol makes the application send those packets, so an idle Session strands the deposit it has already been paid. The Exit therefore sends its own, at a rate this module derives once per SAMPLING_INTERVAL from three quantities and nothing else:

remaining  = (E − shares seen on the target cycle) × (1 + loss_margin)
finish_by  = target.hard_deadline − (1 − finish_fraction) × max_recovery_time
required   = remaining / max(finish_by − now, SAMPLING_INTERVAL)          packets/s
organic    = EMA of the gate's served counter over ORGANIC_WINDOW         packets/s
fill       = clamp(required − organic, heartbeat, max_rate)

Everything here is pure: no clock is read, no I/O is performed, and every input arrives as an argument. The supervisor owns the target selection — which cycle the Exit is currently working for — because that is a lifecycle question; this module owns only the arithmetic and the decision of whether the answer is worth emitting.

§Why organic is the gate counter

served_total counts packets that passed the ServiceGate, which is every application data packet and nothing else — fill itself is ungated, exactly as the SURB-level keep-alives it reuses are. So the counter measures what the application is contributing and never what this planner is, which is what makes required − organic a subtraction rather than a feedback loop that would drive itself to zero.

§Why the stall rule cannot be the idle rule

SupervisorConfig::max_recovery_idle closes a Session whose cycle stops progressing while service is being consumed, and it reads the same gate counter. Fill bypasses the gate, so a cycle that is being filled and making no progress is invisible to it: the idle rule keeps re-arming because, as far as it can see, nothing was served. Left alone, an Exit would fill a doomed cycle — one whose polynomial failed, or whose Entry is supplying SURBs that carry no shares — at max_rate for the whole of max_recovery_time. The rule below is the bound that the gate-based one cannot supply: once the target has not moved for max_recovery_idle, fill falls back to the heartbeat and stays there until the cycle moves again.

Structs§

FillPlanner 🔒
The pure rate law. See the module documentation for what it computes and why.
FillTarget 🔒
The cycle the Exit is currently filling for, as the supervisor sees it.