/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.util;

import java.util.ArrayList;

An alternative cache implementation. This implementation uses two caches: a LRU cache and a FIFO cache. Entries are first kept in the FIFO cache, and if referenced again then marked in a hash set. If referenced again, they are moved to the LRU cache. Stream pages are never added to the LRU cache. It is supposed to be more or less scan resistant, and it doesn't cache large rows in the LRU cache.
/** * An alternative cache implementation. This implementation uses two caches: a * LRU cache and a FIFO cache. Entries are first kept in the FIFO cache, and if * referenced again then marked in a hash set. If referenced again, they are * moved to the LRU cache. Stream pages are never added to the LRU cache. It is * supposed to be more or less scan resistant, and it doesn't cache large rows * in the LRU cache. */
public class CacheTQ implements Cache { static final String TYPE_NAME = "TQ"; private final Cache lru; private final Cache fifo; private final SmallLRUCache<Integer, Object> recentlyUsed = SmallLRUCache.newInstance(1024); private int lastUsed = -1; private int maxMemory; CacheTQ(CacheWriter writer, int maxMemoryKb) { this.maxMemory = maxMemoryKb; lru = new CacheLRU(writer, (int) (maxMemoryKb * 0.8), false); fifo = new CacheLRU(writer, (int) (maxMemoryKb * 0.2), true); setMaxMemory(4 * maxMemoryKb); } @Override public void clear() { lru.clear(); fifo.clear(); recentlyUsed.clear(); lastUsed = -1; } @Override public CacheObject find(int pos) { CacheObject r = lru.find(pos); if (r == null) { r = fifo.find(pos); } return r; } @Override public CacheObject get(int pos) { CacheObject r = lru.find(pos); if (r != null) { return r; } r = fifo.find(pos); if (r != null && !r.isStream()) { if (recentlyUsed.get(pos) != null) { if (lastUsed != pos) { fifo.remove(pos); lru.put(r); } } else { recentlyUsed.put(pos, this); } lastUsed = pos; } return r; } @Override public ArrayList<CacheObject> getAllChanged() { ArrayList<CacheObject> lruChanged = lru.getAllChanged(); ArrayList<CacheObject> fifoChanged = fifo.getAllChanged(); ArrayList<CacheObject> changed = new ArrayList<>(lruChanged.size() + fifoChanged.size()); changed.addAll(lruChanged); changed.addAll(fifoChanged); return changed; } @Override public int getMaxMemory() { return maxMemory; } @Override public int getMemory() { return lru.getMemory() + fifo.getMemory(); } @Override public void put(CacheObject r) { if (r.isStream()) { fifo.put(r); } else if (recentlyUsed.get(r.getPos()) != null) { lru.put(r); } else { fifo.put(r); lastUsed = r.getPos(); } } @Override public boolean remove(int pos) { boolean result = lru.remove(pos); if (!result) { result = fifo.remove(pos); } recentlyUsed.remove(pos); return result; } @Override public void setMaxMemory(int maxMemoryKb) { this.maxMemory = maxMemoryKb; lru.setMaxMemory((int) (maxMemoryKb * 0.8)); fifo.setMaxMemory((int) (maxMemoryKb * 0.2)); recentlyUsed.setMaxSize(4 * maxMemoryKb); } @Override public CacheObject update(int pos, CacheObject record) { if (lru.find(pos) != null) { return lru.update(pos, record); } return fifo.update(pos, record); } }