]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/support-scripts/hold_targeter.pl
LP1809288: Make some brt fields read-only
[Evergreen.git] / Open-ILS / src / support-scripts / hold_targeter.pl
1 #!/usr/bin/perl
2 use strict; 
3 use warnings;
4 use Getopt::Long;
5 use OpenSRF::System;
6 use OpenSRF::AppSession;
7 use OpenSRF::Utils::SettingsClient;
8 use OpenILS::Utils::Fieldmapper;
9 $ENV{OSRF_LOG_CLIENT} = 1;
10 #----------------------------------------------------------------
11 # Batch hold (re)targeter
12 #
13 # Usage:
14 #   ./hold_targeter.pl /openils/conf/opensrf_core.xml
15 #----------------------------------------------------------------
16
17 my $help;
18 my $osrf_config = '/openils/conf/opensrf_core.xml';
19 my $lockfile = '/tmp/hold_targeter-LOCK';
20 my $parallel = 0;
21 my $verbose = 0;
22 my $retarget_interval;
23 my $soft_retarget_interval;
24 my $next_check_interval;
25 my $recv_timeout = 3600;
26 my $parallel_init_sleep = 0;
27
28 # how often the server sends a summary reply per backend.
29 my $return_throttle = 500;
30
31 GetOptions(
32     'help'                  => \$help,
33     'osrf-config=s'         => \$osrf_config,
34     'lockfile=s'            => \$lockfile,
35     'parallel=i'            => \$parallel,
36     'verbose'               => \$verbose,
37     'parallel-init-sleep=i' => \$parallel_init_sleep,
38     'retarget-interval=s'   => \$retarget_interval,
39     'next-check-interval=s'    => \$next_check_interval,
40     'soft-retarget-interval=s' => \$soft_retarget_interval,
41 ) || die "\nSee --help for more\n";
42
43 sub help {
44     print <<HELP;
45
46 Batch hold targeter.
47
48 $0 \
49     --osrf-config /openils/conf/opensrf_core.xml \
50     --lockfile /tmp/hold_targeter-LOCK \
51     --parallel 3
52     --verbose
53
54 General Options
55
56     --osrf-config [/openils/conf/opensrf_core.xml] 
57         OpenSRF config file.
58
59     --lockfile [/tmp/hold_targeter-LOCK]
60         Full path to lock file
61
62     --verbose
63         Print process counts
64
65 Targeting Options
66
67     --parallel <parallel-process-count>
68         Number of parallel hold processors to run.  This overrides any
69         value found in opensrf.xml
70
71     --parallel-init-sleep <seconds=0>
72         Number of seconds to wait before starting each subsequent
73         parallel targeter instance.  This gives each targeter backend
74         time to run the large targetable holds query before the next
75         kicks off, so they don't all hit the database at once.
76
77         Defaults to no sleep.
78
79     --soft-retarget-interval
80         Holds whose previous check time sits between the
81         --soft-retarget-interval and the --retarget-interval are 
82         treated like this:
83         
84         1. The list of potential copies is updated for all matching holds.
85         2. Holds that have a viable target are otherwise left untouched,
86            including their prev_check_time.
87         3. Holds with no viable target are fully retargeted.
88
89     --next-check-interval
90         Specify how long after the current run time the targeter will
91         retarget the currently affected holds.  Applying a specific
92         interval is useful when the retarget_interval is shorter than
93         the time between targeter runs.
94
95         This value is used to determine if an org unit will be closed
96         during the next iteration of the targeter.  It overrides the
97         default behavior of calculating the next retarget time from the
98         retarget-interval.
99
100     --retarget-interval
101         Retarget holds whose previous check time occured before the
102         requested interval.
103         Overrides the 'circ.holds.retarget_interval' global_flag value. 
104
105 HELP
106
107     exit(0);
108 }
109
110 help() if $help;
111
112 sub init {
113
114     OpenSRF::System->bootstrap_client(config_file => $osrf_config);
115     Fieldmapper->import(
116         IDL => OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
117
118     if (!$parallel) {
119         my $settings = OpenSRF::Utils::SettingsClient->new;
120         $parallel = $settings->config_value(hold_targeter => 'parallel') || 1;
121     }
122 }
123
124 sub run_batches {
125
126     # Hanging all of the parallel requests off the same app session
127     # lets us operate the same as a MultiSession batch with additional
128     # fine-grained controls over the receive timeout and real-time
129     # response handling.
130     my $ses = OpenSRF::AppSession->create('open-ils.hold-targeter');
131
132     my @reqs;
133     for my $slot (1..$parallel) {
134
135         if ($slot > 1 && $parallel_init_sleep) {
136             $verbose && print "Sleeping $parallel_init_sleep ".
137                 "seconds before targeter slot=$slot launch\n";
138             sleep $parallel_init_sleep;
139         }
140
141         $verbose && print "Starting targeter slot=$slot\n";
142
143         my $req = $ses->request(
144             'open-ils.hold-targeter.target', {
145                 return_count    => 1,
146                 return_throttle => $return_throttle,
147                 parallel_count  => $parallel,
148                 parallel_slot   => $slot,
149                 retarget_interval      => $retarget_interval,
150                 next_check_interval    => $next_check_interval,
151                 soft_retarget_interval => $soft_retarget_interval
152             }
153         );
154
155         $req->{_parallel_slot} = $slot; # for grouping/logging below
156         push(@reqs, $req);
157     }
158
159     while (@reqs) {
160         my $start = time;
161         $ses->queue_wait($recv_timeout); # wait for a response
162
163         # As a fail-safe, exit if no responses have arrived 
164         # within the timeout interval.
165         last if (time - $start) >= $recv_timeout;
166
167         for my $req (@reqs) {
168             # Pull all responses off the receive queues.
169             while (my $resp = $req->recv(0)) {
170                 die $req->failed . "\n" if $req->failed;
171
172                 $verbose && print sprintf(
173                     "Targeter [%d] processed %d holds\n",
174                     $req->{_parallel_slot},
175                     $resp->content
176                 );
177             }
178         }
179
180         @reqs = grep {!$_->complete} @reqs;
181     }
182 }
183
184 # ----
185
186 die "I seem to be running already. If not remove $lockfile, try again\n" 
187     if -e $lockfile;
188
189 open(LOCK, ">$lockfile") or die "Cannot open lock file: $lockfile : $@\n";
190 print LOCK $$ or die "Cannot write to lock file: $lockfile : $@\n";
191 close LOCK;
192    
193 eval { # Make sure we can delete the lock file.
194
195     init();
196
197     my $start = time;
198
199     run_batches();
200
201     my $minutes = sprintf('%0.2f', (time - $start) / 60.0);
202
203     $verbose && print "Processing took $minutes minutes.\n";
204 };
205
206 warn "Hold processing exited with error: $@\n" if $@;
207
208 unlink $lockfile;
209