package com.orientechnologies.orient.core.index.hashindex.local.cache;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.zip.CRC32;
import com.orientechnologies.orient.core.storage.impl.local.paginated.wal.ODiskWriteAheadLog;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.orientechnologies.common.serialization.types.OIntegerSerializer;
import com.orientechnologies.common.serialization.types.OLongSerializer;
import com.orientechnologies.orient.core.Orient;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.storage.fs.OFileClassic;
import com.orientechnologies.orient.core.storage.impl.local.paginated.OLocalPaginatedStorage;
import com.orientechnologies.orient.core.storage.impl.local.paginated.wal.OLogSequenceNumber;
import com.orientechnologies.orient.core.storage.impl.local.paginated.wal.OWALRecordsFactory;
import com.orientechnologies.orient.core.storage.impl.local.paginated.wal.WriteAheadLogTest;
/**
* @author Andrey Lomakin
* @since 26.07.13
*/
@Test
public class WOWCacheTest {
private int systemOffset = 2 * (OIntegerSerializer.INT_SIZE + OLongSerializer.LONG_SIZE);
private int pageSize = systemOffset + 8;
private OLocalPaginatedStorage storageLocal;
private String fileName;
private ODiskWriteAheadLog writeAheadLog;
private OWOWCache wowCache;
@BeforeClass
public void beforeClass() throws IOException {
OGlobalConfiguration.FILE_LOCK.setValue(Boolean.FALSE);
String buildDirectory = System.getProperty("buildDirectory");
if (buildDirectory == null)
buildDirectory = ".";
storageLocal = (OLocalPaginatedStorage) Orient.instance().loadStorage("plocal:" + buildDirectory + "/WOWCacheTest");
storageLocal.create(null);
fileName = "wowCacheTest.tst";
OWALRecordsFactory.INSTANCE.registerNewRecord((byte) 128, WriteAheadLogTest.TestRecord.class);
}
@BeforeMethod
public void beforeMethod() throws IOException {
closeCacheAndDeleteFile();
initBuffer();
}
private void closeCacheAndDeleteFile() throws IOException {
if (wowCache != null) {
wowCache.close();
wowCache = null;
}
if (writeAheadLog != null) {
writeAheadLog.delete();
writeAheadLog = null;
}
storageLocal.delete();
File testFile = new File(storageLocal.getConfiguration().getDirectory() + File.separator + fileName);
if (testFile.exists()) {
Assert.assertTrue(testFile.delete());
}
File nameIdMapFile = new File(storageLocal.getConfiguration().getDirectory() + File.separator + "name_id_map.cm");
if (nameIdMapFile.exists()) {
Assert.assertTrue(nameIdMapFile.delete());
}
}
@AfterClass
public void afterClass() throws IOException {
closeCacheAndDeleteFile();
File file = new File(storageLocal.getConfiguration().getDirectory());
Assert.assertTrue(file.delete());
}
private void initBuffer() throws IOException {
wowCache = new OWOWCache(true, pageSize, 10000, writeAheadLog, 10, 100, storageLocal, false);
}
public void testLoadStore() throws IOException {
Random random = new Random();
byte[][] pageData = new byte[200][];
long fileId = wowCache.openFile(fileName);
for (int i = 0; i < pageData.length; i++) {
byte[] data = new byte[8];
random.nextBytes(data);
pageData[i] = data;
final OCachePointer cachePointer = wowCache.load(fileId, i);
cachePointer.acquireExclusiveLock();
cachePointer.getDataPointer().set(systemOffset + OWOWCache.PAGE_PADDING, data, 0, data.length);
cachePointer.releaseExclusiveLock();
wowCache.store(fileId, i, cachePointer);
cachePointer.decrementReferrer();
}
for (int i = 0; i < pageData.length; i++) {
byte[] dataOne = pageData[i];
OCachePointer cachePointer = wowCache.load(fileId, i);
byte[] dataTwo = cachePointer.getDataPointer().get(systemOffset + OWOWCache.PAGE_PADDING, 8);
cachePointer.decrementReferrer();
Assert.assertEquals(dataTwo, dataOne);
}
wowCache.flush();
for (int i = 0; i < pageData.length; i++) {
byte[] dataContent = pageData[i];
assertFile(i, dataContent, new OLogSequenceNumber(0, 0));
}
}
public void testDataUpdate() throws Exception {
final NavigableMap<Long, byte[]> pageIndexDataMap = new TreeMap<Long, byte[]>();
long fileId = wowCache.openFile(fileName);
Random random = new Random();
for (int i = 0; i < 600; i++) {
long pageIndex = random.nextInt(2048);
byte[] data = new byte[8];
random.nextBytes(data);
pageIndexDataMap.put(pageIndex, data);
final OCachePointer cachePointer = wowCache.load(fileId, pageIndex);
cachePointer.acquireExclusiveLock();
cachePointer.getDataPointer().set(systemOffset + OWOWCache.PAGE_PADDING, data, 0, data.length);
cachePointer.releaseExclusiveLock();
wowCache.store(fileId, pageIndex, cachePointer);
cachePointer.decrementReferrer();
}
for (Map.Entry<Long, byte[]> entry : pageIndexDataMap.entrySet()) {
long pageIndex = entry.getKey();
byte[] dataOne = entry.getValue();
OCachePointer cachePointer = wowCache.load(fileId, pageIndex);
byte[] dataTwo = cachePointer.getDataPointer().get(systemOffset + OWOWCache.PAGE_PADDING, 8);
cachePointer.decrementReferrer();
Assert.assertEquals(dataTwo, dataOne);
}
for (int i = 0; i < 300; i++) {
long desiredIndex = random.nextInt(2048);
Long pageIndex = pageIndexDataMap.ceilingKey(desiredIndex);
if (pageIndex == null)
pageIndex = pageIndexDataMap.floorKey(desiredIndex);
byte[] data = new byte[8];
random.nextBytes(data);
pageIndexDataMap.put(pageIndex, data);
final OCachePointer cachePointer = wowCache.load(fileId, pageIndex);
cachePointer.acquireExclusiveLock();
cachePointer.getDataPointer().set(systemOffset + OWOWCache.PAGE_PADDING, data, 0, data.length);
cachePointer.releaseExclusiveLock();
wowCache.store(fileId, pageIndex, cachePointer);
cachePointer.decrementReferrer();
}
for (Map.Entry<Long, byte[]> entry : pageIndexDataMap.entrySet()) {
long pageIndex = entry.getKey();
byte[] dataOne = entry.getValue();
OCachePointer cachePointer = wowCache.load(fileId, pageIndex);
byte[] dataTwo = cachePointer.getDataPointer().get(systemOffset + OWOWCache.PAGE_PADDING, 8);
cachePointer.decrementReferrer();
Assert.assertEquals(dataTwo, dataOne);
}
wowCache.flush();
for (Map.Entry<Long, byte[]> entry : pageIndexDataMap.entrySet()) {
assertFile(entry.getKey(), entry.getValue(), new OLogSequenceNumber(0, 0));
}
}
public void testFlushAllContentEventually() throws Exception {
Random random = new Random();
byte[][] pageData = new byte[200][];
long fileId = wowCache.openFile(fileName);
for (int i = 0; i < pageData.length; i++) {
byte[] data = new byte[8];
random.nextBytes(data);
pageData[i] = data;
final OCachePointer cachePointer = wowCache.load(fileId, i);
cachePointer.acquireExclusiveLock();
cachePointer.getDataPointer().set(systemOffset + OWOWCache.PAGE_PADDING, data, 0, data.length);
cachePointer.releaseExclusiveLock();
wowCache.store(fileId, i, cachePointer);
cachePointer.decrementReferrer();
}
for (int i = 0; i < pageData.length; i++) {
byte[] dataOne = pageData[i];
OCachePointer cachePointer = wowCache.load(fileId, i);
byte[] dataTwo = cachePointer.getDataPointer().get(systemOffset + OWOWCache.PAGE_PADDING, 8);
cachePointer.decrementReferrer();
Assert.assertEquals(dataTwo, dataOne);
}
Thread.sleep(5000);
for (int i = 0; i < pageData.length; i++) {
byte[] dataContent = pageData[i];
assertFile(i, dataContent, new OLogSequenceNumber(0, 0));
}
}
private void assertFile(long pageIndex, byte[] value, OLogSequenceNumber lsn) throws IOException {
String path = storageLocal.getConfiguration().getDirectory() + File.separator + fileName;
OFileClassic fileClassic = new OFileClassic();
fileClassic.init(path, "r");
fileClassic.open();
byte[] content = new byte[8 + systemOffset];
fileClassic.read(pageIndex * (8 + systemOffset), content, 8 + systemOffset);
Assert.assertEquals(Arrays.copyOfRange(content, systemOffset, 8 + systemOffset), value);
long magicNumber = OLongSerializer.INSTANCE.deserializeNative(content, 0);
Assert.assertEquals(magicNumber, OWOWCache.MAGIC_NUMBER);
CRC32 crc32 = new CRC32();
crc32.update(content, OIntegerSerializer.INT_SIZE + OLongSerializer.LONG_SIZE, content.length - OIntegerSerializer.INT_SIZE
- OLongSerializer.LONG_SIZE);
int crc = OIntegerSerializer.INSTANCE.deserializeNative(content, OLongSerializer.LONG_SIZE);
Assert.assertEquals(crc, (int) crc32.getValue());
int segment = OIntegerSerializer.INSTANCE.deserializeNative(content, OLongSerializer.LONG_SIZE + OIntegerSerializer.INT_SIZE);
long position = OLongSerializer.INSTANCE
.deserializeNative(content, OLongSerializer.LONG_SIZE + 2 * OIntegerSerializer.INT_SIZE);
OLogSequenceNumber readLsn = new OLogSequenceNumber(segment, position);
Assert.assertEquals(readLsn, lsn);
fileClassic.close();
}
}