blob: cce8dfc3eb182453359fc1a64a9581d67a31043e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.internal.cache.eviction;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.logging.log4j.Logger;
import org.apache.geode.internal.cache.BucketRegion;
import org.apache.geode.internal.cache.versions.RegionVersionVector;
import org.apache.geode.internal.logging.log4j.LogMarker;
import org.apache.geode.logging.internal.log4j.api.LogService;
abstract class AbstractEvictionList implements EvictionList {
private static final Logger logger = LogService.getLogger();
/** The last node in the LRU list after which all new nodes are added */
protected final EvictionNode tail = new GuardNode();
/** The starting point in the LRU list for searching for the LRU node */
protected final EvictionNode head = new GuardNode();
/** Counter for the size of the LRU list */
private final AtomicInteger size = new AtomicInteger();
private final EvictionController controller;
protected AbstractEvictionList(EvictionController controller) {
this.controller = controller;
initEmptyList();
}
@Override
public int size() {
return size.get();
}
private void incrementSize() {
size.incrementAndGet();
}
void decrementSize() {
// Size could go negative if there is a concurrent clear and
// cache updates are in progress.
size.decrementAndGet();
}
@Override
public void closeStats() {
getStatistics().close();
}
@Override
public EvictionCounters getStatistics() {
return this.controller.getCounters();
}
@Override
public void clear(RegionVersionVector regionVersionVector, BucketRegion bucketRegion) {
if (regionVersionVector != null) {
return; // when concurrency checks are enabled the clear operation removes entries iteratively
}
synchronized (this) {
if (bucketRegion != null) {
getStatistics().decrementCounter(bucketRegion.getCounter());
bucketRegion.resetCounter();
} else {
getStatistics().resetCounter();
}
initEmptyList();
}
}
private synchronized void initEmptyList() {
size.set(0);
head.setNext(tail);
tail.setPrevious(head);
}
/**
* Adds an lru node to the tail of the list.
*/
@Override
public synchronized void appendEntry(final EvictionNode evictionNode) {
if (evictionNode.next() != null) {
// already in the list
return;
}
if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) {
logger.trace(LogMarker.LRU_CLOCK_VERBOSE, "adding a Node to lru list: {}", evictionNode);
}
evictionNode.setNext(tail);
tail.previous().setNext(evictionNode);
evictionNode.setPrevious(tail.previous());
tail.setPrevious(evictionNode);
incrementSize();
}
@Override
public synchronized void destroyEntry(EvictionNode evictionNode) {
if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) {
logger.trace(LogMarker.LRU_CLOCK_VERBOSE, "destroyEntry called for {}", evictionNode);
}
if (removeEntry(evictionNode)) {
getStatistics().incDestroys();
}
}
protected synchronized boolean removeEntry(EvictionNode evictionNode) {
if (evictionNode.next() == null) {
// not in the list anymore.
return false;
}
unlinkEntry(evictionNode);
return true;
}
protected synchronized void unlinkEntry(EvictionNode evictionNode) {
EvictionNode next = evictionNode.next();
EvictionNode previous = evictionNode.previous();
next.setPrevious(previous);
previous.setNext(next);
evictionNode.setNext(null);
evictionNode.setPrevious(null);
decrementSize();
}
protected synchronized EvictionNode unlinkTailEntry() {
EvictionNode evictionNode = tail.previous();
if (evictionNode == head) {
return null; // end of eviction list
}
unlinkEntry(evictionNode);
return evictionNode;
}
/**
* Remove and return the head entry in the list
*/
protected synchronized EvictionNode unlinkHeadEntry() {
EvictionNode evictionNode = head.next();
if (evictionNode == tail) {
return null; // end of list
}
unlinkEntry(evictionNode);
return evictionNode;
}
protected boolean isEvictable(EvictionNode evictionNode) {
if (evictionNode.isEvicted()) {
if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) {
logger.trace(LogMarker.LRU_CLOCK_VERBOSE,
"discarding evicted entry");
}
return false;
}
// If this Entry is part of a transaction, skip it since
// eviction should not cause commit conflicts
synchronized (evictionNode) {
if (evictionNode.isInUseByTransaction()) {
if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) {
logger.trace(LogMarker.LRU_CLOCK_VERBOSE,
"removing transactional entry from consideration");
}
return false;
}
}
return true;
}
}