/* * 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()); } }