/*
*
* * Copyright 2010-2016 OrientDB LTD (http://orientdb.com)
* *
* * Licensed 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.
* *
* * For more information: http://orientdb.com
*
*/
package com.orientechnologies.orient.core.storage.cache.pages;
import com.orientechnologies.orient.core.storage.cache.OCacheEntry;
import com.orientechnologies.orient.core.storage.cache.OReadCache;
import com.orientechnologies.orient.core.storage.cache.OWriteCache;
import java.io.IOException;
/**
* The implementation of a page cache suitable for page caches of a very tiny size, like 1-8 pages. Built on arrays, on a page load
* request always evicts the page with the lowest observed hits.
*
* @author Sergey Sitnikov
*/
public class OTinyPageCache implements OPageCache {
private final OReadCache readCache;
private final OCacheEntry[] pages;
private final long[] counters;
/**
* Constructs a new tiny page cache instance.
*
* @param readCache the underlying read cache.
* @param maximumSize the maximum cache size in pages.
*/
public OTinyPageCache(OReadCache readCache, int maximumSize) {
this.readCache = readCache;
this.pages = new OCacheEntry[maximumSize];
this.counters = new long[maximumSize * 2];
}
@Override
public OCacheEntry loadPage(long fileId, long pageIndex, boolean checkPinnedPages, OWriteCache writeCache, int pageCount)
throws IOException {
long victimHits = Long.MAX_VALUE;
int victimIndex = -1;
for (int i = 0; i < pages.length; ++i) {
final OCacheEntry entry = pages[i];
if (entry == null) { // free slot found
victimIndex = i;
victimHits = 0; // mark as free
continue;
}
final int countersBase = i * 2;
assert counters[countersBase + 1] >= 1;
if (pageIndex == entry.getPageIndex() && fileId == entry.getFileId()) { // found in cache
++counters[countersBase];
++counters[countersBase + 1];
return entry;
}
if (victimHits != 0 /* still no free slot found */ && counters[countersBase] <= 0 /* no usages */) {
final long hits = counters[countersBase + 1];
if (hits < victimHits) {
victimIndex = i;
victimHits = hits;
}
}
}
final OCacheEntry entry = readCache.load(fileId, pageIndex, checkPinnedPages, writeCache, pageCount);
if (entry == null)
return null;
if (victimIndex != -1) { // slot found
final int countersBase = victimIndex * 2;
if (victimHits != 0) { // slot is not free
final OCacheEntry victim = pages[victimIndex];
for (long i = counters[countersBase]; i <= 0; ++i)
readCache.release(victim, writeCache);
}
pages[victimIndex] = entry;
counters[countersBase] = 1;
counters[countersBase + 1] = 1;
}
return entry;
}
@Override
public void releasePage(OCacheEntry cacheEntry, OWriteCache writeCache) {
for (int i = 0; i < pages.length; ++i)
if (pages[i] == cacheEntry) { // found in cache
--counters[i * 2];
return;
}
readCache.release(cacheEntry, writeCache); // not cached
}
@Override
public void releaseFilePages(long fileId, OWriteCache writeCache) {
for (int i = 0; i < pages.length; ++i) {
final OCacheEntry entry = pages[i];
if (entry != null && fileId == entry.getFileId()) {
assert counters[i * 2] <= 0;
pages[i] = null;
for (long j = counters[i * 2]; j <= 0; ++j)
readCache.release(entry, writeCache);
}
}
}
@Override
public OCacheEntry purgePage(long fileId, long pageIndex, OWriteCache writeCache) {
for (int i = 0; i < pages.length; ++i) {
final OCacheEntry entry = pages[i];
if (entry != null && pageIndex == entry.getPageIndex() && fileId == entry.getFileId()) {
assert counters[i * 2] <= 0;
pages[i] = null;
for (long j = counters[i * 2]; j < 0; ++j)
readCache.release(entry, writeCache);
return entry;
}
}
return null;
}
@Override
public void reset(OWriteCache writeCache) {
for (int i = 0; i < pages.length; ++i) {
final OCacheEntry entry = pages[i];
if (entry != null) {
assert counters[i * 2] <= 0;
pages[i] = null;
for (long j = counters[i * 2]; j <= 0; ++j)
readCache.release(entry, writeCache);
}
}
}
}