/*
* Copyright 2015 Terracotta, Inc., a Software AG company.
*
* 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.
*/
package org.terracotta.offheapstore.disk.storage;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.terracotta.offheapstore.concurrent.ConcurrentOffHeapHashMap;
import org.terracotta.offheapstore.disk.AbstractDiskTest;
import org.terracotta.offheapstore.disk.paging.MappedPageSource;
import org.terracotta.offheapstore.disk.storage.portability.PersistentByteArrayPortability;
import org.terracotta.offheapstore.disk.storage.portability.PersistentSerializablePortability;
import org.terracotta.offheapstore.util.DebuggingUtils;
import org.terracotta.offheapstore.util.MemoryUnit;
/**
*
* @author Chris Dennis
*/
public class FileBackedStorageEngineTest extends AbstractDiskTest {
private static final NumberFormat FLOAT_FORMAT = NumberFormat.getInstance();
@Test
public void testEmptyPayload() throws IOException {
MappedPageSource source = new MappedPageSource(dataFile);
FileBackedStorageEngine<byte[], byte[]> engine = new FileBackedStorageEngine<byte[], byte[]>(source, Long.MAX_VALUE, MemoryUnit.BYTES, PersistentByteArrayPortability.INSTANCE, PersistentByteArrayPortability.INSTANCE);
try {
long p = engine.writeMapping(new byte[0], new byte[0], 0, 0);
Assert.assertTrue(p >= 0);
engine.flush();
byte[] k = engine.readKey(p, 0);
Assert.assertNotNull(k);
Assert.assertEquals(0, k.length);
byte[] v = engine.readValue(p);
Assert.assertNotNull(v);
Assert.assertEquals(0, v.length);
} finally {
engine.close();
source.close();
}
}
@Test
public void testSmallPayloads() throws IOException {
MappedPageSource source = new MappedPageSource(dataFile);
FileBackedStorageEngine<byte[], byte[]> engine = new FileBackedStorageEngine<byte[], byte[]>(source, Long.MAX_VALUE, MemoryUnit.BYTES, PersistentByteArrayPortability.INSTANCE, PersistentByteArrayPortability.INSTANCE);
try {
Random rndm = new Random();
for (int i = 0; i < 64; i++) {
byte[] b = new byte[i];
rndm.nextBytes(b);
long p = engine.writeMapping(b, b, 0, 0);
Assert.assertTrue(p >= 0);
engine.flush();
byte[] k = engine.readKey(p, 0);
Assert.assertNotNull(k);
Assert.assertArrayEquals(b, k);
byte[] v = engine.readValue(p);
Assert.assertNotNull(v);
Assert.assertArrayEquals(b, v);
engine.freeMapping(p, 0, true);
}
} finally {
engine.close();
source.close();
}
}
@Test
public void testInterruptingReadThreads() throws IOException {
MappedPageSource source = new MappedPageSource(dataFile);
FileBackedStorageEngine<byte[], byte[]> engine = new FileBackedStorageEngine<byte[], byte[]>(source, Long.MAX_VALUE, MemoryUnit.BYTES, PersistentByteArrayPortability.INSTANCE, PersistentByteArrayPortability.INSTANCE);
try {
long p = engine.writeMapping(new byte[0], new byte[32], 0, 0);
Assert.assertTrue(p >= 0);
engine.flush();
Thread.currentThread().interrupt();
byte[] v = engine.readValue(p);
Assert.assertTrue(Thread.interrupted());
Assert.assertNotNull(v);
Assert.assertEquals(32, v.length);
} finally {
engine.close();
source.close();
}
}
@Test
@Ignore("performance test")
public void testHugeMap() throws IOException, InterruptedException, ExecutionException {
System.err.println("Using file: " + dataFile.getAbsolutePath());
MappedPageSource source = new MappedPageSource(dataFile);
final ConcurrentOffHeapHashMap<Integer, byte[]> map = new ConcurrentOffHeapHashMap<Integer, byte[]>(source, FileBackedStorageEngine.createFactory(source, Long.MAX_VALUE, MemoryUnit.BYTES, new PersistentSerializablePortability(), PersistentByteArrayPortability.INSTANCE), 4 * 1024 * 1024, 1);
try {
ExecutorService executor = Executors.newFixedThreadPool(1);
int i = 0;
long size = 0;
while (true) {
Collection<Callable<Void>> tasks = new ArrayList<Callable<Void>>(1024);
for (int c = 0; c < 1024; c++, i++) {
final int key = i;
tasks.add(new Callable<Void>() {
@Override
public Void call() {
map.put(key, new byte[1024]);
return null;
}
});
}
long start = System.nanoTime();
for (Future<Void> f : executor.invokeAll(tasks)) {
f.get();
}
long end = System.nanoTime();
long sizeNow = map.getOccupiedMemory();
long delta = sizeNow - size;
size = sizeNow;
System.out.println("Put " + DebuggingUtils.toBase2SuffixedString(i) + " mappings @ " + FLOAT_FORMAT.format((1000 * 1000 * 1000 * ((double) delta)) / (1024 * 1024 * ((double) (end - start)))) + "MiB/sec [Map Size: " + DebuggingUtils.toBase2SuffixedString(size) + "B Allocated Size: " + DebuggingUtils.toBase2SuffixedString(map.getAllocatedMemory()) + "B]");
}
} finally {
source.close();
}
}
}