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 available {
28               my $self = shift;
29               my ($task) = @_;
30
31               # There are an infinite amount of integers, so we always return true
32               return 1;
33           }
34
35           sub assign {
36               my $self = shift;
37               my ($task, $state) = @_;
38
39               # Next ID, do not record the state change yet!
40               my $id = 1 + ($self->{ID} //= 0);
41
42               print "ASSIGN: $id = $task->{job_id}\n";
43
44               # 'record' should get whatever we need to record the resource, whatever you
45               # pass in will become the argument to the record() sub below. This may be a
46               # scalar, a hash, an array, etc. It will be serialized to JSON before
47               # record() sees it.
48               $state->{record} = $id;
49
50               # Pass the resource into the test, this can be done as envronment variables
51               # and/or arguments to the test (@ARGV).
52               $state->{env_vars}->{FOO_ID} = $id;
53               push @{$state->{args}} => $id;
54
55               # The return is ignored.
56               return;
57           }
58
59           sub record {
60               my $self = shift;
61               my ($job_id, $record_arg_from_assign) = @_;
62
63               # The ID from $state->{record}->{$pkg} in assign.
64               my $id = $record_arg_from_assign;
65
66               # Update our internal state to reflect the new ID.
67               $self->{ID} = $id;
68
69               # Add a mapping of what job ID gets what integer ID.
70               $self->{ID_TO_JOB_ID}->{$id}     = $job_id;
71               $self->{JOB_ID_TO_ID}->{$job_id} = $id;
72
73               print "RECORD: $id = $job_id\n";
74
75               # The return is ignored
76           }
77
78           sub release {
79               my $self = shift;
80               my ($job_id) = @_;
81
82               # Clear the internal mapping, the integer ID is now free. Theoretically it
83               # can be reused, but this example is not that complex.
84               my $id = delete $self->{JOB_ID_TO_ID}->{$job_id};
85
86               # This is called for all tests that complete, even if they did not use
87               # this resource, so we return if the job_id is not applicable.
88               return unless defined $id;
89
90               delete $self->{ID_TO_JOB_ID}->{$id};
91
92               print "  FREE: $id = $job_id\n";
93
94               # The return is ignored
95           }
96
97           sub cleanup {
98               my $self = shift;
99
100               print "CLEANUP!\n";
101           }
102
103           1;
104
105       The print statements generated will look like this when running 2 tests
106       concurrently:
107
108           yath test -R Foo -j2 t/testA.t t/testB.t
109           [...]
110           (INTERNAL)     ASSIGN: 1 = 4F7CF5F6-E43F-11EA-9199-24FCBF610F44
111           (INTERNAL)     RECORD: 1 = 4F7CF5F6-E43F-11EA-9199-24FCBF610F44
112           (INTERNAL)     ASSIGN: 2 = E19CD98C-E436-11EA-8469-8DF0BF610F44
113           (INTERNAL)     RECORD: 2 = E19CD98C-E436-11EA-8469-8DF0BF610F44
114           (INTERNAL)       FREE: 1 = 4F7CF5F6-E43F-11EA-9199-24FCBF610F44
115           (INTERNAL)       FREE: 2 = E19CD98C-E436-11EA-8469-8DF0BF610F44
116           (INTERNAL)     CLEANUP!
117           [...]
118
119       Depending on the tests run the 'FREE' prints may be out of order.
120

WORKFLOW

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

METHODS

214       $res = $class->new(settings => $settings);
215           A default new method, returns a blessed hashref with the settings
216           key set to the Test2::Harness::Settings instance.
217
218       $bool = $res->available(\%task)
219           DO NOT MODIFY ANY INTERNAL STATE IN THIS METHOD
220
221           DO NOT MODIFY THE TASK HASHREF
222
223           Return true if the resource is available, or if the task does not
224           require the resource.
225
226           Return false if the resource is not available, but is needed.
227
228           The only key in "\%task" hashref that most resources will care
229           about is the 'file' key, which contains the test file to be run.
230
231       $res->assign(\%task, \%state)
232           DO NOT MODIFY THE TASK HASHREF
233
234           DO NOT MODIFY ANY INTERNAL STATE IN THIS METHOD
235
236           If the task does not need any resources you may simply return.
237
238           If resources are needed you should deduce what resources to assign.
239
240           You should put any data needed to update the internal state of your
241           resource instance in the "$state->{record}" hash key. It WILL be
242           serialized to JSON before being used as an argument to "record()".
243
244               $state->{record} = $id;
245
246           If you do not set the 'record' key, or set it to undef, then the
247           "record()" method will not be called.
248
249           If your tests need to know what resources to use, you may set
250           environment variables and/or command line arguments to pass into
251           the test (@ARGV).
252
253               $state->{env_vars}->{FOO_ID} = $id;
254               push @{$state->{args}} => $id;
255
256           The "\%state" hashref is used only by your instance, you are free
257           to fully replace the 'env_vars' and 'args' keys. They will
258           eventually be merged into a master state along with those of other
259           resources, but this ref is exclusive to you in this method.
260
261       $inst->record($job_id, $record_arg_from_assign)
262           NOTE: THIS MAY BE CALLED IN MUTLIPLE PROCESSES CONCURRENTLY.
263
264           This will be called in all processes so that your instance can
265           update any internal state.
266
267           The $job_id variable contains the id for the job to which the
268           resource was assigned. You should use this to record any internal
269           state. The $job_id will be passed to "release()" when the job
270           completes and no longer needs the resource.
271
272           This is intended only for modifying internal state, you should not
273           do anything in this sub that will explode if it is also done in
274           another process at the same time with the same arguments. For
275           example creating a database should not be done here, multiple
276           processes will fight to do the create. The creation, if necessary
277           should be done in "assign()" which will be called in only one
278           process.
279
280       $inst->release($job_id)
281           NOTE: THIS MAY BE CALLED IN MUTLIPLE PROCESSES CONCURRENTLY.
282
283           This will be called for every test job that completes, even if it
284           did not use this resource. If the job_id did not use the resource
285           you may simply return, otherwise update the internal state to
286           reflect that the resource is no longer in use.
287
288           This is intended only for modifying internal state, you should not
289           do anything in this sub that will explode if it is also done in
290           another process at the same time with the same arguments. For
291           example deleting a database should not be done here, multiple
292           processes will fight to do the delete. "assign()" is the only
293           method that will be run in a single process, so if a database needs
294           to be cleaned before it can be used you should clean it there. Any
295           final cleanup should be done in "cleanup()" which will only be
296           called by one process at the very end.
297
298       $inst->cleanup()
299           This will be called once by the parent runner process just before
300           it exits.  This is your chance to do any final cleanup tasks such
301           as deleting databases that are no longer going to be used by tests
302           as no more will be run.
303

SOURCE

305       The source code repository for Test2-Harness can be found at
306       http://github.com/Test-More/Test2-Harness/.
307

MAINTAINERS

309       Chad Granum <exodist@cpan.org>
310

AUTHORS

312       Chad Granum <exodist@cpan.org>
313
315       Copyright 2020 Chad Granum <exodist7@gmail.com>.
316
317       This program is free software; you can redistribute it and/or modify it
318       under the same terms as Perl itself.
319
320       See http://dev.perl.org/licenses/
321
322
323
324perl v5.32.1                      2021-03-12Test2::Harness::Runner::Resource(3)
Impressum