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/