/** * Return the Entry that is considered most recently used and available to be evicted to overflow. * Note that this implementation basically just returns the most recent thing added to the list. * So, unlike the parent class, is does no scanning based on the recentlyUsed bit. This is a * perfect implementation for our queues (gateway, client subscription) as long as they never * update something already in the queue. Since updates simply set the recentlyUsed bit then the * most recent node may be the one that was just updated and not moved to the tail of the list. */ @Override public EvictableEntry getEvictableEntry() { long evaluations = 0; EvictionNode evictionNode; // search for entry to return from list for (;;) { evictionNode = unlinkTailEntry(); // end of Lifo list stop searching if (evictionNode == null) { break; } evaluations++; synchronized (evictionNode) { // if entry NOT used by transaction and NOT evicted return entry if (!evictionNode.isInUseByTransaction() && !evictionNode.isEvicted()) { break; } } } getStatistics().incEvaluations(evaluations); return (EvictableEntry) evictionNode; }
@Test public void evictingFromNonEmptyListTest() throws Exception { LIFOList list = new LIFOList(controller); 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); verify(stats).incEvaluations(anyLong()); assertThat(list.size()).isZero(); }
getStatistics().incEvaluations(numEvals); return null; getStatistics().incEvaluations(numEvals); return (EvictableEntry) aNode;
@Test public void doesNotEvictNodeInTransaction() throws Exception { LIFOList list = new LIFOList(controller); EvictionNode nodeInTransaction = mock(EvictableEntry.class); when(nodeInTransaction.isInUseByTransaction()).thenReturn(true); EvictionNode nodeNotInTransaction = mock(EvictableEntry.class); list.appendEntry(nodeNotInTransaction); list.appendEntry(nodeInTransaction); assertThat(list.size()).isEqualTo(2); when(nodeInTransaction.next()).thenReturn(list.tail); when(nodeInTransaction.previous()).thenReturn(nodeNotInTransaction); when(nodeNotInTransaction.next()).thenReturn(list.tail); when(nodeNotInTransaction.previous()).thenReturn(list.head); assertThat(list.getEvictableEntry()).isSameAs(nodeNotInTransaction); verify(stats).incEvaluations(2); assertThat(list.size()).isZero(); }
@Test public void doesNotEvictNodeThatIsEvicted() throws Exception { LIFOList list = new LIFOList(controller); EvictionNode evictedNode = mock(EvictableEntry.class); when(evictedNode.isEvicted()).thenReturn(true); EvictionNode node = mock(EvictableEntry.class); list.appendEntry(node); list.appendEntry(evictedNode); assertThat(list.size()).isEqualTo(2); when(evictedNode.next()).thenReturn(list.tail); when(evictedNode.previous()).thenReturn(node); when(node.next()).thenReturn(list.tail); when(node.previous()).thenReturn(list.head); assertThat(list.getEvictableEntry()).isSameAs(node); verify(stats).incEvaluations(2); assertThat(list.size()).isZero(); } }