1Test2::Harness::Runner:U:sReersoCuorncter(i3b)uted PerlTDeosctu2m:e:nHtaartnieosns::Runner::Resource(3)
2
3
4

NAME

6       Test2::Harness::Runner::Resource - Base class for resource management
7       classes
8

DESCRIPTION

10       Sometimes you have limited resources that must be shared/divided
11       between tests that run concurrently. Resource classes give you a way to
12       leverage the IPC system used by Test2::Harness to manage resource
13       assignment and recovery.
14

SYNOPSIS

16       Here is a resource class that simply assigns an integer to each test.
17       It would be possible to re-use integers, but since there are infinite
18       integers this example is kept simple and just always grabs the next
19       one.
20
21           package Test2::Harness::Runner::Resource::Foo;
22           use strict;
23           use warnings;
24
25           use parent 'Test2::Harness::Runner::Resource';
26
27           sub setup {
28               my $class = shift; # NOT AN INSTANCE
29               ...
30           }
31
32           sub available {
33               my $self = shift;
34               my ($task) = @_;
35
36               # There are an infinite amount of integers, so we always return true
37               return 1;
38           }
39
40           sub assign {
41               my $self = shift;
42               my ($task, $state) = @_;
43
44               # Next ID, do not record the state change yet!
45               my $id = 1 + ($self->{ID} //= 0);
46
47               print "ASSIGN: $id = $task->{job_id}\n";
48
49               # 'record' should get whatever we need to record the resource, whatever you
50               # pass in will become the argument to the record() sub below. This may be a
51               # scalar, a hash, an array, etc. It will be serialized to JSON before
52               # record() sees it.
53               $state->{record} = $id;
54
55               # Pass the resource into the test, this can be done as envronment variables
56               # and/or arguments to the test (@ARGV).
57               $state->{env_vars}->{FOO_ID} = $id;
58               push @{$state->{args}} => $id;
59
60               # The return is ignored.
61               return;
62           }
63
64           sub record {
65               my $self = shift;
66               my ($job_id, $record_arg_from_assign) = @_;
67
68               # The ID from $state->{record}->{$pkg} in assign.
69               my $id = $record_arg_from_assign;
70
71               # Update our internal state to reflect the new ID.
72               $self->{ID} = $id;
73
74               # Add a mapping of what job ID gets what integer ID.
75               $self->{ID_TO_JOB_ID}->{$id}     = $job_id;
76               $self->{JOB_ID_TO_ID}->{$job_id} = $id;
77
78               print "RECORD: $id = $job_id\n";
79
80               # The return is ignored
81           }
82
83           sub tick {
84               my $self = shift;
85
86               # This is called by only 1 process at a time and gives you a way to do
87               # extra stuff at a regular interval without other processes trying to
88               # do the same work at the same time.
89               # For example, if a database is left in a dirty state after it is
90               # released, you can fire off a cleanup action here knowing no other
91               # process will run it at the same time. You can also be sure no record
92               # messages will be sent while this sub is running as the process it
93               # runs in has a lock.
94
95               ...
96           }
97
98
99           sub release {
100               my $self = shift;
101               my ($job_id) = @_;
102
103               # Clear the internal mapping, the integer ID is now free. Theoretically it
104               # can be reused, but this example is not that complex.
105               my $id = delete $self->{JOB_ID_TO_ID}->{$job_id};
106
107               # This is called for all tests that complete, even if they did not use
108               # this resource, so we return if the job_id is not applicable.
109               return unless defined $id;
110
111               delete $self->{ID_TO_JOB_ID}->{$id};
112
113               print "  FREE: $id = $job_id\n";
114
115               # The return is ignored
116           }
117
118           sub cleanup {
119               my $self = shift;
120
121               print "CLEANUP!\n";
122           }
123
124           1;
125
126       The print statements generated will look like this when running 2 tests
127       concurrently:
128
129           yath test -R Foo -j2 t/testA.t t/testB.t
130           [...]
131           (INTERNAL)     ASSIGN: 1 = 4F7CF5F6-E43F-11EA-9199-24FCBF610F44
132           (INTERNAL)     RECORD: 1 = 4F7CF5F6-E43F-11EA-9199-24FCBF610F44
133           (INTERNAL)     ASSIGN: 2 = E19CD98C-E436-11EA-8469-8DF0BF610F44
134           (INTERNAL)     RECORD: 2 = E19CD98C-E436-11EA-8469-8DF0BF610F44
135           (INTERNAL)       FREE: 1 = 4F7CF5F6-E43F-11EA-9199-24FCBF610F44
136           (INTERNAL)       FREE: 2 = E19CD98C-E436-11EA-8469-8DF0BF610F44
137           (INTERNAL)     CLEANUP!
138           [...]
139
140       Depending on the tests run the 'FREE' prints may be out of order.
141

WORKFLOW

143   HOW STATE IS MANAGED
144       Depending on your preload configuration, yath may have several runners
145       launching tests. If a runner has nothing to do it will lock the queue
146       and try to find the next test that should be run. Only 1 of the runners
147       will be in control of the queue at any given time, but the control of
148       the queue may pass between runners. To manage this there is a mechanism
149       to record messages that allow each runner to maintain a copy of the
150       current state.
151
152   CHECK IF RESOURCES ARE AVAILABLE
153       Each runner will have an instance of your resource class. When the
154       runner is in control of the queue, and wants to designate the next test
155       to run, it will check with the resource classes to make sure the
156       correct resources are available. To do that it will call
157       available($task) on each resource instance.
158
159       The $task will contain the specification for the test, it is a hashref,
160       and you SHOULD NOT modify it. The only key most people care about is
161       the 'file' key, which has the test file that will be run if resources
162       are available.
163
164       If resources are available, or if the specific file does not need the
165       resource, the available() method should return true. If the file does
166       need your resource(s), and none are available, this should return
167       false. If any resource class returns false it means the test cannot be
168       run yet and the runner will look for another test to run.
169
170   ASSIGN A RESOURCE
171       If the runner has determined the test can be run, and all necessary
172       resources are available, it will then call "assign($task, $state)" on
173       all resource class instances. At this time the resource class should
174       decide what resource(s) to assign to the class.
175
176       CRITICAL NOTE: the assing() method MUST NOT alter any internal state on
177       the resource class instance. State modification must wait for the
178       record() method to be called. This is because the assign() method is
179       only called in one runner process, the record() method call will happen
180       in every runner process to insure they all have the same internal
181       state.
182
183       The assign() sub should modify the $state hash, which has 3 keys:
184
185       env_vars => {}
186           Env vars to set for the test
187
188       args => []
189           Arguments to pass to the test
190
191       record => ...
192           Data needed to record the state change for resource classes. Can be
193           a scalar, hashref, arrayref, etc. It will be serialized to JSON to
194           be passed between processes.
195
196   RECORD A RESOURCE
197       Once a resource is assigned, a message will be sent to all runner
198       processes INCLUDING THE ONE THAT DID THE ASSIGN that says it should
199       call "record($job_id, $record_val)" on your resource class instance.
200       Your resource class instance must use this to update the state so that
201       once done ALL processes will have the proper internal state.
202
203       The $record_val is whatever you put into "$state->{record}" in the
204       assign() method above.
205
206   QUEUE MANAGEMENT IS UNLOCKED
207       Once the above has been done, queue management will be unlocked. You
208       can be guarenteed that only one process will be run the available(),
209       and assign() sequence at a time, and that they will be called in order,
210       though assign() may not be called if another resource was not
211       available. If assign() is called, you can be guarenteed that all
212       processes, including the one that called assign() will have their
213       record() called with the proper argument BEFORE they try to manage the
214       queue (which is the only place resources are checked or assigned).
215
216   RELEASE A RESOURCE
217       Whenever a process that is using a resource exits, the runner that
218       waits on that process will eventually send an IPC message announcing
219       that the job_id has completed. Every time a job_id completes the
220       release($job_id) method will be called on your resource class in all
221       runner processes. This allows the state to be updated to reflect the
222       freed resource.
223
224       You can be guarenteed that any process that locks the queue to run a
225       new test will eventually see the message. The message may come in
226       during a loop that is checking for resources, in which case the state
227       will not reflect the resource being available, however in such cases
228       the loop will end and be called again later with the message having
229       been receieved. There will be no deadlock due to a queue manager
230       waiting for the message.
231
232       There are no guarentees about what order resources will be released in.
233

METHODS

235       $class->setup($settings)
236           This will be called once before the runner forks or initialized
237           per-process instances. If you have any "setup once" tasks to
238           initialize resources before tests run this is a good place to do
239           it.
240
241           This runs immedietly after plugin setup() methods are called.
242
243           NOTE: Do not rely on recording any global state here, the runner
244           and per-process instances may not be forked from the process that
245           calls setup().
246
247       $res = $class->new(settings => $settings);
248           A default new method, returns a blessed hashref with the settings
249           key set to the Test2::Harness::Settings instance.
250
251       $val = $res->available(\%task)
252           DO NOT MODIFY ANY INTERNAL STATE IN THIS METHOD
253
254           DO NOT MODIFY THE TASK HASHREF
255
256           Returns a positive true value if the resource is available.
257
258           Returns false if the resource is not available, but will be in the
259           future (IE in use by another test, but will be free when that test
260           is done).
261
262           Returns a negative value if the resource is not available and never
263           will be.  This will cause any tests dependent on the resource to be
264           skipped.
265
266           The only key in "\%task" hashref that most resources will care
267           about is the 'file' key, which contains the test file to be run.
268
269       $res->assign(\%task, \%state)
270           DO NOT MODIFY THE TASK HASHREF
271
272           DO NOT MODIFY ANY INTERNAL STATE IN THIS METHOD
273
274           If the task does not need any resources you may simply return.
275
276           If resources are needed you should deduce what resources to assign.
277
278           You should put any data needed to update the internal state of your
279           resource instance in the "$state->{record}" hash key. It WILL be
280           serialized to JSON before being used as an argument to record().
281
282               $state->{record} = $id;
283
284           If you do not set the 'record' key, or set it to undef, then the
285           record() method will not be called.
286
287           If your tests need to know what resources to use, you may set
288           environment variables and/or command line arguments to pass into
289           the test (@ARGV).
290
291               $state->{env_vars}->{FOO_ID} = $id;
292               push @{$state->{args}} => $id;
293
294           The "\%state" hashref is used only by your instance, you are free
295           to fully replace the 'env_vars' and 'args' keys. They will
296           eventually be merged into a master state along with those of other
297           resources, but this ref is exclusive to you in this method.
298
299       $inst->record($job_id, $record_arg_from_assign)
300           NOTE: THIS MAY BE CALLED IN MUTLIPLE PROCESSES CONCURRENTLY.
301
302           This will be called in all processes so that your instance can
303           update any internal state.
304
305           The $job_id variable contains the id for the job to which the
306           resource was assigned. You should use this to record any internal
307           state. The $job_id will be passed to release() when the job
308           completes and no longer needs the resource.
309
310           This is intended only for modifying internal state, you should not
311           do anything in this sub that will explode if it is also done in
312           another process at the same time with the same arguments. For
313           example creating a database should not be done here, multiple
314           processes will fight to do the create. The creation, if necessary
315           should be done in assign() which will be called in only one
316           process.
317
318       $inst->release($job_id)
319           NOTE: THIS MAY BE CALLED IN MUTLIPLE PROCESSES CONCURRENTLY.
320
321           This will be called for every test job that completes, even if it
322           did not use this resource. If the job_id did not use the resource
323           you may simply return, otherwise update the internal state to
324           reflect that the resource is no longer in use.
325
326           This is intended only for modifying internal state, you should not
327           do anything in this sub that will explode if it is also done in
328           another process at the same time with the same arguments. For
329           example deleting a database should not be done here, multiple
330           processes will fight to do the delete. assign() is the only method
331           that will be run in a single process, so if a database needs to be
332           cleaned before it can be used you should clean it there. Any final
333           cleanup should be done in cleanup() which will only be called by
334           one process at the very end.
335
336       $inst->cleanup()
337           This will be called once by the parent runner process just before
338           it exits.  This is your chance to do any final cleanup tasks such
339           as deleting databases that are no longer going to be used by tests
340           as no more will be run.
341
342       $inst->tick()
343           This is called by only 1 process at a time and gives you a way to
344           do extra stuff at a regular interval without other processes trying
345           to do the same work at the same time.
346
347           For example, if a database is left in a dirty state after it is
348           released, you can fire off a cleanup action here knowing no other
349           process will run it at the same time. You can also be sure no
350           record messages will be sent while this sub is running as the
351           process it runs in has a lock.
352
353       $inst->refresh()
354           Called once before each resource-request loop. This is your chance
355           to do things between each set of requests for resources.
356
357       $bool = $inst->job_limiter()
358           True if your resource is intended as a job limiter (IE alternative
359           to specifying -jN at the command line).
360
361       $int = $inst->job_limiter_max()
362           Max number of jobs this will allow at the moment, if this resource
363           is a job limiter.
364
365       $bool = $inst->job_limiter_at_max()
366           True if the limiter has reached its maximum number of running jobs.
367           This is used to avoid a resource-allocation loop as an
368           optimization.
369
370       $number = $inst->sort_weight()
371           Used to sort resources if you want them to be checked in a specific
372           order. For most resources this defaults to 50. For job_limiter
373           resources this defaults to 100. Lower numbers are sorted to the
374           front of the list, IE they are aquired first, before other
375           resources.
376
377           Job slots are sorted later (100) so that we do not try to grab a
378           job slot if other resources are not available.
379
380           Most of the time order will not matter, however with Shared job
381           slots we have a race with other test runs to get slots, and
382           checking availability is enough to consume a slot, even if other
383           resources are not available.
384
385       $string = $inst->status_lines()
386           Get a (multi-line) string with status info for this resource. This
387           is used to populate the output for the "yath resources" command.
388
389           The default implementation will build a string from the data
390           provided by the status_data() method.
391
392       $arrayref = $inst->status_data()
393           The default implementation returns an empty list.
394
395           This should return status data that looks like this:
396
397               return [
398                   {
399                       title  => "Resource Group Title",
400                       tables => [
401                           {
402                               header => \@columns,
403                               rows   => [
404                                   \@row1,
405                                   \@row2,
406                               ],
407
408                               # Optional fields
409                               ##################
410
411                               # formatting for fields in rows
412                               format => [undef, undef, 'duration', ...],
413
414                               # Title for the table
415                               title => "Table Title",
416
417                               # Options to pass to Term::Table if/when it the data is used in Term::Table
418                               term_table_opts => {...},
419                           },
420
421                           # Any number of tables is ok
422                           {...},
423                       ],
424                   },
425
426                   # Any number of groups is ok
427                   {...},
428               ];
429
430           Currently the only supported formats are 'default' (undef), and
431           'duration'.  Duration takes a stamp and tells you how much time has
432           passed since the stamp.
433

SOURCE

435       The source code repository for Test2-Harness can be found at
436       http://github.com/Test-More/Test2-Harness/.
437

MAINTAINERS

439       Chad Granum <exodist@cpan.org>
440

AUTHORS

442       Chad Granum <exodist@cpan.org>
443
445       Copyright 2020 Chad Granum <exodist7@gmail.com>.
446
447       This program is free software; you can redistribute it and/or modify it
448       under the same terms as Perl itself.
449
450       See http://dev.perl.org/licenses/
451
452
453
454perl v5.36.1                      2023-10-04Test2::Harness::Runner::Resource(3)
Impressum