]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/support-scripts/pingest.pl
LP 1768715: Add pingest.pl to Evergreen.
[Evergreen.git] / Open-ILS / src / support-scripts / pingest.pl
1 #!/usr/bin/perl
2 # ---------------------------------------------------------------
3 # Copyright © 2013,2014 Merrimack Valley Library Consortium
4 # Jason Stephenson <jstephenson@mvlc.org>
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 # ---------------------------------------------------------------
16 # TODO: Document with POD.
17 # This guy parallelizes a reingest.
18 use strict;
19 use warnings;
20 use DBI;
21
22 # You will want to adjust the next two based on your database size,
23 # i.e. number of bib records as well as the number of cores on your
24 # database server.  Using roughly number of cores/2 doesn't seem to
25 # have much impact in off peak times.
26 use constant {
27     BATCHSIZE => 10000,
28     MAXCHILD => 8
29 };
30
31 # Globals for the command line options:
32 my $do_browse = 1; # Do the browse reingest.
33 my $do_attrs = 1; # Do the record attributes reingest.
34 my $do_search = 1; # Do the search reingest.
35 my $do_facets = 1; # Do the facets reingest.
36
37 # Command line options to skip different reingests. In this case, we
38 # use the '-' to indicate a minus or a no, so to
39 # skip browse reingest: -browse or -b
40 # skip attribute reingest: -attributes or -a
41 # skip search reingest: -search or -s
42 # skip facet reingest: -facets or -f
43 foreach (@ARGV) {
44     if (/^-b(?:rowse)?$/) {
45         $do_browse = 0;
46     } elsif (/^-a(?:ttr(?:ibute)?s?)?$/) {
47         $do_attrs = 0;
48     } elsif (/^-s(?:earch)?$/) {
49         $do_search = 0;
50     } elsif (/^-f(?:acets?)?$/) {
51         $do_facets = 0;
52     } else {
53         # TODO: Add usage() function to report allowed options.
54         die ("Unrecognized option: $_");
55     }
56 }
57
58 # "Gimme the keys!  I'll drive!"
59 my $q = <<END_OF_Q;
60 SELECT id
61 FROM biblio.record_entry
62 WHERE deleted = 'f'
63 AND id > 0
64 ORDER BY id ASC
65 END_OF_Q
66
67 # Stuffs needed for looping, tracking how many lists of records we
68 # have, storing the actual list of records, and the list of the lists
69 # of records.
70 my ($count, $lists, $records) = (0,0,[]);
71 my @lol = ();
72 # To do the browse-only ingest:
73 my @blist = ();
74
75 # All of the DBI->connect() calls in this file assume that you have
76 # configured the PGHOST, PGPORT, PGDATABASE, PGUSER, and PGPASSWORD
77 # variables in your execution environment.  If you have not, you have
78 # two options:
79 #
80 # 1) configure them
81 #
82 # 2) edit the DBI->connect() calls in this program so that it can
83 # connect to your database.
84 my $dbh = DBI->connect('DBI:Pg:');
85
86 my $results = $dbh->selectall_arrayref($q);
87 foreach my $r (@$results) {
88     my $record = $r->[0];
89     push(@blist, $record); # separate list of browse-only ingest
90     push(@$records, $record);
91     if (++$count == BATCHSIZE) {
92         $lol[$lists++] = $records;
93         $count = 0;
94         $records = [];
95     }
96 }
97 $lol[$lists++] = $records if ($count); # Last batch is likely to be
98                                        # small.
99 $dbh->disconnect();
100
101 # We're going to reuse $count to keep track of the total number of
102 # batches processed.
103 $count = 0;
104
105 # @running keeps track of the running child processes.
106 my @running = ();
107
108 # We start the browse-only ingest before starting the other ingests.
109 browse_ingest(@blist) if ($do_browse);
110
111 # We loop until we have processed all of the batches stored in @lol:
112 while ($count < $lists) {
113     if (scalar(@lol) && scalar(@running) < MAXCHILD) {
114         # Reuse $records for the lulz.
115         $records = shift(@lol);
116         if ($do_search || $do_facets || $do_attrs) {
117             reingest($records);
118         } else {
119             $count++;
120         }
121     } else {
122         my $pid = wait();
123         if (grep {$_ == $pid} @running) {
124             @running = grep {$_ != $pid} @running;
125             $count++;
126             print "$count of $lists processed\n";
127         }
128     }
129 }
130
131 # This subroutine forks a process to do the browse-only ingest on the
132 # @blist above.  It cannot be parallelized, but can run in parrallel
133 # to the other ingests.
134 sub browse_ingest {
135     my @list = @_;
136     my $pid = fork();
137     if (!defined($pid)) {
138         die "failed to spawn child";
139     } elsif ($pid > 0) {
140         # Add our browser to the list of running children.
141         push(@running, $pid);
142         # Increment the number of lists, because this list was not
143         # previously counted.
144         $lists++;
145     } elsif ($pid == 0) {
146         my $dbh = DBI->connect('DBI:Pg:');
147         my $sth = $dbh->prepare("SELECT metabib.reingest_metabib_field_entries(?, TRUE, FALSE, TRUE)");
148         foreach (@list) {
149             if ($sth->execute($_)) {
150                 my $crap = $sth->fetchall_arrayref();
151             } else {
152                 warn ("Browse ingest failed for record $_");
153             }
154         }
155         $dbh->disconnect();
156         exit(0);
157     }
158 }
159
160 # Fork a child to do the other reingests:
161
162 sub reingest {
163     my $list = shift;
164     my $pid = fork();
165     if (!defined($pid)) {
166         die "Failed to spawn a child";
167     } elsif ($pid > 0) {
168         push(@running, $pid);
169     } elsif ($pid == 0) {
170         my $dbh = DBI->connect('DBI:Pg:');
171         reingest_attributes($dbh, $list) if ($do_attrs);
172         reingest_field_entries($dbh, $list) if ($do_facets || $do_search);
173         $dbh->disconnect();
174         exit(0);
175     }
176 }
177
178 # Reingest metabib field entries on a list of records.
179 sub reingest_field_entries {
180     my $dbh = shift;
181     my $list = shift;
182     my $sth = $dbh->prepare("SELECT metabib.reingest_metabib_field_entries(?, ?, TRUE, ?)");
183     # Because reingest uses "skip" options we invert the logic of do variables.
184     $sth->bind_param(2, ($do_facets) ? 0 : 1);
185     $sth->bind_param(3, ($do_search) ? 0 : 1);
186     foreach (@$list) {
187         $sth->bind_param(1, $_);
188         if ($sth->execute()) {
189             my $crap = $sth->fetchall_arrayref();
190         } else {
191             warn ("metabib.reingest_metabib_field_entries failed for record $_");
192         }
193     }
194 }
195
196 # Reingest record attributes on a list of records.
197 sub reingest_attributes {
198     my $dbh = shift;
199     my $list = shift;
200     my $sth = $dbh->prepare(<<END_OF_INGEST
201 SELECT metabib.reingest_record_attributes(id, NULL::TEXT[], marc)
202 FROM biblio.record_entry
203 WHERE id = ?
204 END_OF_INGEST
205     );
206     foreach (@$list) {
207         $sth->bind_param(1, $_);
208         if ($sth->execute()) {
209             my $crap = $sth->fetchall_arrayref();
210         } else {
211             warn ("metabib.reingest_record_attributes failed for record $_");
212         }
213     }
214 }