Files
AWG2_harness/awg2-harness.service
T
founder a9b6e3c15d fix(systemd): drop the duplicate ensure-swap, use Type=exec and stepped restart backoff [P1]
awg2-harness.service ran ensure-swap from ExecStartPre while the daemon runs it again as
the first step of its own startup. Idempotency kept it harmless, but it cost two locks,
two reads of /proc/swaps, two persistence checks, and split the startup failure path
across two units so an operator reading systemctl status could not tell which invocation
failed. The daemon owns the startup sequence, its error contract and its log stream, so
the ExecStartPre is removed. The standalone ensure-swap subcommand is unaffected.

Type=simple reports a unit as started the moment systemd forks, before the binary is
known to be executable. Type=exec waits for the exec to succeed, so a missing or
non-executable /usr/sbin/awg2-harness fails the unit instead of appearing to start.

Restart policy: the daemon moves from Restart=always to on-failure, matching the
scheduler. A clean exit is deliberate and should not be respawned. For the scheduler this
was already load-bearing and is now documented - it exits 0 both when reboot.enabled is
false and after systemd accepts the reboot request, so Restart=always would have spun it
in a loop on any host with scheduled reboots disabled.

RestartSec=10s with StartLimitBurst=5 over StartLimitIntervalSec=60 could exhaust the
limit in about 50 seconds, which is easy to hit when the daemon is failing because Docker
or the container is still coming up - the restarts burn the budget before the dependency
can recover. Replaced with a stepped backoff, 10 -> 20 -> 40 -> 80 -> 160s via
RestartSteps=4 and RestartMaxDelaySec=160s, and the limit window widened to 15 minutes
with a burst of 10 so a full backoff sequence fits inside it. Both directives require
systemd 254+; Debian 13 ships 257.

Documented alongside, because it is the same lifecycle contract and had no home in the
docs: configuration is read once at process start and a config edit needs a restart of the
affected service. daemon-reload is not involved - it applies to unit files, not to the
application config. ExecReload is deliberately absent, since adding it without real SIGHUP
handling would imply a reload path that does not exist.

Unit files are installed from the repo root by tools/build.sh, which already runs
systemd-analyze verify on the build host when it is available, so these changes are
verified there. Not verifiable in this environment: no Debian, no systemd.

Acceptance checklist gained five checks covering unit verification, the restart-required
contract, single ensure-swap per start, growing backoff, and the scheduler not looping
when reboots are disabled.
2026-08-06 22:54:11 +05:00

34 lines
1.4 KiB
Desktop File

[Unit]
Description=AWG2 Harness multi-orchestrator
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service
# Restart backoff below can span ~5 minutes, so the failure window has to be wider than
# the old 60s or the limit would trip during a single legitimate backoff sequence.
StartLimitIntervalSec=15min
StartLimitBurst=10
[Service]
# Type=exec, not simple: startup is only considered successful once the binary has been
# executed, so a missing or non-executable /usr/sbin/awg2-harness fails the unit instead
# of being reported as started.
Type=exec
User=root
# ensure-swap is not run here. The daemon performs it as the first step of its own startup
# sequence, where it shares one error contract, one lock and one log stream. Running it
# twice per start only duplicated the work and split the failure path across two units.
ExecStart=/usr/sbin/awg2-harness --config /etc/awg2-harness/config.toml daemon
# on-failure, not always: a clean exit is deliberate and must not be restarted.
Restart=on-failure
# Stepped backoff 10 -> 20 -> 40 -> 80 -> 160s. A tight restart loop against a dependency
# that is still coming up (Docker, the container, the network) only burns the start limit
# before the dependency has a chance to recover.
RestartSec=10s
RestartSteps=4
RestartMaxDelaySec=160s
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target