feat(sentinel): report the critical level whether or not the restart is enabled - #294
Merged
Merged
Conversation
Base automatically changed from
thiagoesteves/deployex-vm-resource-monitoring
to
main
August 13, 2026 14:51
…tests use Foundation.CatalogTest wipes var_path in its setup and ran as an async module, so it removed the tree while Foundation.CatalogExDocTest was walking it. The doctest for setup_all_apps/0 failed with :enotdir whenever the two overlapped, roughly one run in seven on this machine. A module that destroys state the whole suite shares cannot be async. Risk assessment Impact: the catalog tests run in the synchronous phase, after the async ones. The suite takes marginally longer and stops failing at random. Blast radius: one test module. No production code is involved. Regression risk: none, the module keeps every assertion it had. Rollback: plain commit revert.
…is enabled A resource that crossed the restart threshold with enable_restart set to false said nothing at all. The restart clause did not match, the warning clause had already fired on the way past the warning threshold, and the check fell through to its catch-all. The only signal an operator ever got was the warning, which is what the installer templates hand out for the DeployEx atom, process and port limits. enable_restart decides what DeployEx may do about a resource, not whether the resource is worth reporting. The three near-identical clause families are replaced by the level the usage falls into, ok, warning or critical, which is the model the UI card already uses. Reporting follows the changes of level, so a resource that stays above a threshold no longer repeats its log line and its notification on every check, and a usage that jumps past both thresholds at once is reported as critical instead of being announced as a warning. Restarting still follows the level itself, the heaviest application is restarted for as long as the host memory stays critical, with the DeployEx termination as the exception since the node is already on its way down. Thresholds are now inclusive, matching the UI, which reads Critical at exactly the restart threshold while the watchdog used to stay quiet. watchdog_threshold_exceeded carries an action of :restart or :no_restart, and the Slack and PagerDuty adapters say which one happened. The event stays critical for PagerDuty either way, the resource is at its limit regardless of what DeployEx is allowed to do about it. Risk assessment Impact: a resource reaching the restart threshold is now logged and notified even when it is not acted upon, and the DeployEx host memory is reported even when there is no application to restart for it. A consumer treating watchdog_threshold_exceeded as proof of a restart needs to read the new action field. The memory event now carries the DeployEx node, the node the measurement is about, instead of the application that happened to be restarted, which is named in the log line. Repeated notifications for a resource that stays above a threshold stop. Blast radius: the threshold evaluation in Sentinel.Watchdog and the formatting of watchdog_threshold_exceeded in the Slack and PagerDuty adapters. Metric collection, the ETS layout, the restart mechanics and every other event are untouched. Regression risk: low for the actions, which keep their existing behaviour, and moderate for anything parsing the log lines, since the comparisons they show are now >= and <. The tests cover each level transition, the critical level with the restart disabled and the absence of a repeat while a level lasts. Rollback: plain commit revert. No data or config migration, the level lives only in the runtime ETS table.
…s open The terminal server opens its connection in a continue, after Host.Terminal.new/1 has returned, so the page renders before the LiveView knows the process it writes to. A key pressed inside that window reached Commander.send/2 with a nil process and took the whole terminal down. Those keys are dropped now, there is no connection to carry them yet. The window is small in a browser but wide open in the test suite, where the key event is sent as soon as the LiveView mounts. The terminal test failed roughly one run in twelve here, and it now waits for the connection through a fixture helper, so it asserts what it means to assert instead of racing the server. Risk assessment Impact: a keystroke typed in the moment between the terminal page rendering and the connection opening no longer crashes the page. Blast radius: the key handler of the terminal LiveView and one test helper. Every other terminal event, the terminal server and the session lifecycle are untouched. Regression risk: none for a connected terminal, the existing clause keeps handling every key once the process is known. Rollback: plain commit revert.
thiagoesteves
force-pushed
the
thiagoesteves/watchdog-threshold-levels
branch
from
August 13, 2026 15:55
b1dba4e to
46691b6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed and why
Follows #292, now merged, and targets
main.A resource that crossed the restart threshold with
enable_restart: falsesaid nothing at all. The restart clause did not match, the warning clause had already fired on the way past the warning threshold, and the check fell through to its catch-all. The only signal an operator ever got was the warning at 75%, and that is exactly the shape the installer templates hand out for the DeployExatom,processandportlimits, whereenable_restartis off by default.enable_restartdecides what DeployEx may do about a resource, not whether the resource is worth reporting.Sentinel.Watchdognow tracks each resource through the level its usage falls into,:ok,:warningor:critical, which is the modelComponents.Monitoringalready uses to colour the card. That replaces thewarning_log_flagboolean and collapses three near-identical clause families into one, a net 105 lines lighter.Behaviour changes to be aware of
watchdog_threshold_exceededcarries anactionof:restartor:no_restart. Anything treating the event as proof that something was restarted needs to read it. Slack and PagerDuty say which one happened, and the PagerDuty severity stayscriticaleither way, the resource is at its limit regardless of what DeployEx is allowed to do about it.The memory event now carries the DeployEx node, the node the measurement is about, instead of the application that happened to be restarted. Previously the payload read
type: :memorynext to a monitored application's node, which looks like that application's memory rather than the host's. The restarted application is named in the log line.The comparisons are now inclusive,
>=for both thresholds. The UI card already read Critical at exactly the restart threshold while the watchdog stayed quiet. The log lines print>=and<accordingly, so log greps built on the old>and<=wording need updating.The DeployEx host memory is reported at critical even when there is no monitored application to restart for it. The
nilclause used to come first and suppressed the whole check, warnings included.Two unrelated fixes, each in its own commit
Both came out of chasing intermittent suite failures and are kept separate so they can be dropped on their own.
Foundation.CatalogTestranasync: truewhile itssetupwipesvar_path, the treeFoundation.CatalogExDocTestwalks in thesetup_all_apps/0doctest. When the two overlapped,mkdir_pfailed with:enotdir, about one full-suite run in seven. A module that destroys state the whole suite shares cannot be async.Host.Terminal.Serveropens its connection in a continue, afterTerminal.new/1has returned, so the terminal page renders while the LiveView still has no process to write to. A key pressed inside that window reachedCommander.send/2with anilprocess and took the terminal down. Those keys are dropped now, and the test waits for the connection through a fixture helper instead of racing the server. That one failed about one run in twelve.Risk assessment
Impact: operators get a log line and a notification when a resource reaches its restart threshold with the restart disabled, which is the default the templates ship for the three DeployEx Beam VM limits. Repeated notifications for a resource that stays above a threshold stop. The payload of
watchdog_threshold_exceededgainsaction, and itsnodechanges for the memory resource. A keystroke typed before the terminal connection is open no longer crashes the page.Blast radius: the threshold evaluation in
Sentinel.Watchdog, the formatting ofwatchdog_threshold_exceededin the Slack and PagerDuty adapters, and the key handler of the terminal LiveView. Metric collection, the ETS layout, the restart mechanics, the YAML parsing and every other event are untouched.Regression risk: low for the actions, which keep their existing behaviour including the memory cascade, and moderate for anything parsing the log lines, since the comparisons they show changed. The tests cover every level transition, the critical level with the restart disabled, and the absence of a repeat while a level lasts.
Rollback: plain commit revert, per commit. No data or config migration, the level lives only in the runtime ETS table.
Verification
Compile
--warnings-as-errors,mix format --check-formatted,mix credo --strictandmix dialyzerpass. Around 90 suite runs went into the two flake fixes: foundation and web went from one failure in twelve to zero in fifteen, the terminal tests zero in twenty-five, sentinel zero in twenty, deployer zero in ten, and the CI invocationmix test --cover --warnings-as-errorszero in eight.Follow-up
The CHANGELOG is not touched here. The
actionfield, thenodechange on the memory event and the new log wording belong under Backwards incompatible changes for0.9.13.ObserverWeb.Telemetry.Storagewas seen crashing once withSystem.os_time/1 is undefined, meck reloadingSystemfor awith_mocksblock while the observer_web poller calls into it. It crashes a GenServer without failing a test and is left alone here, the fix is to giveFoundation.Yamlan injectable config path so the tests stop mockingSystemglobally.🤖 Generated with Claude Code