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