/** * diqube: Distributed Query Base. * * Copyright (C) 2015 Bastian Gloeckle * * This file is part of diqube. * * diqube is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * 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/>. */ package org.diqube.executionenv; import java.util.UUID; import org.diqube.cache.CountingCacheTestUtil; import org.diqube.context.Profiles; import org.diqube.data.flatten.FlattenedTable; import org.diqube.util.Pair; import org.mockito.Mockito; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests for {@link FlattenedTableInstanceManager}. * * @author Bastian Gloeckle */ public class FlattenedTableInstanceManagerTest { private static final int MEMORY_CAP_CACHE_MB = 5; private static final String TABLE = "tab"; private static final String FLATTEN_BY = "a[*]"; private AnnotationConfigApplicationContext dataContext; private FlattenedTableInstanceManager flattenedTableManager; @BeforeMethod public void before() { dataContext = new AnnotationConfigApplicationContext(); dataContext.getEnvironment().setActiveProfiles(Profiles.UNIT_TEST); dataContext.scan("org.diqube"); dataContext.refresh(); flattenedTableManager = dataContext.getBean(FlattenedTableInstanceManager.class); flattenedTableManager.setFlattenedTableCacheSizeMb(MEMORY_CAP_CACHE_MB); flattenedTableManager.initialize(); // force the internal cache to do the internal cleanup /always/. CountingCacheTestUtil.setCleanupStrategy(flattenedTableManager.getCache(), () -> true); } @AfterMethod public void after() { dataContext.close(); } @Test public void oldCountGetsAddedToNewVersionAndOldVersionGetsEvicted() { // GIVEN FlattenedTable oldTable = table(MEMORY_CAP_CACHE_MB); UUID oldUuid = UUID.randomUUID(); FlattenedTable newTable = table(MEMORY_CAP_CACHE_MB); UUID newUuid = UUID.randomUUID(); flattenedTableManager.setFlagNanoseconds(0L); // no automatic flagging in register call flattenedTableManager.setCacheConsolidateStrategy(() -> false); // never consolidate automatically, we trigger this // manually. // WHEN flattenedTableManager.registerFlattenedTableVersion(oldUuid, oldTable, TABLE, FLATTEN_BY); flattenedTableManager.registerFlattenedTableVersion(newUuid, newTable, TABLE, FLATTEN_BY); flattenedTableManager.getCache().consolidate(); // THEN Assert.assertEquals(flattenedTableManager.getNewestFlattenedTableVersion(TABLE, FLATTEN_BY).getLeft(), newUuid, "Expected that new table is newest one"); Assert.assertTrue(flattenedTableManager.getNewestFlattenedTableVersion(TABLE, FLATTEN_BY).getRight() == newTable, "Expected that new table is newest one"); Assert.assertTrue(flattenedTableManager.getFlattenedTable(newUuid, TABLE, FLATTEN_BY) == newTable, "Expected that newest table is returned when querying."); Assert.assertNull(flattenedTableManager.getFlattenedTable(oldUuid, TABLE, FLATTEN_BY), "Expected that old table is evicted."); Pair<String, String> keyPair = new Pair<>(TABLE, FLATTEN_BY); Assert.assertEquals((long) flattenedTableManager.getCache().getCount(keyPair, newUuid), 3L /* 2 register, 1 get */, "Expected correct count for new version"); // null: 1 registered (removed when new version was registered), 1 get (did not succeed because element was fully // evicted already). Assert.assertNull(flattenedTableManager.getCache().getCount(keyPair, oldUuid), "Expected correct count for old version"); } @Test public void registeringFlagsTable() { // GIVEN FlattenedTable oldTable = table(MEMORY_CAP_CACHE_MB); UUID oldUuid = UUID.randomUUID(); FlattenedTable newTable = table(MEMORY_CAP_CACHE_MB); UUID newUuid = UUID.randomUUID(); flattenedTableManager.setFlagNanoseconds(10 * 1_000_000_000L); // 10s flattenedTableManager.setCacheConsolidateStrategy(() -> false); // never consolidate automatically, we trigger this // manually. // WHEN flattenedTableManager.registerFlattenedTableVersion(oldUuid, oldTable, TABLE, FLATTEN_BY); flattenedTableManager.registerFlattenedTableVersion(newUuid, newTable, TABLE, FLATTEN_BY); flattenedTableManager.getCache().consolidate(); // THEN Assert.assertEquals(flattenedTableManager.getNewestFlattenedTableVersion(TABLE, FLATTEN_BY).getLeft(), newUuid, "Expected that new table is newest one"); Assert.assertTrue(flattenedTableManager.getNewestFlattenedTableVersion(TABLE, FLATTEN_BY).getRight() == newTable, "Expected that new table is newest one"); Assert.assertTrue(flattenedTableManager.getFlattenedTable(newUuid, TABLE, FLATTEN_BY) == newTable, "Expected that newest table is returned when querying."); // oldTable should still be returned, since it should be flagged. Assert.assertTrue(flattenedTableManager.getFlattenedTable(oldUuid, TABLE, FLATTEN_BY) == oldTable, "Expected that old table is returned when querying."); Pair<String, String> keyPair = new Pair<>(TABLE, FLATTEN_BY); flattenedTableManager.getCache().consolidate(); Assert.assertEquals((long) flattenedTableManager.getCache().getCount(keyPair, newUuid), 3L /* 2 register, 1 get */, "Expected correct count for new version"); // oldtable should not have a count anymore -> It will be evicted after its flagging time is over. Assert.assertEquals((long) flattenedTableManager.getCache().getCount(keyPair, oldUuid), 1L /* 1 register (removed when new version was registered), 1 get */, "Expected correct count for old version"); } @Test public void newestFlagFlags() throws InterruptedException { // GIVEN FlattenedTable oldTable = table(MEMORY_CAP_CACHE_MB); UUID oldUuid = UUID.randomUUID(); FlattenedTable newTable = table(MEMORY_CAP_CACHE_MB); UUID newUuid = UUID.randomUUID(); flattenedTableManager.setFlagNanoseconds(2 * 1_000_000_000L); // 2s flattenedTableManager.setCacheConsolidateStrategy(() -> false); // never consolidate automatically, we trigger this // manually. // WHEN flattenedTableManager.registerFlattenedTableVersion(oldUuid, oldTable, TABLE, FLATTEN_BY); Thread.sleep(1500); Pair<UUID, FlattenedTable> newestReturn = flattenedTableManager.getNewestFlattenedTableVersionAndFlagIt(TABLE, FLATTEN_BY); flattenedTableManager.registerFlattenedTableVersion(newUuid, newTable, TABLE, FLATTEN_BY); Thread.sleep(1000); // if only "register" flagged the entry, that flag is now outdated. But if "newest" flagged it, // too, the flag is still valid. flattenedTableManager.getCache().consolidate(); // THEN Assert.assertEquals(newestReturn.getLeft(), oldUuid, "The newest method should've returned the correct ID."); Assert.assertTrue(newestReturn.getRight() == oldTable, "The newest method should've returned the correct table."); Assert.assertEquals(flattenedTableManager.getNewestFlattenedTableVersion(TABLE, FLATTEN_BY).getLeft(), newUuid, "Expected that new table is newest one"); Assert.assertTrue(flattenedTableManager.getNewestFlattenedTableVersion(TABLE, FLATTEN_BY).getRight() == newTable, "Expected that new table is newest one"); Assert.assertTrue(flattenedTableManager.getFlattenedTable(newUuid, TABLE, FLATTEN_BY) == newTable, "Expected that newest table is returned when querying."); // oldTable should still be returned, since it should be flagged. Assert.assertTrue(flattenedTableManager.getFlattenedTable(oldUuid, TABLE, FLATTEN_BY) == oldTable, "Expected that old table is returned when querying."); Pair<String, String> keyPair = new Pair<>(TABLE, FLATTEN_BY); Assert.assertEquals((long) flattenedTableManager.getCache().getCount(keyPair, newUuid), 3/* 2 register, 1 get */, "Expected correct count for new version"); // oldtable should not have a count anymore -> It will be evicted after its flagging time is over. Assert.assertEquals((long) flattenedTableManager.getCache().getCount(keyPair, oldUuid), 1 /* 1 register (which was removed when the new version was registered), 1 get */, "Expected correct count for old version"); } @Test public void registerTheSameTwice() throws InterruptedException { // GIVEN FlattenedTable oldTable = table(MEMORY_CAP_CACHE_MB); FlattenedTable newTable = table(MEMORY_CAP_CACHE_MB); UUID uuid = UUID.randomUUID(); flattenedTableManager.setFlagNanoseconds(2 * 1_000_000_000L); // 2s flattenedTableManager.setCacheConsolidateStrategy(() -> false); // never consolidate automatically, we trigger this // manually. // WHEN // use same UUID, but different table instances so we can disinguish later. flattenedTableManager.registerFlattenedTableVersion(uuid, oldTable, TABLE, FLATTEN_BY); flattenedTableManager.registerFlattenedTableVersion(uuid, newTable, TABLE, FLATTEN_BY); flattenedTableManager.getCache().consolidate(); // THEN Assert.assertEquals(flattenedTableManager.getNewestFlattenedTableVersion(TABLE, FLATTEN_BY).getLeft(), uuid, "Expected that valid table is newest one"); Assert.assertTrue(flattenedTableManager.getNewestFlattenedTableVersion(TABLE, FLATTEN_BY).getRight() == oldTable, "Expected that valid table is newest one"); Assert.assertTrue(flattenedTableManager.getFlattenedTable(uuid, TABLE, FLATTEN_BY) == oldTable, "Expected that old table is returned when querying."); Pair<String, String> keyPair = new Pair<>(TABLE, FLATTEN_BY); // this will fail if e.g. the entry has falsely been marked for eviction. Assert.assertEquals((long) flattenedTableManager.getCache().getCount(keyPair, uuid), 3L/* two register, one get */, "Expected correct count"); } private FlattenedTable table(long memoryMB) { FlattenedTable mockedTable = Mockito.mock(FlattenedTable.class); Mockito.when(mockedTable.calculateApproximateSizeInBytes()).thenReturn(memoryMB * 1024L * 1024L); return mockedTable; } }