Skip to content

Commit db62740

Browse files
committed
feat(throttling): throttle jobs resources based on parameters size
step 1: define a priority scaling for jobs resources based on a configuration assign the configuration to parameter prio_throttling_parameters in openqa.ini step 2: apply changes requested in PR#6952 Apply the priority throttling as by ticket's Suggestions in the description use configuration format "<parameter_name>:<scale_decimal>:<reference_valuee>,..." Set throttling code in create_from_settings, Add 2 subtests in priority tests of t/api/04-jobs.t step 3: move in a routine load_prio_throttling the format checking for throttling load the resulting hash after validated the throtting configuration and fix _load_prio_throttling,in Setup.pm calculate the resulting priority and update debug_message,in Jobs.pm update tests t/api/04-jobs.t and t/config.t to manage the throttling update the throttling description in openqa.ini fix comments in sub load_throttling_config Ref. ticket poo#192952
1 parent cb768c9 commit db62740

File tree

6 files changed

+147
-1
lines changed

6 files changed

+147
-1
lines changed

etc/openqa/openqa.ini

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,11 @@ concurrent = 0
369369
## scheduled products without any jobs (e.g. due to errors) are not immediately
370370
## cleaned up.
371371
#scheduled_product_min_storage_duration = 34
372+
## Throttling: set a comma-separated list of test parameters that trigger throttling
373+
## of scheduled openQA jobs by priority. Example: PARAM1:SCALE1[:THRESH1][,...]
374+
## Set a scale that a parameter value, minus an optional threshold, is multiplied by
375+
## and added to each job prio, as: '$base_prio + $scale# * ($param# - $thresh#)'
376+
#prio_throttling_parameters =
372377

373378
[archiving]
374379
## Moves logs of jobs which are preserved during the cleanup because they are

lib/OpenQA/Schema/ResultSet/Jobs.pm

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,21 @@ sub create_from_settings ($self, $settings, $scheduled_product_id = undef) {
158158
$new_job_args{priority} += $malus;
159159
}
160160
}
161+
# apply resources throttling control
162+
if (my $throttling = OpenQA::App->singleton->config->{misc_limits}->{prio_throttling_data}) {
163+
my $throttling_info;
164+
for my $resource (keys %$throttling) {
165+
next if !defined $settings{$resource};
166+
my $scale = $throttling->{$resource}->{scale};
167+
my $reference = $throttling->{$resource}->{reference};
168+
my $prio = int(($settings{$resource} - $reference) * $scale);
169+
$throttling_info .= $resource . ", scale: $scale" . ($reference ? ", reference: $reference;" : ';');
170+
$new_job_args{priority} += $prio;
171+
}
172+
$debug_msg .= sprintf("\nAdjusting job priority by %s based on resource requirement(s): %s",
173+
$new_job_args{priority}, $throttling_info)
174+
if $throttling_info;
175+
}
161176

162177
my $job = $self->create(\%new_job_args);
163178
log_debug(sprintf "(Job %d) $debug_msg", $job->id) if $debug_msg;

lib/OpenQA/Setup.pm

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,29 @@ sub _load_config ($app, $defaults, $mode_specific_defaults) {
5454
_read_config_file($config, $config_file, $defaults, $mode_defaults);
5555
$config->{ini_config} = $config_file;
5656
}
57-
5857
# ensure default values are assigned; warn if config files were supplied at all
5958
_read_config_file($config, undef, $defaults, $mode_defaults);
6059
$app->log->warn('No configuration files supplied, will fallback to default configuration') unless $config_file;
6160
return $config;
6261
}
6362

63+
sub _load_prio_throttling ($app, $config) {
64+
return
65+
unless my $throttling = $config->{misc_limits}->{prio_throttling_parameters};
66+
# floating point number
67+
my $n = qr/[+-]?(?:\d+\.?\d*|\.\d+)/;
68+
# Unit format = Param.name: scale: reference(optional)
69+
my $u = qr/([A-Z_][A-Z0-9_]*):($n)(?::($n))?/i;
70+
$throttling =~ s/\s+//g;
71+
unless ($throttling =~ qr/^$u(?:,$u)*$/i) {
72+
$app->log->warn("Wrong format in openqa.ini 'prio_throttling_parameters': $throttling");
73+
return;
74+
}
75+
my %hash = map { my ($k, $s, $r) = $_ =~ /$u/g; $k => {scale => $s, reference => $r // 0} }
76+
split(',', $throttling);
77+
return \%hash;
78+
}
79+
6480
sub read_config ($app) {
6581
my %defaults = (
6682
global => {
@@ -243,6 +259,8 @@ sub read_config ($app) {
243259
mcp_max_result_size => 500000,
244260
max_job_time_prio_scale => 100,
245261
scheduled_product_min_storage_duration => 34,
262+
prio_throttling_parameters => '',
263+
prio_throttling_data => undef,
246264
},
247265
archiving => {
248266
archive_preserved_important_jobs => 0,
@@ -293,6 +311,7 @@ sub read_config ($app) {
293311
if (my $minion_fail_job_blocklist = $config->{influxdb}->{ignored_failed_minion_jobs}) {
294312
$config->{influxdb}->{ignored_failed_minion_jobs} = [split(/\s+/, $minion_fail_job_blocklist)];
295313
}
314+
$config->{misc_limits}->{prio_throttling_data} = _load_prio_throttling($app, $config);
296315
my $results = delete $global_config->{parallel_children_collapsable_results};
297316
$global_config->{parallel_children_collapsable_results_sel}
298317
= ' .status' . join('', map { ":not(.result_$_)" } split(/\s+/, $results));

t/api/04-jobs.t

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,42 @@ subtest 'priority correctly assigned when posting job' => sub {
10521052
$t->json_is('/job/priority', 50, 'feature disabled: prio value unchanged');
10531053
};
10541054

1055+
subtest 'priority scaled up due to QEMURAM demand' => sub {
1056+
my $add = 20;
1057+
local $jobs_post_params{QEMURAM} = 4096;
1058+
1059+
my $config = OpenQA::Setup::read_config($t->app);
1060+
$config->{misc_limits}->{prio_throttling_parameters} = 'XXX :0.2, QEMURAM:0.01:2048';
1061+
$config->{misc_limits}->{prio_throttling_data} = OpenQA::Setup::_load_prio_throttling($t->app, $config);
1062+
$t->post_ok('/api/v1/jobs', form => \%jobs_post_params)->status_is(200);
1063+
$t->get_ok('/api/v1/jobs/' . $t->tx->res->json->{id})->status_is(200);
1064+
$t->json_is('/job/priority', 50 + $add, 'increased prio value');
1065+
};
1066+
1067+
subtest 'priority improved due to QEMURAM low demand' => sub {
1068+
my $add = -10;
1069+
local $jobs_post_params{QEMURAM} = 1024;
1070+
1071+
my $config = OpenQA::Setup::read_config($t->app); # OpenQA::App->singleton->config;
1072+
$config->{misc_limits}->{prio_throttling_parameters} = 'XXX :0.2, QEMURAM:0.01:2048';
1073+
$config->{misc_limits}->{prio_throttling_data} = OpenQA::Setup::_load_prio_throttling($t->app, $config);
1074+
$t->post_ok('/api/v1/jobs', form => \%jobs_post_params)->status_is(200);
1075+
$t->get_ok('/api/v1/jobs/' . $t->tx->res->json->{id})->status_is(200);
1076+
$t->json_is('/job/priority', 50 + $add, 'decreased prio value');
1077+
};
1078+
1079+
subtest 'priority scaled up due to HDDSIZEGB demand' => sub {
1080+
my $add = 2;
1081+
local $jobs_post_params{HDDSIZEGB} = 40;
1082+
my $config = OpenQA::Setup::read_config($t->app); # OpenQA::App->singleton->config;
1083+
$config->{misc_limits}->{prio_throttling_parameters}
1084+
= 'XXX :0.2, FAKE_HDDSIZEGB:0.01, HDDSIZEGB:0.05, YYY: 0.1';
1085+
$config->{misc_limits}->{prio_throttling_data} = OpenQA::Setup::_load_prio_throttling($t->app, $config);
1086+
$t->post_ok('/api/v1/jobs', form => \%jobs_post_params)->status_is(200);
1087+
$t->get_ok('/api/v1/jobs/' . $t->tx->res->json->{id})->status_is(200);
1088+
$t->json_is('/job/priority', 50 + $add, 'increased prio value');
1089+
};
1090+
10551091
# post new job in job group with customized default priority
10561092
$schema->resultset('JobGroups')->find({name => 'opensuse test'})->update({default_priority => 42});
10571093
$jobs_post_params{_GROUP} = 'opensuse test';

t/config-prio.t

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Copyright 2014-2021 SUSE LLC
2+
# SPDX-License-Identifier: GPL-2.0-or-later
3+
4+
use Test::Most;
5+
6+
use FindBin;
7+
use lib "$FindBin::Bin/lib", "$FindBin::Bin/../external/os-autoinst-common/lib";
8+
9+
use Test::MockObject;
10+
use Test::Warnings ':report_warnings';
11+
use Mojo::Base -signatures;
12+
use Mojolicious;
13+
use OpenQA::Setup;
14+
15+
my $quiet_log = Mojo::Log->new(level => 'warn');
16+
17+
subtest 'check throttling configuration validation and application' => sub {
18+
my $warn_called = 0;
19+
my $log_mock = Test::MockObject->new();
20+
my $app_mock = Test::MockObject->new();
21+
$log_mock->mock('warn', sub { $warn_called = 1; });
22+
$app_mock->mock('log', sub { $log_mock });
23+
my $config = OpenQA::App->set_singleton(my $app = Mojolicious->new(log => $quiet_log));
24+
# my $config = OpenQA::Setup::read_config($app);
25+
26+
subtest 'invalid prio_throttling_parameters' => sub {
27+
$config->{misc_limits}->{prio_throttling_parameters} = 'invalid';
28+
$config->{misc_limits}->{prio_throttling_data} = OpenQA::Setup::_load_prio_throttling($app_mock, $config);
29+
is_deeply $config->{misc_limits}->{prio_throttling_data}, undef,
30+
'prio_throttling_data is empty hash for invalid';
31+
is $warn_called, 1, 'warning called for invalid format';
32+
};
33+
subtest 'no prio_throttling_parameters' => sub {
34+
$warn_called = 0;
35+
$config->{misc_limits}->{prio_throttling_parameters} = undef;
36+
$config->{misc_limits}->{prio_throttling_data} = OpenQA::Setup::_load_prio_throttling($app_mock, $config);
37+
is $config->{misc_limits}->{prio_throttling_data}, undef, 'prio_throttling_data is undef when no parameters';
38+
is $warn_called, 0, 'no warning for undef';
39+
};
40+
subtest 'empty string' => sub {
41+
$warn_called = 0;
42+
$config->{misc_limits}->{prio_throttling_parameters} = '';
43+
$config->{misc_limits}->{prio_throttling_data} = OpenQA::Setup::_load_prio_throttling($app_mock, $config);
44+
is $config->{misc_limits}->{prio_throttling_data}, undef, 'prio_throttling_data is undef for empty string';
45+
is $warn_called, 0, 'no warning for empty';
46+
};
47+
subtest 'valid prio_throttling_parameter with space separators' => sub {
48+
$warn_called = 0;
49+
$config->{misc_limits}->{prio_throttling_parameters} = 'KEY1ONE: 1.5';
50+
$config->{misc_limits}->{prio_throttling_data} = OpenQA::Setup::_load_prio_throttling($app_mock, $config);
51+
is_deeply $config->{misc_limits}->{prio_throttling_data},
52+
{KEY1ONE => {scale => 1.5, reference => 0}},
53+
'prio_throttling_data parsed correctly';
54+
};
55+
subtest 'valid multiple prio_throttling_parameters keys' => sub {
56+
$warn_called = 0;
57+
$config->{misc_limits}->{prio_throttling_parameters} = 'A:1.04,B:2:3,C:0.04:5';
58+
$config->{misc_limits}->{prio_throttling_data} = OpenQA::Setup::_load_prio_throttling($app_mock, $config);
59+
is_deeply $config->{misc_limits}->{prio_throttling_data},
60+
{
61+
A => {scale => 1.04, reference => 0},
62+
B => {scale => 2, reference => 3},
63+
C => {scale => 0.04, reference => 5}
64+
},
65+
'prio_throttling_data parses multiple correctly';
66+
};
67+
};
68+
69+
done_testing();

t/config.t

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ subtest 'Test configuration default modes' => sub {
193193
mcp_max_result_size => 500000,
194194
max_job_time_prio_scale => 100,
195195
scheduled_product_min_storage_duration => 34,
196+
prio_throttling_parameters => '',
196197
},
197198
archiving => {
198199
archive_preserved_important_jobs => 0,
@@ -215,6 +216,7 @@ subtest 'Test configuration default modes' => sub {
215216
$test_config->{_openid_secret} = $config->{_openid_secret};
216217
$test_config->{logging}->{level} = 'debug';
217218
$test_config->{global}->{service_port_delta} = 2;
219+
$test_config->{misc_limits}->{prio_throttling_data} = undef;
218220
is ref delete $config->{global}->{auto_clone_regex}, 'Regexp', 'auto_clone_regex parsed as regex';
219221
ok delete $config->{'test_preset example'}, 'default values for example tests assigned';
220222
is_deeply $config, $test_config, '"test" configuration';

0 commit comments

Comments
 (0)