mirror of
https://github.com/zap-proto/zip.git
synced 2026-10-19 15:38:57 +00:00
A plugin that panicked was invisible and permanent. Nothing watched the child,
so when it died cur still pointed at the corpse: every request became a
connection error dressed as a 502, Plugins() kept reporting Running, and only a
manual Reload recovered. A plugin is a separate process precisely so a panic
cannot take the host down — which is only true if the host notices.
It notices now. The supervisor sees the exit, stops serving from the dead
instance (503 "deployed but down", the truth, rather than 502 blaming an
upstream that no longer exists), and restarts with capped backoff. An instance
retired by Reload or Unload also exits; a compare-and-swap tells the two apart,
so only a death that was still current counts as a crash. Restarts is reported
separately from Reloads — deliberate swaps and crashes should never be one
number, and a climbing Restarts is a crash loop.
Proven, not argued: TestPlugins_SurvivesPanic panics the plugin on a goroutine,
where no recover() can catch it, and asserts the host survives, the pid changes,
Restarts increments, and the same routes serve again with no re-registration.
Child exit is now a closed channel rather than a single-value one, because the
supervisor, a drain and Shutdown all need to observe the same exit and the first
reader would otherwise starve the rest.
Also adds per-plugin resource accounting, read from /proc:
Usage{CPU, RSS, Threads, FDs} // measured, not self-reported
This is the observability a monolith cannot offer at any price. One process has
one RSS and one CPU clock, and nothing inside it can be attributed to a
subsystem — "which app is eating the memory" is an argument. Running a service
as its own process makes it a question with an exact answer. Measured on a live
plugin: rss=12.7MB threads=5 fds=7.
And plugin output is tagged with the plugin name. An untagged line in a merged
stream is worse than useless: you can see something is wrong and not what.
Co-authored-by: Hanzo Dev <[email protected]>
8 lines
220 B
Go
8 lines
220 B
Go
//go:build !linux
|
|
|
|
package zip
|
|
|
|
// readUsage reports nothing where /proc is unavailable. The zero Usage is
|
|
// meaningful: it says "not measured here", not "measured as zero".
|
|
func readUsage(int) Usage { return Usage{} }
|