/* * Copyright 2008-2009 LinkedIn, Inc * * 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 voldemort.store.bdb; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; import java.io.File; import java.util.Arrays; import java.util.Collection; import junit.framework.Assert; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import voldemort.TestUtils; import voldemort.server.VoldemortConfig; import voldemort.utils.ByteUtils; import voldemort.utils.Props; import voldemort.versioning.Versioned; import com.sleepycat.je.Database; import com.sleepycat.je.DatabaseConfig; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.Durability; import com.sleepycat.je.Environment; import com.sleepycat.je.EnvironmentConfig; import com.sleepycat.je.EnvironmentStats; import com.sleepycat.je.StatsConfig; /** * checks BDB runtime behavior relating to operating multiple environments * * */ @RunWith(Parameterized.class) public class BdbSplitStorageEngineTest { private File bdbMasterDir; private BdbStorageConfiguration bdbStorage; private static long CACHE_SIZE = (long) Math.min(Runtime.getRuntime().maxMemory() * 0.30, 32 * 1000 * 1000); private boolean prefixPartitionId; public BdbSplitStorageEngineTest(boolean prefixPartitionId) { this.prefixPartitionId = prefixPartitionId; } @Parameters public static Collection<Object[]> modes() { Object[][] data = new Object[][] { { true }, { false } }; return Arrays.asList(data); } @Before public void setUp() throws Exception { bdbMasterDir = TestUtils.createTempDir(); } @After public void tearDown() throws Exception { if(bdbStorage != null) bdbStorage.close(); } @Test public void testNoMultipleEnvironment() { // lets use all the default values. Props props = new Props(); props.put("node.id", 1); props.put("voldemort.home", "test/common/voldemort/config"); VoldemortConfig voldemortConfig = new VoldemortConfig(props); voldemortConfig.setBdbCacheSize(1 * 1024 * 1024); voldemortConfig.setBdbDataDirectory(bdbMasterDir.toURI().getPath()); voldemortConfig.setBdbOneEnvPerStore(false); voldemortConfig.setBdbPrefixKeysWithPartitionId(prefixPartitionId); bdbStorage = new BdbStorageConfiguration(voldemortConfig); BdbStorageEngine storeA = (BdbStorageEngine) bdbStorage.getStore(TestUtils.makeStoreDefinition("storeA"), TestUtils.makeSingleNodeRoutingStrategy()); BdbStorageEngine storeB = (BdbStorageEngine) bdbStorage.getStore(TestUtils.makeStoreDefinition("storeB"), TestUtils.makeSingleNodeRoutingStrategy()); storeA.put(TestUtils.toByteArray("testKey1"), new Versioned<byte[]>("value".getBytes()), null); storeA.put(TestUtils.toByteArray("testKey2"), new Versioned<byte[]>("value".getBytes()), null); storeA.put(TestUtils.toByteArray("testKey3"), new Versioned<byte[]>("value".getBytes()), null); storeB.put(TestUtils.toByteArray("testKey1"), new Versioned<byte[]>("value".getBytes()), null); storeB.put(TestUtils.toByteArray("testKey2"), new Versioned<byte[]>("value".getBytes()), null); storeB.put(TestUtils.toByteArray("testKey3"), new Versioned<byte[]>("value".getBytes()), null); storeA.close(); storeB.close(); Assert.assertEquals("common BDB file should exists.", true, (bdbMasterDir.exists())); assertNotSame("StoreA BDB file should not exists.", true, (new File(bdbMasterDir + "/" + "storeA").exists())); assertNotSame("StoreB BDB file should not exists.", true, (new File(bdbMasterDir + "/" + "storeB").exists())); } @Test public void testMultipleEnvironment() { // lets use all the default values. Props props = new Props(); props.put("node.id", 1); props.put("voldemort.home", "test/common/voldemort/config"); VoldemortConfig voldemortConfig = new VoldemortConfig(props); voldemortConfig.setBdbCacheSize(1 * 1024 * 1024); voldemortConfig.setBdbOneEnvPerStore(true); voldemortConfig.setBdbDataDirectory(bdbMasterDir.toURI().getPath()); voldemortConfig.setBdbPrefixKeysWithPartitionId(prefixPartitionId); bdbStorage = new BdbStorageConfiguration(voldemortConfig); BdbStorageEngine storeA = (BdbStorageEngine) bdbStorage.getStore(TestUtils.makeStoreDefinition("storeA"), TestUtils.makeSingleNodeRoutingStrategy()); BdbStorageEngine storeB = (BdbStorageEngine) bdbStorage.getStore(TestUtils.makeStoreDefinition("storeB"), TestUtils.makeSingleNodeRoutingStrategy()); storeA.put(TestUtils.toByteArray("testKey1"), new Versioned<byte[]>("value".getBytes()), null); storeA.put(TestUtils.toByteArray("testKey2"), new Versioned<byte[]>("value".getBytes()), null); storeA.put(TestUtils.toByteArray("testKey3"), new Versioned<byte[]>("value".getBytes()), null); storeB.put(TestUtils.toByteArray("testKey1"), new Versioned<byte[]>("value".getBytes()), null); storeB.put(TestUtils.toByteArray("testKey2"), new Versioned<byte[]>("value".getBytes()), null); storeB.put(TestUtils.toByteArray("testKey3"), new Versioned<byte[]>("value".getBytes()), null); storeA.close(); storeB.close(); assertEquals("StoreA BDB file should exists.", true, (new File(bdbMasterDir + "/" + "storeA").exists())); assertEquals("StoreB BDB file should exists.", true, (new File(bdbMasterDir + "/" + "storeB").exists())); } @Test public void testUnsharedCache() throws DatabaseException { EnvironmentConfig environmentConfig = new EnvironmentConfig(); environmentConfig.setDurability(Durability.COMMIT_NO_SYNC); environmentConfig.setAllowCreate(true); environmentConfig.setTransactional(true); environmentConfig.setSharedCache(false); environmentConfig.setCacheSize(CACHE_SIZE); DatabaseConfig databaseConfig = new DatabaseConfig(); databaseConfig.setAllowCreate(true); databaseConfig.setTransactional(true); databaseConfig.setSortedDuplicates(true); long maxCacheSize = getMaxCacheUsage(environmentConfig, databaseConfig); assertEquals("MaxCacheSize > CACHE_SIZE", true, maxCacheSize > CACHE_SIZE); assertEquals("MaxCacheSize < 2 * CACHE_SIZE", true, maxCacheSize < 2 * CACHE_SIZE); } @Test public void testSharedCache() throws DatabaseException { EnvironmentConfig environmentConfig = new EnvironmentConfig(); environmentConfig.setDurability(Durability.COMMIT_NO_SYNC); environmentConfig.setAllowCreate(true); environmentConfig.setTransactional(true); environmentConfig.setSharedCache(true); environmentConfig.setCacheSize(CACHE_SIZE); DatabaseConfig databaseConfig = new DatabaseConfig(); databaseConfig.setAllowCreate(true); databaseConfig.setTransactional(true); databaseConfig.setSortedDuplicates(true); long maxCacheSize = getMaxCacheUsage(environmentConfig, databaseConfig); // Include a buffer of 1mb.. since the actual cache usage can be a few // bytes more assertEquals("MaxCacheSize" + maxCacheSize + " <= CACHE_SIZE:" + CACHE_SIZE, true, maxCacheSize <= (CACHE_SIZE + ByteUtils.BYTES_PER_MB)); } private long getMaxCacheUsage(EnvironmentConfig environmentConfig, DatabaseConfig databaseConfig) throws DatabaseException { File dirA = new File(bdbMasterDir + "/" + "storeA"); if(!dirA.exists()) { dirA.mkdirs(); } Environment environmentA = new Environment(dirA, environmentConfig); Database databaseA = environmentA.openDatabase(null, "storeA", databaseConfig); BdbStorageEngine storeA = BdbStorageEngineTest.makeBdbStorageEngine("storeA", environmentA, databaseA, new BdbRuntimeConfig(), this.prefixPartitionId); File dirB = new File(bdbMasterDir + "/" + "storeB"); if(!dirB.exists()) { dirB.mkdirs(); } Environment environmentB = new Environment(dirB, environmentConfig); Database databaseB = environmentB.openDatabase(null, "storeB", databaseConfig); BdbStorageEngine storeB = BdbStorageEngineTest.makeBdbStorageEngine("storeB", environmentB, databaseB, new BdbRuntimeConfig(), this.prefixPartitionId); long maxCacheUsage = 0; for(int i = 0; i <= 10000; i++) { byte[] value = new byte[(int) (CACHE_SIZE / 10000)]; // try to push values in cache storeA.put(TestUtils.toByteArray(i + "A"), new Versioned<byte[]>(value), null); storeA.get(TestUtils.toByteArray(i + "A"), null); storeB.put(TestUtils.toByteArray(i + "B"), new Versioned<byte[]>(value), null); storeB.get(TestUtils.toByteArray(i + "B"), null); EnvironmentStats statsA = environmentA.getStats(new StatsConfig()); EnvironmentStats statsB = environmentB.getStats(new StatsConfig()); long totalCacheSize = statsA.getCacheTotalBytes() + statsB.getCacheTotalBytes(); System.out.println("A.size:" + statsA.getCacheTotalBytes() + " B.size:" + statsB.getCacheTotalBytes() + " total:" + totalCacheSize + " max:" + maxCacheUsage + " cacheMax:" + environmentA.getConfig().getCacheSize()); System.out.println("Shared.A:" + statsA.getSharedCacheTotalBytes() + " nSharedEnv:" + statsA.getNSharedCacheEnvironments()); maxCacheUsage = Math.max(maxCacheUsage, totalCacheSize); } return maxCacheUsage; } }