Skip to content

Commit 105750a

Browse files
kidclamptomascohen
authored andcommitted
Bug 34178: Cache ItemsAnyAvailableAndNotRestricted in memory and don't precalculate
There are several places in the code where we precalculate ItemsAnyAvailableAndNotRestricted to avoid looping on this routine when calling IsAvailableForItemLevelRequest on a list of items form a biblio The value of ItemsAnyAvailableAndNotRestricted is only used when there is a circulation rule for 'onshelfholds' with a value of '2' (If all unavailable) Rather than calculate a value that may never be used, let's cache this value per request when we do calculate it - and reuse the cached value To test: 1 - Apply patch 2 - Set circulation rule 'On shelf holds allowed' as 'If all unavailable' make sure the rule applies to all of the items/patrons you test with 3 - Find a record with two items that are available 4 - Try to place a hold for a patron - not allowed 5 - Check out one item to another patron 6 - Attempt hold - still not allowed 7 - Check out second item to another patron 8 - Attempt hold - allowed! 9 - Apply patch 10 - Cancel and replace hold - it is allowed! 11 - Check in one item, and cancel hold 12 - Place hold - not allowed! 13 - Check in second item 14 - Place hold - not allowed! 15 - prove -v t/db_dependent/Holds/DisallowHoldIfItemsAvailable.t Signed-off-by: Sam Lau <samalau@gmail.com> Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
1 parent 4ce7f8c commit 105750a

File tree

4 files changed

+17
-27
lines changed

4 files changed

+17
-27
lines changed

C4/Circulation.pm

+2-6
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use Encode;
2626

2727
use C4::Context;
2828
use C4::Stats qw( UpdateStats );
29-
use C4::Reserves qw( CheckReserves CanItemBeReserved MoveReserve ModReserve ModReserveMinusPriority RevertWaitingStatus IsItemOnHoldAndFound IsAvailableForItemLevelRequest ItemsAnyAvailableAndNotRestricted );
29+
use C4::Reserves qw( CheckReserves CanItemBeReserved MoveReserve ModReserve ModReserveMinusPriority RevertWaitingStatus IsItemOnHoldAndFound IsAvailableForItemLevelRequest );
3030
use C4::Biblio qw( UpdateTotalIssues );
3131
use C4::Items qw( ModItemTransfer ModDateLastSeen CartToShelf );
3232
use C4::Accounts;
@@ -3056,17 +3056,13 @@ sub CanBookBeRenewed {
30563056
foreach my $possible_hold (@possible_holds) {
30573057
my $fillable = 0;
30583058
my $patron_with_reserve = Koha::Patrons->find($possible_hold->borrowernumber);
3059-
my $items_any_available = ItemsAnyAvailableAndNotRestricted({
3060-
biblionumber => $item->biblionumber,
3061-
patron => $patron_with_reserve
3062-
});
30633059

30643060
# FIXME: We are not checking whether the item we are renewing can fill the hold
30653061

30663062
foreach my $other_item (@other_items) {
30673063
next if defined $matched_items{$other_item->itemnumber};
30683064
next if IsItemOnHoldAndFound( $other_item->itemnumber );
3069-
next unless IsAvailableForItemLevelRequest($other_item, $patron_with_reserve, undef, $items_any_available);
3065+
next unless IsAvailableForItemLevelRequest($other_item, $patron_with_reserve, undef);
30703066
next unless CanItemBeReserved($patron_with_reserve,$other_item,undef,{ignore_hold_counts=>1})->{status} eq 'OK';
30713067
# NOTE: At checkin we call 'CheckReserves' which checks hold 'policy'
30723068
# CanItemBeReserved checks 'rules' and 'policies' which means

C4/Reserves.pm

+11-8
Original file line numberDiff line numberDiff line change
@@ -1336,9 +1336,6 @@ sub IsAvailableForItemLevelRequest {
13361336
my $item = shift;
13371337
my $patron = shift;
13381338
my $pickup_branchcode = shift;
1339-
# items_any_available is precalculated status passed from request.pl when set of items
1340-
# looped outside of IsAvailableForItemLevelRequest to avoid nested loops:
1341-
my $items_any_available = shift;
13421339

13431340
my $dbh = C4::Context->dbh;
13441341
# must check the notforloan setting of the itemtype
@@ -1376,13 +1373,19 @@ sub IsAvailableForItemLevelRequest {
13761373
return 1;
13771374
} elsif ( $on_shelf_holds == 2 ) {
13781375

1379-
# if we have this param predefined from outer caller sub, we just need
1380-
# to return it, so we saving from having loop inside other loop:
1381-
return $items_any_available ? 0 : 1
1382-
if defined $items_any_available;
1376+
# These calculations work at the biblio level, and can be expensive
1377+
# we use the in-memory cache to avoid calling once per item when looping items on a biblio
13831378

1384-
my $any_available = ItemsAnyAvailableAndNotRestricted( { biblionumber => $item->biblionumber, patron => $patron });
1379+
my $memory_cache = Koha::Cache::Memory::Lite->get_instance();
1380+
my $cache_key = sprintf "ItemsAnyAvailableAndNotRestricted:%s:%s", $patron->id, $item->biblionumber;
1381+
1382+
my $any_available = $memory_cache->get_from_cache( $cache_key );
1383+
return $any_available ? 0 : 1 if defined( $any_available );
1384+
1385+
$any_available = ItemsAnyAvailableAndNotRestricted( { biblionumber => $item->biblionumber, patron => $patron });
1386+
$memory_cache->set_in_cache( $cache_key, $any_available );
13851387
return $any_available ? 0 : 1;
1388+
13861389
} else { # on_shelf_holds == 0 "If any unavailable" (the description is rather cryptic and could still be improved)
13871390
return $item->onloan || IsItemOnHoldAndFound( $item->itemnumber );
13881391
}

opac/opac-reserve.pl

+2-6
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
use C4::Auth qw( get_template_and_user );
2626
use C4::Koha qw( getitemtypeimagelocation getitemtypeimagesrc );
2727
use C4::Circulation qw( GetBranchItemRule );
28-
use C4::Reserves qw( CanItemBeReserved CanBookBeReserved AddReserve GetReservesControlBranch ItemsAnyAvailableAndNotRestricted IsAvailableForItemLevelRequest GetReserveFee );
28+
use C4::Reserves qw( CanItemBeReserved CanBookBeReserved AddReserve GetReservesControlBranch IsAvailableForItemLevelRequest GetReserveFee );
2929
use C4::Biblio qw( GetBiblioData GetFrameworkCode );
3030
use C4::Output qw( output_html_with_http_headers );
3131
use C4::Context;
@@ -441,8 +441,6 @@
441441
# to pass this value further inside down to IsAvailableForItemLevelRequest to
442442
# it's complicated logic to analyse.
443443
# (before this loop was inside that sub loop so it was O(n^2) )
444-
my $items_any_available;
445-
$items_any_available = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblioNum, patron => $patron }) if $patron;
446444
foreach my $item (@{$biblioData->{items}}) {
447445

448446
my $item_info = $item->unblessed;
@@ -481,11 +479,9 @@
481479

482480
my $branch = GetReservesControlBranch( $item_info, $patron_unblessed );
483481

484-
# items_any_available defined outside of the current loop,
485-
# so we avoiding loop inside IsAvailableForItemLevelRequest:
486482
my $policy_holdallowed =
487483
CanItemBeReserved( $patron, $item, undef, { get_from_cache => 1 } )->{status} eq 'OK' &&
488-
IsAvailableForItemLevelRequest($item, $patron, undef, $items_any_available);
484+
IsAvailableForItemLevelRequest($item, $patron, undef);
489485

490486
if ($policy_holdallowed) {
491487
my $opac_hold_policy = Koha::CirculationRules->get_opacitemholds_policy( { item => $item, patron => $patron } );

reserve/request.pl

+2-7
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ =head1 request.pl
3333
use Date::Calc qw( Date_to_Days );
3434
use C4::Output qw( output_html_with_http_headers );
3535
use C4::Auth qw( get_template_and_user );
36-
use C4::Reserves qw( RevertWaitingStatus AlterPriority ToggleLowestPriority ToggleSuspend CanBookBeReserved GetMaxPatronHoldsForRecord ItemsAnyAvailableAndNotRestricted CanItemBeReserved IsAvailableForItemLevelRequest );
36+
use C4::Reserves qw( RevertWaitingStatus AlterPriority ToggleLowestPriority ToggleSuspend CanBookBeReserved GetMaxPatronHoldsForRecord CanItemBeReserved IsAvailableForItemLevelRequest );
3737
use C4::Items qw( get_hostitemnumbers_of );
3838
use C4::Koha qw( getitemtypeimagelocation );
3939
use C4::Serials qw( CountSubscriptionFromBiblionumber );
@@ -393,9 +393,6 @@ =head1 request.pl
393393
# to pass this value further inside down to IsAvailableForItemLevelRequest to
394394
# it's complicated logic to analyse.
395395
# (before this loop was inside that sub loop so it was O(n^2) )
396-
my $items_any_available;
397-
$items_any_available = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblio->biblionumber, patron => $patron })
398-
if $patron;
399396

400397
for my $item_object ( @items ) {
401398
my $do_check;
@@ -512,9 +509,7 @@ =head1 request.pl
512509
!$item->{cantreserve}
513510
&& !$exceeded_maxreserves
514511
&& $can_item_be_reserved eq 'OK'
515-
# items_any_available defined outside of the current loop,
516-
# so we avoiding loop inside IsAvailableForItemLevelRequest:
517-
&& IsAvailableForItemLevelRequest($item_object, $patron, undef, $items_any_available)
512+
&& IsAvailableForItemLevelRequest($item_object, $patron, undef)
518513
) || C4::Context->preference('AllowHoldPolicyOverride')
519514
# If AllowHoldPolicyOverride is set, it overrides EVERY restriction
520515
# not just branch item rules

0 commit comments

Comments
 (0)