Control Loops Outside the Lab · 02
Keeping a Threshold from Flapping
Separate entry and exit thresholds, add persistence where it helps, and measure the delay as well as the reduction in transitions.
A threshold is an appealing piece of code. Compare a value with a constant and return a boolean. The trouble starts when changing that boolean causes work.
Take a utilization signal near 70 percent:
69, 71, 68, 72, 70, 69, 71
If every crossing starts or stops capacity, the controller will spend its time reversing decisions. The utilization has barely changed, but the system is busy.
- Signal
- Threshold (drag it)
- Switch on
Move the threshold through the noisy part of the signal and watch the transition count. Moving it beyond the normal signal range can eliminate switching, but that may also eliminate useful detection. The question is whether the rule separates states you actually care about.
Give the rule a little memory
A cooling thermostat can turn on above 24 °C and turn off below 22 °C. Between those temperatures it keeps its existing state. A reading of 23 °C therefore has two possible outcomes, depending on how the system got there.
That is hysteresis. The two thresholds create a region in which the current state matters. The relay model in Simulink uses this same rule.
It differs from a dead zone, which usually suppresses an output for inputs inside a range. Hysteresis retains state inside the range. The distinction matters when translating a diagram into code.
For an alert, the implementation can be as small as:
if (!active && lagMinutes >= 10) active = true;
else if (active && lagMinutes <= 5) active = false;
This stops repeated crossings near ten minutes from repeatedly opening and closing the alert. A sufficiently large fluctuation can still cross both thresholds.
Require the condition to last
Persistence handles a different case: a signal that crosses the boundary far enough, but only briefly.
You can require lag to remain above ten minutes for 30 seconds before opening the alert, and below five minutes for 30 seconds before closing it. A sample that fails the relevant condition resets its timer.
The sampling interval belongs in the specification. Three consecutive samples at ten-second intervals are not the same policy as three at one-minute intervals. A missing sample also needs an explicit interpretation; silently treating it as healthy data is usually a poor default.
- Signal
- High / low thresholds
- Single threshold
- State on
All three policies receive the same seeded signal, sampled ten times per second. The timelines and counters use those same decisions. The cost is just transitions multiplied by a fixed illustrative price; it does not model the cost of missed or late detection.
Try increasing persistence until the third policy rarely switches. That is a useful reduction only if it still catches the events you want. A policy that never activates can have an excellent transition count and be completely ineffective.
There is a cost on both sides
Hysteresis requires a larger movement before reversing state. Persistence requires more time. Both can reduce nuisance transitions, and both can delay a necessary response.
For a production alert, replay ordinary traffic and known incidents through the proposed rule. Measure false activations, detection delay, recovery delay, and incidents missed entirely. Pick an acceptable tradeoff before tuning the thresholds.
Entry and recovery do not have to be symmetric. You might activate quickly and require a longer healthy interval before declaring recovery. A severe failure can have its own immediate path while a less urgent condition uses persistence.
Also count the transitions themselves. A service can have acceptable average latency while its circuit breaker changes state hundreds of times. That is useful evidence about the control policy, even if the latency dashboard looks quiet.
A small rule is still a policy
The same idea is useful when deciding whether to change a routine: choose what evidence would justify the change, and over what interval you expect to see it. One poor result may be normal variation. A persistent pattern deserves attention.
That is an analogy, not a claim that people behave like thermostats. In software, at least, we can make the policy explicit, replay the input, and see exactly which decisions it would have made. A few lines of state and a timer often do more useful work than another round of threshold guessing.