]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher.pm
fixing cache paging
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Storage / Publisher.pm
1 package OpenILS::Application::Storage::Publisher;
2 use base qw/OpenILS::Application::Storage/;
3 our $VERSION = 1;
4
5 use Digest::MD5 qw/md5_hex/;
6 use OpenSRF::EX qw/:try/;;
7 use OpenSRF::Utils::Logger qw/:level/;
8 use OpenILS::Utils::Fieldmapper;
9
10 my $log = 'OpenSRF::Utils::Logger';
11
12
13 sub register_method {
14         my $class = shift;
15         my %args = @_;
16         my %dup_args = %args;
17
18         $class = ref($class) || $class;
19
20         $args{package} ||= $class;
21         __PACKAGE__->SUPER::register_method( %args );
22
23         if (exists($dup_args{cachable}) and $dup_args{cachable}) {
24                 (my $name = $dup_args{api_name}) =~ s/^open-ils\.storage/open-ils.storage.cachable/o;
25                 if ($name ne $dup_args{api_name}) {
26                         $dup_args{real_api_name} = $dup_args{api_name};
27                         $dup_args{method} = 'cachable_wrapper';
28                         $dup_args{api_name} = $name;
29                         $dup_args{package} = __PACKAGE__;
30                         __PACKAGE__->SUPER::register_method( %dup_args );
31                 }
32         }
33 }
34
35 sub cachable_wrapper {
36         my $self = shift;
37         my $client = shift;
38         my @args = @_;
39
40         my %cache_args = (
41                 limit           => 100,
42                 offset          => 0,
43                 timeout         => 7200,
44                 cache_page_size => 1000,
45         );
46
47         my @real_args;
48         my $key_string = $self->api_name;
49         for (my $ind = 0; $ind < scalar(@args); $ind++) {
50                 if (    $args[$ind] eq 'limit' ||
51                         $args[$ind] eq 'offset' ||
52                         $args[$ind] eq 'cache_page_size' ||
53                         $args[$ind] eq 'timeout' ) {
54
55                         my $key_ind = $ind;
56                         $ind++;
57                         my $value_ind = $ind;
58                         $cache_args{$args[$key_ind]} = $args[$value_ind];
59                         $log->debug("Cache limiter value for $args[$key_ind] is $args[$value_ind]", INTERNAL);
60                         next;
61                 }
62                 $key_string .= $args[$ind];
63                 $log->debug("Partial cache key value is $args[$ind]", INTERNAL);
64                 push @real_args, $args[$ind];
65         }
66
67         my $cache_page = int($cache_args{offset} / $cache_args{cache_page_size});
68         my $cache_key = md5_hex($key_string.$cache_page);
69
70         $log->debug("Key string for cache lookup is $key_string -> $cache_key", DEBUG);
71         $log->debug("Cache page is $cache_page", DEBUG);
72
73         my $cached_res = OpenSRF::Utils::Cache->new->get_cache( $cache_key );
74         if (defined $cached_res) {
75                 $log->debug("Found ".scalar(@$cached_res)." records in the cache", INFO);
76                 $log->debug("Values from cache: ".join(', ', @$cached_res), INTERNAL);
77                 my $start = int($cache_args{offset} - ($cache_page * $cache_args{cache_page_size}));
78                 my $end = int($start + $cache_args{limit} - 1);
79                 $log->debug("Responding with values from ".$start.' to '.$end,DEBUG);
80                 $client->respond( $_ ) for ( grep { defined } @$cached_res[ $start .. $end ]);
81                 return undef;
82         }
83
84         my $method = $self->method_lookup($self->{real_api_name});
85         my @res = $method->run(@real_args);
86
87
88         $client->respond( $_ ) for ( grep { defined } @res[$cache_args{offset} .. int($cache_args{offset} + $cache_args{limit} - 1)] );
89
90         $log->debug("Saving values from ".int($cache_page * $cache_args{cache_page_size})." to ".
91                 int(($cache_page + 1) * $cache_args{cache_page_size}). "to the cache", INTERNAL);
92         OpenSRF::Utils::Cache->new->put_cache(
93                 $cache_key =>
94                 [@res[int($cache_page * $cache_args{cache_page_size}) .. int(($cache_page + 1) * $cache_args{cache_page_size}) ]] =>
95                 $cache_args{timeout}
96         );
97
98         return undef;
99 }
100
101 sub retrieve_node {
102         my $self = shift;
103         my $client = shift;
104         my @ids = @_;
105
106         my $cdbi = $self->{cdbi};
107
108         for my $id ( @ids ) {
109                 next unless ($id);
110
111                 my ($rec) = $cdbi->fast_fieldmapper($id);
112                 if ($self->api_name !~ /batch/o) {
113                         return $rec if ($rec);
114                 }
115                 $client->respond($rec);
116         }
117         return undef;
118 }
119
120 sub search {
121         my $self = shift;
122         my $client = shift;
123         my $searches = shift;
124
125         my $cdbi = $self->{cdbi};
126
127         $log->debug("Searching $cdbi for { ".join(',', map { "$_ => $$searches{$_}" } keys %$searches).' }',DEBUG);
128
129         for my $obj ($cdbi->search($searches)) {
130                 $client->respond( $obj->to_fieldmapper );
131         }
132         return undef;
133 }
134
135 sub search_one_field {
136         my $self = shift;
137         my $client = shift;
138         my @terms = @_;
139
140         (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
141         (my $col = $self->api_name) =~ s/.*\.$search_type\.([^.]+).*/$1/;
142         my $cdbi = $self->{cdbi};
143
144         my $like = 0;
145         $like = 1 if ($search_type =~ /like$/o);
146
147         for my $term (@terms) {
148                 $log->debug("Searching $cdbi for $col using type $search_type, value '$term'",DEBUG);
149                 if (@terms == 1) {
150                         return [ $cdbi->fast_fieldmapper($term,$col,$like) ];
151                 }
152                 $client->respond( [ $cdbi->fast_fieldmapper($term,$col,$like) ] );
153         }
154         return undef;
155 }
156
157
158 sub create_node {
159         my $self = shift;
160         my $client = shift;
161         my $node = shift;
162
163         my $cdbi = $self->{cdbi};
164
165         my $success;
166         try {
167                 my $rec = $cdbi->create($node);
168                 $success = $rec->id if ($rec);
169         } catch Error with {
170                 $success = 0;
171         };
172
173         return $success;
174 }
175
176 sub update_node {
177         my $self = shift;
178         my $client = shift;
179         my $node = shift;
180
181         my $cdbi = $self->{cdbi};
182
183         return $cdbi->update($node);
184 }
185
186 sub mass_delete {
187         my $self = shift;
188         my $client = shift;
189         my $search = shift;
190
191         my $where = 'WHERE ';
192
193         my $cdbi = $self->{cdbi};
194         my $table = $cdbi->table;
195
196         my @keys = sort keys %$search;
197         
198         my @binds;
199         my @wheres;
200         for my $col ( @keys ) {
201                 if (ref($$search{$col}) and ref($$search{$col}) =~ /ARRAY/o) {
202                         push @wheres, "$col IN (" . join(',', map { '?' } @{ $$search{$col} }) . ')';
203                         push @binds, map { "$_" } @{ $$search{$col} };
204                 } else {
205                         push @wheres, "$col = ?";
206                         push @binds, $$search{$col};
207                 }
208         }
209         $where .= join ' AND ', @wheres;
210
211         my $delete = "DELETE FROM $table $where";
212
213         $log->debug("Performing MASS deletion : $delete",DEBUG);
214
215         my $dbh = $cdbi->db_Main;
216         my $success = 1;
217         try {
218                 my $sth = $dbh->prepare($delete);
219                 $sth->execute( @binds );
220                 $sth->finish;
221                 $log->debug("MASS Delete succeeded",DEBUG);
222         } catch Error with {
223                 $log->debug("MASS Delete FAILED : ".shift(),DEBUG);
224                 $success = 0;
225         };
226         return $success;
227 }
228
229 sub delete_node {
230         my $self = shift;
231         my $client = shift;
232         my $node = shift;
233
234         my $cdbi = $self->{cdbi};
235
236         my $success = 1;
237         try {
238                 $success = $cdbi->delete($node);
239         } catch Error with {
240                 $success = 0;
241         };
242         return $success;
243 }
244
245 sub batch_call {
246         my $self = shift;
247         my $client = shift;
248         my @nodes = @_;
249
250         my $cdbi = $self->{cdbi};
251         my $api_name = $self->api_name;
252         (my $single_call_api_name = $api_name) =~ s/batch\.//o;
253
254         $log->debug("Default $api_name looking up $single_call_api_name...",INTERNAL);
255         my $method = $self->method_lookup($single_call_api_name);
256
257         my @success;
258         while ( my $node = shift(@nodes) ) {
259                 my ($res) = $method->run( $node ); 
260                 push(@success, 1) if ($res >= 0);
261         }
262
263         my $insert_total = 0;
264         $insert_total += $_ for (@success);
265
266         return $insert_total;
267 }
268
269 eval '
270 use OpenILS::Application::Storage::Publisher::actor;
271 use OpenILS::Application::Storage::Publisher::action;
272 use OpenILS::Application::Storage::Publisher::asset;
273 use OpenILS::Application::Storage::Publisher::biblio;
274 use OpenILS::Application::Storage::Publisher::config;
275 use OpenILS::Application::Storage::Publisher::metabib;
276 ';
277
278 for my $fmclass ( (Fieldmapper->classes) ) {
279         $log->debug("Generating methods for Fieldmapper class $fmclass", DEBUG);
280
281         (my $cdbi = $fmclass) =~ s/^Fieldmapper:://o;
282         (my $class = $cdbi) =~ s/::.*//o;
283         (my $api_class = $cdbi) =~ s/::/./go;
284         my $registration_class = __PACKAGE__ . "::$class";
285         my $api_prefix = 'open-ils.storage.direct.'.$api_class;
286
287         # Create the search method
288         unless ( __PACKAGE__->is_registered( $api_prefix.'.search' ) ) {
289                 __PACKAGE__->register_method(
290                         api_name        => $api_prefix.'.search',
291                         method          => 'search',
292                         api_level       => 1,
293                         stream          => 1,
294                         cdbi            => $cdbi,
295                         cachable        => 1,
296                 );
297         }
298
299         # Create the retrieve method
300         unless ( __PACKAGE__->is_registered( $api_prefix.'.retrieve' ) ) {
301                 __PACKAGE__->register_method(
302                         api_name        => $api_prefix.'.retrieve',
303                         method          => 'retrieve_node',
304                         api_level       => 1,
305                         cdbi            => $cdbi,
306                         cachable        => 1,
307                 );
308         }
309
310         # Create the batch retrieve method
311         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.retrieve' ) ) {
312                 __PACKAGE__->register_method(
313                         api_name        => $api_prefix.'.batch.retrieve',
314                         method          => 'retrieve_node',
315                         api_level       => 1,
316                         stream          => 1,
317                         cdbi            => $cdbi,
318                         cachable        => 1,
319                 );
320         }
321
322         unless ($fmclass->is_virtual) {
323                 for my $field ($fmclass->real_fields) {
324                         unless ( __PACKAGE__->is_registered( $api_prefix.'.search.'.$field ) ) {
325                                 __PACKAGE__->register_method(
326                                         api_name        => $api_prefix.'.search.'.$field,
327                                         method          => 'search_one_field',
328                                         api_level       => 1,
329                                         cdbi            => $cdbi,
330                                         cachable        => 1,
331                                 );
332                         }
333                         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like.'.$field ) ) {
334                                 __PACKAGE__->register_method(
335                                         api_name        => $api_prefix.'.search_like.'.$field,
336                                         method          => 'search_one_field',
337                                         api_level       => 1,
338                                         cdbi            => $cdbi,
339                                         cachable        => 1,
340                                 );
341                         }
342                 }
343         }
344
345
346         # Create the create method
347         unless ( __PACKAGE__->is_registered( $api_prefix.'.create' ) ) {
348                 __PACKAGE__->register_method(
349                         api_name        => $api_prefix.'.create',
350                         method          => 'create_node',
351                         api_level       => 1,
352                         cdbi            => $cdbi,
353                 );
354         }
355
356         # Create the batch create method
357         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.create' ) ) {
358                 __PACKAGE__->register_method(
359                         api_name        => $api_prefix.'.batch.create',
360                         method          => 'batch_call',
361                         api_level       => 1,
362                         cdbi            => $cdbi,
363                 );
364         }
365
366         # Create the update method
367         unless ( __PACKAGE__->is_registered( $api_prefix.'.update' ) ) {
368                 __PACKAGE__->register_method(
369                         api_name        => $api_prefix.'.update',
370                         method          => 'update_node',
371                         api_level       => 1,
372                         cdbi            => $cdbi,
373                 );
374         }
375
376         # Create the batch update method
377         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.update' ) ) {
378                 __PACKAGE__->register_method(
379                         api_name        => $api_prefix.'.batch.update',
380                         method          => 'batch_call',
381                         api_level       => 1,
382                         cdbi            => $cdbi,
383                 );
384         }
385
386         # Create the delete method
387         unless ( __PACKAGE__->is_registered( $api_prefix.'.delete' ) ) {
388                 __PACKAGE__->register_method(
389                         api_name        => $api_prefix.'.delete',
390                         method          => 'delete_node',
391                         api_level       => 1,
392                         cdbi            => $cdbi,
393                 );
394         }
395
396         # Create the batch delete method
397         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.delete' ) ) {
398                 __PACKAGE__->register_method(
399                         api_name        => $api_prefix.'.batch.delete',
400                         method          => 'batch_call',
401                         api_level       => 1,
402                         cdbi            => $cdbi,
403                 );
404         }
405
406         # Create the search-based mass delete method
407         unless ( __PACKAGE__->is_registered( $api_prefix.'.mass_delete' ) ) {
408                 __PACKAGE__->register_method(
409                         api_name        => $api_prefix.'.mass_delete',
410                         method          => 'mass_delete',
411                         api_level       => 1,
412                         cdbi            => $cdbi,
413                 );
414         }
415
416 }
417
418 1;