Ctrl K
ComunidadGuías…

script/benchmark-operations

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env perl
# SPDX-FileCopyrightText: 2026 GPForum Contributors
# SPDX-License-Identifier: BSD-3-Clause

package main;
use v5.40;
use Carp qw(croak);
use Const::Fast;
use English    qw(-no_match_vars);
use File::Path qw(make_path);
use File::Spec;
use FindBin;
use Getopt::Long qw(GetOptions);
use JSON::MaybeXS;
use List::Util  qw(any);
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
use lib "$FindBin::Bin/lib", "$FindBin::Bin/../lib";
use GPForum::Performance::Operations qw(summarize inventory);

our $VERSION = '0.001';
const my $EXIT_SHIFT  => 8;
const my $SIGNAL_MASK => 127;
exit main();

sub main {
    my $option = _options();
    my $json   = JSON::MaybeXS->new( canonical => 1, pretty => 1 );
    return 0 if _print_only( $option, $json );
    my $output = File::Spec->rel2abs( $option->{output} );
    croak 'Refusing an existing output directory; use a new run directory'
      if -e $output;
    my $tests = _tests($option);
    make_path("$output/traces");
    my $started = clock_gettime(CLOCK_MONOTONIC);
    my @runs;

    for my $repeat ( 1 .. $option->{repetitions} ) {
        push @runs, _run( $output, $option, $tests, $repeat );
    }
    my $report = summarize("$output/traces");
    $report->{runs}             = \@runs;
    $report->{jobs}             = $option->{jobs};
    $report->{duration_seconds} = clock_gettime(CLOCK_MONOTONIC) - $started;
    my $failed = any { $_->{exit_code} || $_->{signal} } @runs;
    $report->{status} =
      @{ $report->{measurements} } && !$failed ? 'ok' : 'fail';
    _write( "$output/operations.json", $json->encode($report) );
    print $json->encode(
        {
            status           => $report->{status},
            coverage         => $report->{coverage},
            report           => "$output/operations.json",
            duration_seconds => $report->{duration_seconds}
        }
    ) or croak 'Cannot write benchmark summary';
    return $report->{status} eq 'ok' ? 0 : 1;
}

sub _options {
    my %option = ( jobs => 1, repetitions => 1 );
    GetOptions( \%option, 'output=s', 'jobs=i', 'repetitions=i', 'test=s@',
        'inventory', 'summarize', 'help' )
      or croak 'Invalid benchmark options';
    croak 'Use --output DIR and positive jobs/repetitions'
      if @ARGV || $option{jobs} < 1 || $option{repetitions} < 1;
    croak 'Use --output DIR'
      if !$option{help} && !$option{inventory} && !$option{output};
    return \%option;
}

sub _print_only ( $option, $json ) {
    if ( $option->{help} ) {
        print
"Usage: script/gpforum-carton exec script/benchmark-operations --output DIR [--test t/integration/FILE.t] [--jobs 1] [--repetitions 1]\nRuns all PostgreSQL integration workflows by default, on temporary clusters. DIR must be new. --inventory lists the current operation catalogue. --summarize rebuilds a report from DIR/traces.\n"
          or croak 'Cannot write usage';
        return 1;
    }
    if ( $option->{inventory} ) {
        print $json->encode( inventory() ) or croak 'Cannot write inventory';
        return 1;
    }
    my $output = File::Spec->rel2abs( $option->{output} );
    if ( $option->{summarize} ) {
        my $report = summarize("$output/traces");
        if ( -f "$output/operations.json" ) {
            open my $previous, '<:raw', "$output/operations.json"
              or croak 'Cannot open previous report';
            my $text =
              do { local $INPUT_RECORD_SEPARATOR = undef; <$previous> };
            close $previous or croak 'Cannot close previous report';
            $report = { %{ $json->decode($text) }, %{$report} };
        }
        _write( "$output/operations.json", $json->encode($report) );
        return 1;
    }
    return 0;
}

sub _tests ($option) {
    my @tests = @{ $option->{test} // ['t/integration'] };
    for my $test (@tests) {
        croak 'Tests must be existing files/directories below t/integration'
          if $test !~ m{\At/integration(?:/[[:alnum:]_.-]+)*\z}msx
          || $test =~ m{(?:\A|/)[.][.](?:/|\z)}msx
          || !-e $test;
    }
    return \@tests;
}

sub _run ( $output, $option, $tests, $repeat ) {
    local %ENV = %ENV;

    # Avoid accidentally using a server configured in the caller's shell.
    delete @ENV{
        qw(GPFORUM_DATABASE_DSN GPFORUM_TEST_DSN GPFORUM_DATABASE_USER GPFORUM_DATABASE_PASSWORD GPFORUM_POSTGRES_CONTROLLED_TEST)
    };
    local $ENV{GPFORUM_BENCHMARK_TRACE_DIR} = "$output/traces";
    my $pid = fork;
    croak "Cannot fork benchmark: $OS_ERROR" if !defined $pid;
    if ( !$pid ) {
        open STDOUT, '>', "$output/tests-$repeat.log"
          or croak 'Cannot open test log';
        open STDERR, '>&', \*STDOUT or croak 'Cannot redirect diagnostics';
        exec {$EXECUTABLE_NAME} $EXECUTABLE_NAME, '-S', 'prove', '-l', '-j',
          $option->{jobs},
          '-It/lib', '--exec',
'perl -Ilib -It/lib -Iscript/lib -MGPForum::Test::TemporaryPostgres -MGPForum::Performance::OperationProbe',
          '-r', @{$tests}
          or croak "Cannot execute prove: $OS_ERROR";
    }
    waitpid $pid, 0;
    return {
        repetition => $repeat,
        exit_code  => $CHILD_ERROR >> $EXIT_SHIFT,
        signal     => $CHILD_ERROR & $SIGNAL_MASK,
        log        => "tests-$repeat.log"
    };
}

sub _write ( $path, $text ) {
    open my $file, '>:raw', $path or croak "Cannot write report: $OS_ERROR";
    print {$file} $text or croak "Cannot write report: $OS_ERROR";
    close $file         or croak "Cannot close report: $OS_ERROR";
    return undef;
}