reachlin

reachlin's development notes

Started the day with a simple complaint: an alert fired to PagerDuty, the underlying condition cleared, but the incident just sat there open. Ended up digging through a small internal tool’s architecture, patching it, and then watching the fix play out live across log tunnels, task schedulers, and a PagerDuty incident feed.

The tool

We run a small scheduled Lambda — one per environment — that polls Elasticsearch on a fixed interval and turns query results into PagerDuty pages. It has two ways to define what pages:

  1. A reviewed path — alert rules committed to a JSON file in the repo, PR’d and redeployed. Every run, the Lambda re-runs each rule’s ES query and reflects the current state: breach → trigger, no breach → resolve. Since PagerDuty’s Events API already dedups on a dedup_key, there’s no state table needed — the Lambda just says what’s true right now, every run.
  2. A self-service path — teams can wire their own Kibana alerting rule directly to an ES index the poller also watches, no PR required. Kibana’s .es-query rule type can write a small JSON document into that index whenever its own condition matches. The poller picks it up, forwards it to PagerDuty, marks it processed.

The self-service path was the old design carried over from a predecessor script, and it only ever triggered. A one-shot indexed document has no ongoing query to re-evaluate against, so there was never a natural place to hang a resolve off of. Anyone using the self-service path just had to remember to close their own PagerDuty incidents by hand.

The fix

Kibana’s alerting rules actually have a second action group most people don’t reach for: recovered, which fires exactly once when a rule transitions from active back to healthy. So the fix was:

Old docs without the new key still fall back to using their own _id, so nothing already wired up breaks.

Debugging the rollout

Getting the code merged and deployed was the easy part. Verifying it actually worked end-to-end was more interesting:

One fun tangent: a batch of ResourceNotFoundException errors around secret retrieval showed up in the logs from earlier in the day, before any of today’s changes. Traced them to a five-minute window and confirmed they’d self-resolved and never recurred — an unrelated, already-healed blip, not a symptom of the change.

Takeaway

“No state store” designs are elegant until you remember that reflecting current state every run only works if there’s a run that actually re-evaluates the state. A one-shot event has nothing to re-evaluate against by definition — the fix isn’t cleverer polling, it’s giving the upstream system (Kibana, in this case) a second, explicit action to say “this got better,” and giving the poller a stable key to tie that message back to the page it’s closing.