2026-08-26
When you build a controller, watcher, or event handler, you make a fundamental choice: edge-triggered (react to changes) or level-triggered (react to current state). Most engineers reach for edge-triggered because it feels efficient — only do work when something changes. That instinct is usually wrong for distributed systems.
Edge-triggered means: "the desired replica count changed from 3 to 5, so start 2 pods." You process the delta. If you miss the event, you're broken forever — there's no way to recover without external intervention.
Level-triggered means: "the desired count is 5, the actual count is 3, so start 2 pods." You process the current state. Miss an event? The next reconciliation loop reads state again and does the right thing anyway.
Real-world example: Imagine a Deployment controller that watches for scale events. Edge-triggered version: "scale event: +2 pods" arrives, controller creates 2 pods, done. Now the API server restarts and the controller misses a "scale event: -1 pod" during the outage. The controller now thinks there are 5 pods when there are actually 4 — permanently drifted. Level-triggered version: controller just re-reads the Deployment spec (replicas: 4), counts running pods (5), deletes one. It doesn't matter what events it missed. State converges.
The rule of thumb: if losing a single event permanently breaks correctness, you're edge-triggered and you have a bug waiting to happen. Level-triggered systems tolerate arbitrary event loss because they always re-derive intent from observed state.
The trade-off is work per loop. Edge-triggered does O(delta) work; level-triggered does O(state) work every reconcile. For a Deployment with 10,000 pods, checking every pod every second is expensive. That's why real controllers combine both: events wake you up (edge as a hint), but reconciliation reads full state (level as the source of truth). Events are optimizations, not correctness mechanisms.
Practical guidelines:
Kubernetes' entire controller model is level-triggered specifically because networks drop packets, watchers disconnect, and controllers crash. Level-triggering makes those failures invisible.
