Skip to content

Build1 publisher3 min readPublished

A duplicate process wrote for ten hours because the watchdog compared strings after formatting

An unattended system needed five interventions in two days. None of them printed an error a human would see, because every one left the process running and the watchdog green. The fixes are ordering rules.

The Engineer · Build desk

Illustration accompanying A duplicate process wrote for ten hours because the watchdog compared strings after formatting

What happened

  • An operator running an automated system on a remote machine with nobody watching it logged five separate interventions in two days, and not one of them produced an error message a human would see.
  • One failure was a lost login: the service kept running, kept polling and kept reporting itself healthy while it was no longer authenticated.
  • Two of the five failures happened only because something restarted, one of them a config that silently reverted to defaults and left slightly different numbers as its only symptom.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint A check that reports the process is up cannot sit at the top of an alerting stack for an unattended machine, because a deaf poller and an unauthenticated one both satisfy it.
  • decision Anyone whose poll loop branches on whether a payload contains items has to decide whether to inspect status first, since a revoked token and a rate limit arrive as errors rather than as an empty list.
  • exposure The events least likely to be noticed are exactly the ones this pipeline cannot account for, so whoever depends on the notification learns nothing when a send fails.

The duplicate-process watchdog existed to stop a second copy of the process from starting. It compared strings after formatting instead of before, so the duplicate did not look like a duplicate to it, according to the author of a post on dev.to [5]. Both copies ran side by side for ten hours. Both were writing, and neither complained [4].

Two of the five failures were failures of attachment. A component was still installed and still configured but no longer attached to the thing it was supposed to be attached to, and nothing logged it [7]. A file was written, correct and current, for a consumer that had stopped reading it months earlier [8].

The message loop had the same defect in a different place. It checked whether a response contained items, and went back to sleep when it did not [18]. A revoked token returns an error. So does rate limiting, and so does a second instance stealing the connection [19]. One sleep branch therefore served four conditions: three failures and one genuinely quiet poll [24]. "The process stayed alive, the watchdog stayed green, and the system was completely deaf," the author wrote [20].

The startup-order case is the one with timestamps. The journal shows the component loaded at :29, the value read at :36, and the connection authorized at :39 [13]. The read beat authorization by three seconds [14]. It returned a clean, plausible zero, the system used that zero as a measurement, and it configured itself three hours away from correct [12].

Two of the five interventions happened only because something restarted [10]. The current practice is that every scheduled job writes a heartbeat on every run, including runs where it decided there was nothing to do, and that the absence of a heartbeat for longer than the interval is itself an alert [17]. That rule requires the alerting side to know each job's interval.

The path failure is the cheapest one to reproduce. The author moved a directory; the wrapper script coped because it resolved its own location at runtime, and the registered scheduled tasks failed because they held absolute paths recorded at registration time [15]. The machine said nothing about it [15].

The notification pipeline deleted each event from disk before confirming it had been sent, so a failed send left no event and no retry [22]. "I'd built at-most-once delivery by accident, in the one place where at-least-once was the entire point," the author wrote [23].

Five interventions in two days is one operator's log, and the post does not say what the system does [2][25]. For these failures to be yours, some ordering has to match: your comparison runs after normalization, your scheduler stores absolute paths at registration, your startup reads a remote value before it checks that the connection is usable. The author frames the gap as the one between "automated" and "passive" [1], and states the test this way: "if the only evidence of health is the absence of errors, you have no evidence of health" [26].

What to watch

  • Whether the alerting side keeps each job's interval in sync with the scheduler, since the absence-of-heartbeat alert only fires correctly if it knows the interval.
  • Whether a second operator publishes the same class of quiet failures with timestamps. That would move this from one log to a measured pattern.
  • Whether the scheduler in question records absolute paths at registration by default, which decides how reproducible the path failure is elsewhere.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories