package com.tesora.dve.test.autoincrement; /* * #%L * Tesora Inc. * Database Virtualization Engine * %% * Copyright (C) 2011 - 2014 Tesora Inc. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import com.tesora.dve.common.catalog.AutoIncrementTracker; import com.tesora.dve.common.catalog.CatalogDAO; import com.tesora.dve.common.catalog.TestCatalogHelper; import com.tesora.dve.common.catalog.UserDatabase; import com.tesora.dve.common.catalog.UserTable; import com.tesora.dve.common.catalog.CatalogDAO.CatalogDAOFactory; import com.tesora.dve.exceptions.PEException; import com.tesora.dve.server.bootstrap.BootstrapHost; import com.tesora.dve.sql.util.ProxyConnectionResource; import com.tesora.dve.standalone.PETest; import com.tesora.dve.test.simplequery.SimpleQueryTest; public class AutoIncrementTrackerTest extends PETest { static UserDatabase db; static UserTable foo, bar; @BeforeClass public static void setup() throws Throwable { Class<?> bootClass = PETest.class; TestCatalogHelper.createTestCatalog(bootClass); bootHost = BootstrapHost.startServices(bootClass); SimpleQueryTest.cleanupSites(2,"TestDB"); ProxyConnectionResource pcr = new ProxyConnectionResource(); SimpleQueryTest.createSites(2, pcr); SimpleQueryTest.createGroupAndTestDB(2, pcr); pcr.execute("create table foo (id int auto_increment, value varchar(20)) random distribute"); pcr.execute("create table bar (id int auto_increment, value varchar(20)) random distribute"); pcr.disconnect(); db = getGlobalDAO().findDatabase(UserDatabase.DEFAULT); foo = db.getTableByName("foo"); bar = db.getTableByName("bar"); } @Before public void beforeTest() throws Exception { AutoIncrementTracker autoIncrBar = bar.getAutoIncr(); AutoIncrementTracker autoIncrFoo = foo.getAutoIncr(); getGlobalDAO().begin(); autoIncrBar.reset(catalogDAO); autoIncrFoo.reset(catalogDAO); getGlobalDAO().commit(); assertEquals("Tracker " + bar.getAutoIncr().getId() + " not reset: ", 1, bar.getAutoIncr().readNextValue(catalogDAO)); assertEquals("Tracker " + foo.getAutoIncr().getId() + " not reset: ", 1, foo.getAutoIncr().readNextValue(catalogDAO)); } @Test public void testGetNextValue() throws PEException { catalogDAO.refresh(foo); // Test that there are no gaps when the next block is gotten for (int i = 1; i < 26; ++i) { assertEquals(i, foo.getNextIncrValue(catalogDAO)); } } @Test public void testGetIdBlock() throws PEException { catalogDAO.refresh(bar); // test that block requests increment ids correctly assertEquals(1, bar.getNextIncrValue(catalogDAO)); assertEquals(2, bar.getNextIncrBlock(catalogDAO, 2)); assertEquals(4, bar.getNextIncrValue(catalogDAO)); assertEquals(5, bar.getNextIncrBlock(catalogDAO, 2)); assertEquals(7, bar.getNextIncrValue(catalogDAO)); assertEquals(8, bar.getNextIncrBlock(catalogDAO, 10)); assertEquals(18, bar.getNextIncrValue(catalogDAO)); assertEquals(19, bar.getNextIncrBlock(catalogDAO, 10)); assertEquals(29, bar.getNextIncrBlock(catalogDAO, 2)); assertEquals(31, bar.getNextIncrValue(catalogDAO)); } @Test public void testRemoveValue() throws PEException { long[] testValues = { 5, 10, 20, 50 }; catalogDAO.refresh(bar); AutoIncrementTracker autoIncr = bar.getAutoIncr(); for (int tv = 0; tv < testValues.length; ++tv) { long testValue = testValues[tv]; autoIncr.removeValue(catalogDAO, testValue); for(long incrValue; (incrValue=autoIncr.getNextValue(catalogDAO)) <= testValue;) assertTrue("AutoIncrementTracker returned removed value " + testValue, incrValue != testValue); // make sure calling remove again results in a no-op and no exception is thrown autoIncr.removeValue(catalogDAO, testValue); } } @Test public void testRemoveValueStatic() throws PEException { long[] testValues = { 5, 10, 20, 50 }; catalogDAO.refresh(bar); AutoIncrementTracker autoIncr = bar.getAutoIncr(); for (int tv = 0; tv < testValues.length; ++tv) { long testValue = testValues[tv]; AutoIncrementTracker.removeValue(catalogDAO, autoIncr.getId(), testValue); for(long incrValue; (incrValue=autoIncr.getNextValue(catalogDAO)) <= testValue;) assertTrue("AutoIncrementTracker returned removed value " + testValue, incrValue != testValue); // make sure calling remove again results in a no-op and no exception is thrown AutoIncrementTracker.removeValue(catalogDAO, autoIncr.getId(), testValue); } } @Ignore @Test public void testConcurrentGetNextId_128() throws Exception { doTestConcurrentGetIdBlock(1, 128, 20000); } @Ignore @Test public void testConcurrentGetBlock() throws Exception { doTestConcurrentGetIdBlock(13, 32, 20000); } static int WARMUP_ITERATIONS = 1000; private void doTestConcurrentGetIdBlock(int numIds, int numThreads, int numIterations) throws Exception { catalogDAO.refresh(foo); final ExecutorService executor = Executors .newFixedThreadPool(numThreads); final AutoIncrementTracker ait = foo.getAutoIncr(); System.out.format("AutoIncrementTracker.getIdBlock(%d) with %d threads:%n", numIds, numThreads); // create a catalog DAO per thread BlockingQueue<CatalogDAO> catalogs = new ArrayBlockingQueue<CatalogDAO>(numThreads); for (int i = 0; i < numThreads; i++) { catalogs.put(CatalogDAOFactory.newInstance()); } if (WARMUP_ITERATIONS > 0) { System.out.println("Warming up... (" + WARMUP_ITERATIONS + ")"); executor.invokeAll(getTasks(ait.getId(), WARMUP_ITERATIONS, numIds, catalogs)); } System.out.println("Running ... (" + numIterations + ")"); List<Callable<Long>> incrementTasks = getTasks(ait.getId(), numIterations, numIds, catalogs); long start = System.currentTimeMillis(); List<Future<Long>> results = executor.invokeAll(incrementTasks); long elapsed = System.currentTimeMillis() - start; double average = elapsed / (double) numIterations; System.out.format(" Average time (ms) = %.2f%n", average); // cleanup executor.shutdown(); do { catalogs.take().close(); } while (!catalogs.isEmpty()); AutoIncrementTracker.dumpStats(); // validate results Set<Long> set = new HashSet<Long>(); for (Future<Long> future : results) { set.add(future.get()); } assertEquals(results.size(), set.size()); // wait for any pending cache update... Thread.sleep(500); catalogDAO.begin(); assertEquals((WARMUP_ITERATIONS + numIterations) * numIds + ait.getCacheSize() + 1, ait.readNextValue(catalogDAO)); catalogDAO.commit(); } private List<Callable<Long>> getTasks(final int tableId, final int iterations, final int numIds, final BlockingQueue<CatalogDAO> catalogs) { List<Callable<Long>> tasks = new ArrayList<Callable<Long>>(iterations); for (int i = 0; i < iterations; i++) { Callable<Long> task = new Callable<Long>() { @Override public Long call() throws Exception { CatalogDAO c = catalogs.take(); try { Thread.sleep(57); return AutoIncrementTracker.getIdBlock(c, tableId, numIds); } finally { catalogs.put(c); } } }; tasks.add(task); } return tasks; } }