36
36
import org .opensearch .common .collect .Tuple ;
37
37
import org .opensearch .common .util .concurrent .ReleasableLock ;
38
38
39
+ import java .util .ArrayList ;
39
40
import java .util .Arrays ;
40
41
import java .util .HashMap ;
41
42
import java .util .Iterator ;
43
+ import java .util .List ;
42
44
import java .util .Map ;
43
45
import java .util .Objects ;
44
46
import java .util .concurrent .CompletableFuture ;
@@ -396,7 +398,12 @@ private V get(K key, long now, Consumer<Entry<K, V>> onExpiration) {
396
398
if (entry == null ) {
397
399
return null ;
398
400
} else {
399
- promote (entry , now );
401
+ List <RemovalNotification <K , V >> removalNotifications = promote (entry , now ).v2 ();
402
+ if (!removalNotifications .isEmpty ()) {
403
+ for (RemovalNotification <K , V > removalNotification : removalNotifications ) {
404
+ removalListener .onRemoval (removalNotification );
405
+ }
406
+ }
400
407
return entry .value ;
401
408
}
402
409
}
@@ -446,8 +453,14 @@ private V compute(K key, CacheLoader<K, V> loader) throws ExecutionException {
446
453
447
454
BiFunction <? super Entry <K , V >, Throwable , ? extends V > handler = (ok , ex ) -> {
448
455
if (ok != null ) {
456
+ List <RemovalNotification <K , V >> removalNotifications = new ArrayList <>();
449
457
try (ReleasableLock ignored = lruLock .acquire ()) {
450
- promote (ok , now );
458
+ removalNotifications = promote (ok , now ).v2 ();
459
+ }
460
+ if (!removalNotifications .isEmpty ()) {
461
+ for (RemovalNotification <K , V > removalNotification : removalNotifications ) {
462
+ removalListener .onRemoval (removalNotification );
463
+ }
451
464
}
452
465
return ok .value ;
453
466
} else {
@@ -512,16 +525,22 @@ private void put(K key, V value, long now) {
512
525
CacheSegment <K , V > segment = getCacheSegment (key );
513
526
Tuple <Entry <K , V >, Entry <K , V >> tuple = segment .put (key , value , now );
514
527
boolean replaced = false ;
528
+ List <RemovalNotification <K , V >> removalNotifications = new ArrayList <>();
515
529
try (ReleasableLock ignored = lruLock .acquire ()) {
516
530
if (tuple .v2 () != null && tuple .v2 ().state == State .EXISTING ) {
517
531
if (unlink (tuple .v2 ())) {
518
532
replaced = true ;
519
533
}
520
534
}
521
- promote (tuple .v1 (), now );
535
+ removalNotifications = promote (tuple .v1 (), now ). v2 ( );
522
536
}
523
537
if (replaced ) {
524
- removalListener .onRemoval (new RemovalNotification <>(tuple .v2 ().key , tuple .v2 ().value , RemovalReason .REPLACED ));
538
+ removalNotifications .add (new RemovalNotification <>(tuple .v2 ().key , tuple .v2 ().value , RemovalReason .REPLACED ));
539
+ }
540
+ if (!removalNotifications .isEmpty ()) {
541
+ for (RemovalNotification <K , V > removalNotification : removalNotifications ) {
542
+ removalListener .onRemoval (removalNotification );
543
+ }
525
544
}
526
545
}
527
546
@@ -767,8 +786,17 @@ public long getEvictions() {
767
786
}
768
787
}
769
788
770
- private boolean promote (Entry <K , V > entry , long now ) {
789
+ /**
790
+ * Promotes the desired entry to the head of the lru list and tries to see if it needs to evict any entries in
791
+ * case the cache size is exceeding or the entry got expired.
792
+ * @param entry Entry to be promoted
793
+ * @param now the current time
794
+ * @return Returns a tuple. v1 signifies whether an entry got promoted, v2 signifies the list of removal
795
+ * notifications that the callers needs to handle.
796
+ */
797
+ private Tuple <Boolean , List <RemovalNotification <K , V >>> promote (Entry <K , V > entry , long now ) {
771
798
boolean promoted = true ;
799
+ List <RemovalNotification <K , V >> removalNotifications = new ArrayList <>();
772
800
try (ReleasableLock ignored = lruLock .acquire ()) {
773
801
switch (entry .state ) {
774
802
case DELETED :
@@ -782,10 +810,21 @@ private boolean promote(Entry<K, V> entry, long now) {
782
810
break ;
783
811
}
784
812
if (promoted ) {
785
- evict (now );
813
+ while (tail != null && shouldPrune (tail , now )) {
814
+ Entry <K , V > entryToBeRemoved = tail ;
815
+ CacheSegment <K , V > segment = getCacheSegment (entryToBeRemoved .key );
816
+ if (segment != null ) {
817
+ segment .remove (entryToBeRemoved .key , entryToBeRemoved .value , f -> {});
818
+ }
819
+ if (unlink (entryToBeRemoved )) {
820
+ removalNotifications .add (
821
+ new RemovalNotification <>(entryToBeRemoved .key , entryToBeRemoved .value , RemovalReason .EVICTED )
822
+ );
823
+ }
824
+ }
786
825
}
787
826
}
788
- return promoted ;
827
+ return new Tuple <>( promoted , removalNotifications ) ;
789
828
}
790
829
791
830
private void evict (long now ) {
0 commit comments