blob: 197aecc130ec17af5538cc7947ec6003c4ade7ae [file] [log] [blame]
package edu.uci.ics.hyracks.storage.am.common.dataflow;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import edu.uci.ics.hyracks.api.exceptions.HyracksDataException;
import edu.uci.ics.hyracks.storage.am.common.api.IIndex;
import edu.uci.ics.hyracks.storage.am.common.api.IIndexLifecycleManager;
public class IndexLifecycleManager implements IIndexLifecycleManager {
private static final long DEFAULT_MEMORY_BUDGET = 1024 * 1024 * 100; // 100 megabytes
private final Map<Long, IndexInfo> indexInfos;
private final long memoryBudget;
private long memoryUsed;
public IndexLifecycleManager() {
this(DEFAULT_MEMORY_BUDGET);
}
public IndexLifecycleManager(long memoryBudget) {
this.indexInfos = new HashMap<Long, IndexInfo>();
this.memoryBudget = memoryBudget;
this.memoryUsed = 0;
}
private boolean evictCandidateIndex() throws HyracksDataException {
// Why min()? As a heuristic for eviction, we will take an open index (an index consuming memory)
// that is not being used (refcount == 0) and has been least recently used. The sort order defined
// for IndexInfo maintains this. See IndexInfo.compareTo().
IndexInfo info = Collections.min(indexInfos.values());
if (info.referenceCount != 0 || !info.isOpen) {
return false;
}
info.index.deactivate();
memoryUsed -= info.index.getMemoryAllocationSize();
info.isOpen = false;
return true;
}
@Override
public IIndex getIndex(long resourceID) {
IndexInfo info = indexInfos.get(resourceID);
return info == null ? null : info.index;
}
@Override
public void register(long resourceID, IIndex index) throws HyracksDataException {
if (indexInfos.containsKey(resourceID)) {
throw new HyracksDataException("Index with resource ID " + resourceID + " already exists.");
}
indexInfos.put(resourceID, new IndexInfo(index));
}
@Override
public void unregister(long resourceID) throws HyracksDataException {
IndexInfo info = indexInfos.remove(resourceID);
if (info == null) {
throw new HyracksDataException("Index with resource ID " + resourceID + " does not exist.");
}
if (info.referenceCount != 0) {
indexInfos.put(resourceID, info);
throw new HyracksDataException("Cannot remove index while it is open.");
}
if (info.isOpen) {
info.index.deactivate();
memoryUsed -= info.index.getMemoryAllocationSize();
}
}
@Override
public void open(long resourceID) throws HyracksDataException {
IndexInfo info = indexInfos.get(resourceID);
if (info == null) {
throw new HyracksDataException("Failed to open index with resource ID " + resourceID
+ " since it does not exist.");
}
long inMemorySize = info.index.getMemoryAllocationSize();
while (memoryUsed + inMemorySize > memoryBudget) {
if (!evictCandidateIndex()) {
throw new HyracksDataException("Cannot activate index since memory budget would be exceeded.");
}
}
if (!info.isOpen) {
info.index.activate();
info.isOpen = true;
memoryUsed += inMemorySize;
}
info.touch();
}
@Override
public void close(long resourceID) {
indexInfos.get(resourceID).untouch();
}
private class IndexInfo implements Comparable<IndexInfo> {
private final IIndex index;
private int referenceCount;
private long lastAccess;
private boolean isOpen;
public IndexInfo(IIndex index) {
this.index = index;
this.lastAccess = -1;
this.referenceCount = 0;
this.isOpen = false;
}
public void touch() {
lastAccess = System.currentTimeMillis();
referenceCount++;
}
public void untouch() {
lastAccess = System.currentTimeMillis();
referenceCount--;
}
@Override
public int compareTo(IndexInfo i) {
// sort by (isOpen, referenceCount, lastAccess) ascending, where true < false
//
// Example sort order:
// -------------------
// (F, 0, 70) <-- largest
// (F, 0, 60)
// (T, 10, 80)
// (T, 10, 70)
// (T, 9, 90)
// (T, 0, 100) <-- smallest
if (isOpen && !i.isOpen) {
return -1;
} else if (!isOpen && i.isOpen) {
return 1;
} else {
if (referenceCount < i.referenceCount) {
return -1;
} else if (referenceCount > i.referenceCount) {
return 1;
} else {
if (lastAccess < i.lastAccess) {
return -1;
} else if (lastAccess > i.lastAccess) {
return 1;
} else {
return 0;
}
}
}
}
public String toString() {
return "{index: " + index + ", isOpen: " + isOpen + ", refCount: " + referenceCount + ", lastAccess: "
+ lastAccess + "}";
}
}
@Override
public List<IIndex> getOpenIndexes() {
List<IIndex> openIndexes = new ArrayList<IIndex>();
for (IndexInfo i : indexInfos.values()) {
if (i.isOpen) {
openIndexes.add(i.index);
}
}
return openIndexes;
}
}