LRUListWithAsyncSorting extends AbstractEvictionList { @Override public EvictableEntry getEvictableEntry() { int evictionAttempts = 0; for (;;) { final EvictionNode evictionNode = unlinkHeadEntry(); if (evictionNode == null) { return null; } if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) { logger.trace(LogMarker.LRU_CLOCK_VERBOSE, "lru considering {}", evictionNode); } if (!isEvictable(evictionNode)) { continue; } if (evictionNode.isRecentlyUsed() && evictionAttempts < maxEvictionAttempts) { evictionAttempts++; evictionNode.unsetRecentlyUsed(); appendEntry(evictionNode); continue; } if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) { logger.trace(LogMarker.LRU_CLOCK_VERBOSE, "returning unused entry: {}", evictionNode); } if (evictionNode.isRecentlyUsed()) { scanIfNeeded(); getStatistics().incGreedyReturns(1); } return (EvictableEntry) evictionNode; } } LRUListWithAsyncSorting(EvictionController controller);   LRUListWithAsyncSorting(EvictionController controller, ExecutorService executor,
      int maxEvictionAttempts); @Override void clear(RegionVersionVector regionVersionVector, BucketRegion bucketRegion); @Override EvictableEntry getEvictableEntry(); @Override void incrementRecentlyUsed();  }
@Test public void evictingFromEmptyListTest() throws Exception { LRUListWithAsyncSorting list = new LRUListWithAsyncSorting(controller, executor, 1); assertThat(list.getEvictableEntry()).isNull(); assertThat(list.size()).isZero(); }
@Test public void evictingFromNonEmptyListTest() throws Exception { LRUListWithAsyncSorting list = new LRUListWithAsyncSorting(controller, executor, 1); EvictionNode node = mock(EvictableEntry.class); list.appendEntry(node); assertThat(list.size()).isEqualTo(1); when(node.next()).thenReturn(list.tail); when(node.previous()).thenReturn(list.head); assertThat(list.getEvictableEntry()).isSameAs(node); assertThat(list.size()).isZero(); }
@Test public void doesNotEvictNodeInTransaction() throws Exception { LRUListWithAsyncSorting list = new LRUListWithAsyncSorting(controller, executor, 1); EvictionNode nodeInTransaction = mock(EvictableEntry.class, "nodeInTransaction"); when(nodeInTransaction.isInUseByTransaction()).thenReturn(true); EvictionNode nodeNotInTransaction = mock(EvictableEntry.class, "nodeNotInTransaction"); list.appendEntry(nodeInTransaction); list.appendEntry(nodeNotInTransaction); assertThat(list.size()).isEqualTo(2); when(nodeInTransaction.next()).thenReturn(nodeNotInTransaction); when(nodeInTransaction.previous()).thenReturn(list.head); when(nodeNotInTransaction.next()).thenReturn(list.tail); when(nodeNotInTransaction.previous()).thenReturn(list.head); assertThat(list.getEvictableEntry()).isSameAs(nodeNotInTransaction); assertThat(list.size()).isZero(); }
@Test public void doesNotEvictNodeThatIsEvicted() throws Exception { LRUListWithAsyncSorting list = new LRUListWithAsyncSorting(controller, executor, 1); EvictionNode evictedNode = mock(EvictableEntry.class); when(evictedNode.isEvicted()).thenReturn(true); EvictionNode node = mock(EvictableEntry.class); list.appendEntry(evictedNode); list.appendEntry(node); assertThat(list.size()).isEqualTo(2); when(evictedNode.next()).thenReturn(node); when(evictedNode.previous()).thenReturn(list.head); when(node.next()).thenReturn(list.tail); when(node.previous()).thenReturn(list.head); assertThat(list.getEvictableEntry()).isSameAs(node); assertThat(list.size()).isZero(); }
@Test public void startScanIfEvictableEntryIsRecentlyUsed() throws Exception { List<EvictionNode> nodes = new ArrayList<>(); LRUListWithAsyncSorting lruEvictionList = new LRUListWithAsyncSorting(controller, executor, 1); IntStream.range(0, 11).forEach(i -> { EvictionNode node = new LRUTestEntry(i); nodes.add(node); lruEvictionList.appendEntry(node); node.setRecentlyUsed(mock(RegionEntryContext.class)); }); assertThat(lruEvictionList.getEvictableEntry().isRecentlyUsed()).isTrue(); verify(executor).submit(any(Runnable.class)); }