Test2::Harness::Stall::Detector(3)

NAME
Test2::Harness::Stall::Detector - Notice when the scheduler has stopped starting tests.

DESCRIPTION
A stall is new tests not being started even though tests are pending and there is capacity to start them. Whether other tests are currently running is irrelevant; a stall is about what is not being started.

This class decides when that has happened. It is driven from the main "yath test" process, which is healthy during a stall and is an ancestor of the scheduler. It adds no bookkeeping to the runner: everything it needs is already in the run's own "dispatch.jsonl".

Two readers are involved. A raw Test2::Harness::Util::Queue supplies the record stamps, because "last_job_activity" moves when a test stops as well as when one starts. A Test2::Harness::Runner::State replay supplies the counts.

The replay is built with an explicit in-tree "resources" list. "Test2::Harness::Runner::State" only constructs the resource classes named in the settings when none are supplied, so passing one keeps release() away from user code -- the callback most likely to be wedged when this fires. Do not remove that argument.

Nothing here may end a healthy run, so the replay is wrapped and repeated failures disable reporting rather than repeating forever.

  WHAT IS NOT A STALL
Two waits are suppressed, because the scheduler is right to hold back and both can last as long as the longest running test: pending tests that are all "isolation" while something is running, and pending tests all blocked by a conflict already held. Reporting is also gated on a stage being currently ready, so a long preload is not mistaken for a stall.

SYNOPSIS
    use Test2::Harness::Stall::Detector;

    my %spec = Test2::Harness::Stall::Detector->parse_spec('600:1200')
        or return;

    my $detector = Test2::Harness::Stall::Detector->new(
        %spec,
        workdir   => $workdir,
        job_count => $job_count,
    );

    # Called often; it rate-limits itself.
    if (my $found = $detector->check) {
        ...
    }

ATTRIBUTES

$string = $detector->workdir()
    The run's working directory.

$int = $detector->job_count()
    Used only to build the replay's job limiter.

$seconds = $detector->strong()
    How long to wait before reporting while tests are pending and none are running.

$seconds = $detector->loose()
    How long to wait before reporting while tests are pending and others are still running.

PUBLIC METHODS

%spec = $class->parse_spec($string)
    Parses a "--stall-report" value, either "SECONDS" or "STRONG:LOOSE". Returns an empty list when reporting is disabled, and croaks on an unparseable value.

$hashref = $detector->check()
    Returns nothing unless a stall is being reported. Rate-limits itself, so it is safe to call from a busy loop. The returned hashref describes what was observed, and carries the replayed state, the pending tasks, and the scheduler and stage pids.

$hashref = $detector->stage_pids()
    Stage name to pid, for stages that are currently up. These feed the signal whitelist, so a stage that has gone down is removed.

$queue = $detector->queue()
    The raw dispatch queue reader, separate from the replay's own. Never "$state->dispatch_file": that reader is stateful and the main process needs it untouched so stop() can replay the whole file on Ctrl-C.

$bool = $detector->poll_stamps()
    Reads new records for their stamps and pids, returning false if the queue could not be read. "last_job_activity" cannot serve here, because it moves when a test stops as well as when one starts.

$state = $detector->build_state()
    Builds the observer state. See the warning above about the "resources" argument.

$state = $detector->replay()
    Polls the observer state, returning nothing if it could not be read. Repeated failures disable reporting rather than re-reading the whole queue every second for the rest of the run.

$arrayref = $detector->pending_tasks($state)
    Every pending task across all runs, flattened.

$bool = $detector->all_pending_blocked($state, $tasks)
    Whether every pending test is one the scheduler is right to be holding back: an "isolation" test while anything runs, an "immiscible" one while another immiscible runs, an exclusive claim on a name something else holds either way, or a shared claim on a name something holds exclusively. These mirror the rejections in "State::_next" and must follow it.

SOURCE
The source code repository for Test2-Harness can be found at <https://github.com/Test-More/Test2-Harness/>.

MAINTAINERS

Chad Granum <exodist7@gmail.com>

AUTHORS

Chad Granum <exodist7@gmail.com>

COPYRIGHT
Copyright Chad Granum <exodist7@gmail.com>.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

See <https://dev.perl.org/licenses/>