One process per role, and the roles block
One process per role, and the roles block
Every long-running role of stado serve (the host process, with or without
--worker, --api, a resolver) takes a lock at start,
~/.stado/roles/<role>.lock, holding its pid, program and start time, for as
long as the process lives. A second start of the same role on the host is
refused before any listener binds: role serve already runs on this host: pid <pid>, program <path>, since <time>; one process holds a role, and a second start of the same role is never admitted beside it. A process whose program
is not the installed Stado (~/.stado/bin/stado: the same file, so a link or
hard link to it is it) is refused the same way, naming both programs.
Each health pass then reads the host's process table for Stado role processes
nothing accounts for, judging each by the image the kernel maps for it (its
resolved path and file), not the name it was started by: the installed
program is the same file or the installed path, so a reinstall there is left
to the release handoff. It ends a copy or a checkout's build running a role,
a process whose image has no file left, stado agent from a release that had
that role, and a second holder of a role with SIGKILL, waits until each no
longer runs (gone, or a zombie for its parent to reap), and publishes the roles block: held (pid, program,
since per role), ended_strays (pid, command, run time, reason —
not_installed_program, program_gone, retired_role, duplicate_role —
and a detail sentence), and a stray it cannot end in unended_strays
beside end_refused. stado service list --json carries the block on every
unit row. The process table is read with ps, each image from
/proc/<pid>/exe or lsof; when either, a live process's image or a role
lock cannot be read, the block is {"unread": <cause>}, said on stderr.
The block also carries declared_not_running: the stado serve roles the
registry declares for the publishing process's own serve unit
(targets[].services, read by serve's own definition) that the process does
not run, as {"roles": [...], "declared_by": [<unit>...], "unreadable": [{"unit", "cause"}...]}. A unit rewritten without a declared role keeps
answering while that role stands silent — a registry authority whose unit
lost --resolver serves its object API while every host's registry read
goes stale — and this is where that is said. The units compared are the
declared serve lines that take the process's own role lock (serve, or
serve--<target> for a worker target), since one process holds a lock; a
serve unit for another target, or a unit running another Stado command such
as a periodic product sync, is another process and is not compared. When no
declared serve line takes the lock, nothing was compared and the field is
{"no_declared_unit": <lock>}, never an empty list read as every role
running. A declared serve line this build cannot parse is listed under
unreadable with the parser's reason, and the process's own arguments
failing to parse makes the whole field {"unread": <cause>}. A beacon
collected by hand carries null, since that command's arguments are not the
unit's. stado host health <target> prints it as roles: every declared role runs or roles: declared but not running: <roles> (<units>), either followed
by ; unreadable: <unit>: <cause> for each refused line, roles: no declared serve unit takes role lock <lock>, roles: the comparison could not be read: <cause>, roles: the role register could not be read: <cause>, roles: not measured: collected by hand or roles: not reported; ; a stray could not be ended: <cause> ends any of them.
When roles are missing, the pass renders the resident unit again with the
same detached converge the executable starts when its unit's argument vector
is refused: <stado> service ensure stado --host <host> --reason "the registry declares serve roles the resident unit does not run: <roles>", writing its
answer to ~/.stado/logs/resident-converge.log and recording itself in
~/.stado/logs/resident-converge.json with its pid, birth second (null for
a converge that had already ended, a failed one among them) and the line it
was started for. The field then carries converge: {"started": <pid>, "log": <path>}, {"running": <pid>, "log": <path>} while a converge
recorded earlier still runs, {"none": <why>} when the process is no resident
unit of the host (no unit file under its home starts its line — a process
started by hand or under a home of its own) or a converge already ran for this
very line and the unit still starts it, or {"refused": <cause>}. stado host health adds ; rendered again: <that answer> to the roles line. A unit that
still lacks a role after its converge, whether the converge restarted it or
failed, is not rendered again for the same line; the beacon keeps naming the
role.
Source: this website