2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-05 04:27:55 +00:00

docs(xcat-core): RespawnUtils does not say what it is for

The module is seven short subs over a hash of counters, and nothing in it says what is
being paced or why the pacing is shaped this way. A reader can follow every line and still
not know that the delay ceilings rather than terminates, that `healthy` means "stayed up
long enough to have claimed its resource", or that returning a new state instead of
mutating one is load-bearing rather than stylistic.

Add a short header giving the module's intent -- what it paces, why it backs off, why it
never stops, and why the functions are pure -- and one line per sub in
(inputs) -> output form. Not the banner from
docs/source/developers/guides/code/code_standard.rst: every one of these takes a state and
a timestamp, so seven Arguments:/Returns: blocks would restate the same signature and bury
the line that carries meaning.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-08-27 16:48:11 -03:00
parent 5ef9d65a80
commit dfdc4f4293
+39
View File
@@ -1,15 +1,46 @@
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT::RespawnUtils;
# Pacing for a parent that has to keep a child alive; xcatd's install monitor is the caller.
#
# The child is re-forked whenever it dies, but not as fast as fork() returns -- it may be
# dying because a resource it needs is held by someone else, and retrying flat out burns CPU
# and interferes with whatever handshake it performs to claim that resource. So attempts back
# off, doubling from min_interval to max_interval and then holding there.
#
# It never stops retrying. A retry budget that runs out cannot be refilled, because with no
# child alive nothing is left to reset it, so the resource would stay unserved until the whole
# daemon is restarted -- the failure the respawn exists to prevent. A child that stayed up
# `healthy` seconds evidently did claim its resource and serve, so its death resets the delay
# and only a real streak of failures to start builds the backoff up.
#
# Every function returns a NEW state and never mutates the one it is handed. That is what
# makes them safe to call from a SIGCHLD handler: the result is complete before the caller's
# assignment installs it, so a signal cannot catch the pacing half-written.
#
# The state is a plain hash. Callers may read these; use the functions below to get the next
# state rather than writing to them.
#
# min_interval shortest wait between attempts, and what a healthy run resets the delay to
# max_interval longest wait -- the delay doubles up to this and then stays here
# healthy how long a child must survive before we count it as having served
# delay how long to wait after the NEXT failure
# next_at earliest time() at which another attempt is allowed
# started_at when the running child was forked, or undef when none is running
# streak how many children in a row have died young
# reported whether we have already logged that this streak reached the ceiling
use strict;
use warnings;
# Read one tunable, falling back to the default unless it really looks like a whole number.
sub _tunable {
my ($value, $default) = @_;
return $default unless defined($value) && $value =~ /^\s*\d+\s*$/;
return $value + 0;
}
# Start pacing a child from scratch. Anything the caller leaves out gets a sensible default.
sub policy {
my (%opt) = @_;
@@ -32,16 +63,21 @@ sub policy {
};
}
# Is it time to try again yet? This can say "not yet", but it never says "no more".
sub due {
my ($state, $now) = @_;
return $now >= $state->{next_at} ? 1 : 0;
}
# Note that we are about to fork, so we can tell later how long the child lasted. Call this
# before forking: the child can die and be reaped before fork() even returns to us.
sub forked {
my ($state, $now) = @_;
return { %$state, started_at => $now };
}
# Note that the child died, and decide when to try again -- straight away if it had been up
# long enough to have served, later and later if it keeps failing to start.
sub exited {
my ($state, $now) = @_;
@@ -65,6 +101,8 @@ sub exited {
return \%next;
}
# Has this run of failures just hit the ceiling, and not been mentioned yet? Keeps the log to
# one line per streak instead of one per attempt.
sub should_report {
my ($state) = @_;
return 0 if $state->{reported};
@@ -72,6 +110,7 @@ sub should_report {
return $state->{delay} >= $state->{max_interval} ? 1 : 0;
}
# Remember that we have already logged the ceiling for this streak.
sub reported {
my ($state) = @_;
return { %$state, reported => 1 };