/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.common.buffer.impl;
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.ref.WeakReference;
import org.junit.After;
import org.junit.Test;
import org.mockito.Mockito;
import org.teiid.common.buffer.CacheEntry;
import org.teiid.common.buffer.FileStore;
import org.teiid.common.buffer.Serializer;
import org.teiid.common.buffer.StorageManager;
import org.teiid.core.TeiidComponentException;
public class TestBufferFrontedFileStoreCache {
private BufferFrontedFileStoreCache cache;
private final static class SimpleSerializer implements Serializer<Integer> {
@Override
public Integer deserialize(ObjectInput ois)
throws IOException, ClassNotFoundException {
Integer result = ois.readInt();
for (int i = 0; i < result; i++) {
assertEquals(i, ois.readInt());
}
return result;
}
@Override
public Long getId() {
return 1l;
}
@Override
public void serialize(Integer obj, ObjectOutput oos)
throws IOException {
oos.writeInt(obj);
for (int i = 0; i < obj; i++) {
oos.writeInt(i);
}
}
@Override
public boolean useSoftCache() {
return false;
}
@Override
public String describe(Integer obj) {
return null;
}
}
@After public void teardown() {
if (this.cache != null) {
cache.shutdown();
}
}
@Test public void testAddGetMultiBlock() throws Exception {
cache = createLayeredCache(1 << 26, 1 << 26, true, true);
CacheEntry ce = new CacheEntry(2l);
Serializer<Integer> s = new SimpleSerializer();
cache.createCacheGroup(s.getId());
Integer cacheObject = Integer.valueOf(2);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
ce = get(cache, 2l, s);
assertEquals(cacheObject, ce.getObject());
//test something that exceeds the direct inode data blocks
ce = new CacheEntry(3l);
cacheObject = Integer.valueOf(80000);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
ce = get(cache, 3l, s);
assertEquals(cacheObject, ce.getObject());
//repeat the test to ensure proper cleanup
ce = new CacheEntry(4l);
cacheObject = Integer.valueOf(60000);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
ce = get(cache, 4l, s);
assertEquals(cacheObject, ce.getObject());
cache.removeCacheGroup(1l);
assertEquals(0, cache.getDataBlocksInUse());
assertEquals(0, cache.getInodesInUse());
//test something that exceeds the indirect data blocks
ce = new CacheEntry(3l);
cache.createCacheGroup(s.getId());
cacheObject = Integer.valueOf(5000000);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
ce = get(cache, 3l, s);
assertEquals(cacheObject, ce.getObject());
cache.removeCacheGroup(1l);
assertEquals(0, cache.getDataBlocksInUse());
assertEquals(0, cache.getInodesInUse());
//test something that exceeds the allowable object size
ce = new CacheEntry(3l);
cache.createCacheGroup(s.getId());
cacheObject = Integer.valueOf(500000000);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
ce = get(cache, 3l, s);
assertNull(ce);
cache.removeCacheGroup(1l);
assertEquals(0, cache.getDataBlocksInUse());
assertEquals(0, cache.getInodesInUse());
}
private static CacheEntry get(BufferFrontedFileStoreCache cache, Long oid,
Serializer<Integer> s) throws TeiidComponentException {
PhysicalInfo o = cache.lockForLoad(oid, s);
CacheEntry ce = cache.get(o, oid, new WeakReference<Serializer<?>>(s));
cache.unlockForLoad(o);
return ce;
}
@Test public void testEviction() throws Exception {
cache = createLayeredCache(1<<15, 1<<15, true, true);
assertEquals(3, cache.getMaxMemoryBlocks());
CacheEntry ce = new CacheEntry(2l);
Serializer<Integer> s = new SimpleSerializer();
WeakReference<? extends Serializer<?>> ref = new WeakReference<Serializer<?>>(s);
ce.setSerializer(ref);
cache.createCacheGroup(s.getId());
Integer cacheObject = Integer.valueOf(5000);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
ce = new CacheEntry(3l);
ce.setSerializer(ref);
cacheObject = Integer.valueOf(5001);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
assertTrue(cache.getDataBlocksInUse() < 4);
assertTrue(cache.getInodesInUse() < 2);
ce = get(cache, 2l, s);
assertEquals(Integer.valueOf(5000), ce.getObject());
ce = get(cache, 3l, s);
assertEquals(Integer.valueOf(5001), ce.getObject());
}
@Test public void testEvictionFails() throws Exception {
cache = createLayeredCache(1<<15, 1<<15, false, true);
BufferManagerImpl bmi = Mockito.mock(BufferManagerImpl.class);
cache.setBufferManager(bmi);
Serializer<Integer> s = new SimpleSerializer();
WeakReference<? extends Serializer<?>> ref = new WeakReference<Serializer<?>>(s);
cache.createCacheGroup(s.getId());
for (int i = 0; i < 3; i++) {
add(cache, s, ref, i);
}
Mockito.verify(bmi, Mockito.atLeastOnce()).invalidCacheGroup(Long.valueOf(1));
}
private void add(BufferFrontedFileStoreCache cache, Serializer<Integer> s,
WeakReference<? extends Serializer<?>> ref, int i) {
CacheEntry ce = new CacheEntry(Long.valueOf(i));
ce.setSerializer(ref);
Integer cacheObject = Integer.valueOf(5000 + i);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
}
private static BufferFrontedFileStoreCache createLayeredCache(int bufferSpace, int objectSize, boolean memStorage, boolean allocate) throws TeiidComponentException {
BufferFrontedFileStoreCache fsc = new BufferFrontedFileStoreCache();
fsc.cleanerRunning.set(true); //prevent asynch affects
fsc.setMemoryBufferSpace(bufferSpace);
fsc.setMaxStorageObjectSize(objectSize);
fsc.setDirect(false);
if (memStorage) {
SplittableStorageManager ssm = new SplittableStorageManager(new MemoryStorageManager());
ssm.setMaxFileSizeDirect(MemoryStorageManager.MAX_FILE_SIZE);
fsc.setStorageManager(ssm);
} else {
StorageManager sm = new StorageManager() {
@Override
public void initialize() throws TeiidComponentException {
}
@Override
public FileStore createFileStore(String name) {
return new FileStore() {
@Override
public void setLength(long length) throws IOException {
throw new OutOfDiskException(null);
}
@Override
protected void removeDirect() {
}
@Override
protected int readWrite(long fileOffset, byte[] b, int offSet, int length,
boolean write) throws IOException {
return 0;
}
@Override
public long getLength() {
return 0;
}
};
}
@Override
public long getMaxStorageSpace() {
return -1;
}
};
fsc.setStorageManager(sm);
}
fsc.initialize(allocate);
return fsc;
}
@Test public void testSizeIndex() throws Exception {
PhysicalInfo info = new PhysicalInfo(1l, 1l, -1, 0, 0);
info.setSize(1<<13);
assertEquals(0, info.sizeIndex);
info = new PhysicalInfo(1l, 1l, -1, 0, 0);
info.setSize(1 + (1<<13));
assertEquals(1, info.sizeIndex);
info = new PhysicalInfo(1l, 1l, -1, 0, 0);
info.setSize(2 + (1<<15));
assertEquals(3, info.sizeIndex);
}
@Test(expected=Exception.class) public void testSizeChanged() throws Exception {
PhysicalInfo info = new PhysicalInfo(1l, 1l, -1, 0, 0);
info.setSize(1<<13);
assertEquals(0, info.sizeIndex);
info.setSize(1 + (1<<13));
}
@Test public void testDefragTruncateEmpty() throws Exception {
cache = createLayeredCache(1<<15, 1<<15, true, true);
cache.setMinDefrag(10000000);
Serializer<Integer> s = new SimpleSerializer();
WeakReference<? extends Serializer<?>> ref = new WeakReference<Serializer<?>>(s);
cache.createCacheGroup(s.getId());
Integer cacheObject = Integer.valueOf(5000);
for (int i = 0; i < 4; i++) {
CacheEntry ce = new CacheEntry((long)i);
ce.setSerializer(ref);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
}
assertEquals(98304, cache.getDiskUsage());
for (int i = 0; i < 4; i++) {
cache.remove(1l, (long)i);
}
assertEquals(98304, cache.getDiskUsage());
cache.setMinDefrag(0);
cache.defragTask.run();
assertEquals(98304, cache.getDiskUsage());
cache.setTruncateInterval(1);
cache.defragTask.run();
assertEquals(0, cache.getDiskUsage());
}
@Test public void testDefragTruncate() throws Exception {
cache = createLayeredCache(1<<15, 1<<15, true, true);
cache.setMinDefrag(10000000);
Serializer<Integer> s = new SimpleSerializer();
WeakReference<? extends Serializer<?>> ref = new WeakReference<Serializer<?>>(s);
cache.createCacheGroup(s.getId());
Integer cacheObject = Integer.valueOf(5000);
for (int i = 0; i < 30; i++) {
CacheEntry ce = new CacheEntry((long)i);
ce.setSerializer(ref);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
}
assertEquals(950272, cache.getDiskUsage());
for (int i = 0; i < 25; i++) {
cache.remove(1l, (long)i);
}
assertEquals(950272, cache.getDiskUsage());
cache.setMinDefrag(0);
cache.setTruncateInterval(1);
cache.defragTask.run();
assertEquals(622592, cache.getDiskUsage());
cache.defragTask.run();
assertEquals(262144, cache.getDiskUsage());
cache.defragTask.run();
assertEquals(131072, cache.getDiskUsage());
cache.defragTask.run();
//we've reached a stable size
assertEquals(131072, cache.getDiskUsage());
}
@Test public void testDefragTruncateCompact() throws Exception {
cache = createLayeredCache(1<<15, 1<<15, true, true);
cache.setCompactBufferFiles(true);
cache.setTruncateInterval(1);
cache.setMinDefrag(10000000);
Serializer<Integer> s = new SimpleSerializer();
WeakReference<? extends Serializer<?>> ref = new WeakReference<Serializer<?>>(s);
cache.createCacheGroup(s.getId());
Integer cacheObject = Integer.valueOf(5000);
for (int i = 0; i < 30; i++) {
CacheEntry ce = new CacheEntry((long)i);
ce.setSerializer(ref);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
}
assertEquals(950272, cache.getDiskUsage());
for (int i = 0; i < 25; i++) {
cache.remove(1l, (long)i);
}
assertEquals(950272, cache.getDiskUsage());
cache.setMinDefrag(0);
cache.setTruncateInterval(1);
cache.defragTask.run();
assertEquals(131072, cache.getDiskUsage());
cache.defragTask.run();
//we've reached a stable size
assertEquals(131072, cache.getDiskUsage());
}
@Test public void testDefragMin() throws Exception {
cache = createLayeredCache(1<<15, 1<<15, true, true);
cache.setMinDefrag(10000000);
Serializer<Integer> s = new SimpleSerializer();
WeakReference<? extends Serializer<?>> ref = new WeakReference<Serializer<?>>(s);
cache.createCacheGroup(s.getId());
Integer cacheObject = Integer.valueOf(5000);
for (int i = 0; i < 100; i++) {
CacheEntry ce = new CacheEntry((long)i);
ce.setSerializer(ref);
ce.setObject(cacheObject);
cache.addToCacheGroup(s.getId(), ce.getId());
cache.add(ce, s);
}
assertEquals(3244032, cache.getDiskUsage());
for (int i = 0; i < 90; i++) {
cache.remove(1l, (long)i);
}
assertEquals(3244032, cache.getDiskUsage());
cache.setMinDefrag(5000);
cache.setTruncateInterval(1);
cache.defragTask.run();
assertEquals(1802240, cache.getDiskUsage());
cache.defragTask.run();
assertEquals(1114112, cache.getDiskUsage());
cache.defragTask.run();
assertEquals(655360, cache.getDiskUsage());
cache.defragTask.run();
//we've reached a stable size
assertEquals(655360, cache.getDiskUsage());
}
@Test public void testLargeMax() throws TeiidComponentException {
createLayeredCache(1 << 20, 1 << 30, false, false);
}
@Test public void testNonAlignedMaxBlocks() throws TeiidComponentException {
BufferFrontedFileStoreCache bf = createLayeredCache(1 << 20, 8000000, false, false);
assertEquals(974, bf.getMaxMemoryBlocks());
}
}