/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package org.voltdb; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.json_voltpatches.JSONObject; import org.json_voltpatches.JSONStringer; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.google_voltpatches.common.collect.RangeSet; import com.google_voltpatches.common.collect.TreeRangeSet; public class TestDRConsumerDrIdTracker { private DRConsumerDrIdTracker tracker; @Before public void setUp() throws IOException { tracker = DRConsumerDrIdTracker.createPartitionTracker(-1L, 0L, 0L, 0); } @After public void tearDown() throws InterruptedException { } @Test public void testAppend() throws Exception { // Append single to range tracker.append(5L, 8L, 0L, 0L); tracker.append(9L, 9L, 0L, 0L); // Append single to single tracker.append(11L, 11L, 0L, 0L); tracker.append(12L, 12L, 0L, 0L); // Append range to single tracker.append(14L, 14L, 0L, 0L); tracker.append(15L, 20L, 0L, 0L); // Append range to range tracker.append(25L, 30L, 0L, 0L); tracker.append(31L, 40L, 0L, 0L); // try appending an overlapping range boolean failed = false; try { tracker.append(40L, 45L, 0L, 0L); } catch (AssertionError e) { failed = true; } assertTrue(failed); // try appending an overlapping value failed = false; try { tracker.append(7L, 7L, 0L, 0L); } catch (AssertionError e) { failed = true; } assertTrue(failed); assertTrue(tracker.size() == 5); tracker.truncate(9L); assertTrue(tracker.size() == 4 && tracker.getSafePointDrId() == 9L); tracker.truncate(11L); assertTrue(tracker.size() == 3 && tracker.getSafePointDrId() == 12L); tracker.truncate(20L); assertTrue(tracker.size() == 2 && tracker.getSafePointDrId() == 20L); tracker.truncate(20L); assertTrue(tracker.size() == 2 && tracker.getSafePointDrId() == 20L); tracker.truncate(25L); assertTrue(tracker.size() == 1 && tracker.getSafePointDrId() == 40L); tracker.truncate(25L); assertTrue(tracker.size() == 1 && tracker.getSafePointDrId() == 40L); tracker.truncate(39L); assertTrue(tracker.size() == 1 && tracker.getSafePointDrId() == 40L); tracker.truncate(40L); assertTrue(tracker.size() == 1 && tracker.getSafePointDrId() == 40L); tracker.truncate(40L); assertTrue(tracker.size() == 1 && tracker.getSafePointDrId() == 40L); tracker.truncate(41L); assertTrue(tracker.size() == 1 && tracker.getSafePointDrId() == 41L); } @Test public void testMergeTrackers() throws Exception { RangeSet<Long> expectedMap = TreeRangeSet.create(); tracker.append(8L, 9L, 0L, 0L); tracker.append(11L, 12L, 0L, 0L); tracker.append(20L, 25L, 0L, 0L); tracker.append(30L, 35L, 0L, 0L); tracker.append(40L, 40L, 0L, 0L); tracker.append(50L, 60L, 0L, 0L); tracker.append(70L, 80L, 0L, 0L); tracker.append(90L, 90L, 0L, 0L); DRConsumerDrIdTracker tracker2 = DRConsumerDrIdTracker.createPartitionTracker(5L, 0L, 0L, 0); // This should insert a new entry before the beginning tracker2.append(6L, 6L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(6L, 6L)); // This should combine tracker's first and second entries (singletons) tracker2.append(10L, 10L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(8L, 12L)); // This should combine tracker's third and fourth entries (ranges) tracker2.append(26L, 29L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(20L, 35L)); // This should extend the new second entry tracker2.append(36L, 37L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(20L, 37L)); // This should prepend the entry starting at 40 tracker2.append(39L, 39L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(39L, 40L)); // This should prepend the entry starting at 50 tracker2.append(48L, 49L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(48L, 60L)); // This should create a new entry tracker2.append(62L, 66L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(62L, 66L)); // This should append the entry now starting at 70 tracker2.append(81L, 81L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(70L, 81L)); // This should append the entry now starting at 90 tracker2.append(91L, 95L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(90L, 95L)); // Add a new entry at the end tracker2.append(98L, 99L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(98L, 99L)); tracker.mergeTracker(tracker2); assertTrue(tracker.getSafePointDrId() == 6L); assertTrue(tracker.getDrIdRanges().equals(expectedMap)); } @Test public void testMergeTrackersWithOverlaps() throws Exception { RangeSet<Long> expectedMap = TreeRangeSet.create(); tracker.append(8L, 9L, 0L, 0L); tracker.append(11L, 12L, 0L, 0L); tracker.append(20L, 25L, 0L, 0L); tracker.append(32L, 35L, 0L, 0L); tracker.append(40L, 40L, 0L, 0L); tracker.append(50L, 60L, 0L, 0L); DRConsumerDrIdTracker tracker2 = DRConsumerDrIdTracker.createPartitionTracker(6L, 0L, 0L, 0); // overlaps with the beginning of the first entry tracker2.append(7L, 8L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(8L, 9L)); // overlaps with the end of the second entry tracker2.append(12L, 14L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(11L, 14L)); // completely covers the third entry tracker2.append(19L, 30L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(19L, 30L)); // covers multiple ranges at the end tracker2.append(36L, 70L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(32L, 70L)); tracker.mergeTracker(tracker2); assertTrue(tracker.getSafePointDrId() == 9L); assertEquals(expectedMap, tracker.getDrIdRanges()); } @Test public void testAppendToEmptyTracker() { RangeSet<Long> expectedMap = TreeRangeSet.create(); DRConsumerDrIdTracker tracker2 = DRConsumerDrIdTracker.createPartitionTracker(5L, 0L, 0L, 0); expectedMap.add(DRConsumerDrIdTracker.range(5L, 5L)); tracker2.append(11L, 11L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(11L, 11L)); tracker2.append(13L, 15L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(13L, 15L)); tracker.mergeTracker(tracker2); // should not have modified neighbor tracker assertEquals(3, tracker2.size()); // modification to the neighbor tracker should not affect our tracker after the append tracker2.getDrIdRanges().clear(); assertTrue(tracker.getDrIdRanges().equals(expectedMap)); } @Test public void testAppendNeighborTracker() throws Exception { RangeSet<Long> expectedMap = TreeRangeSet.create(); tracker.append(6L, 10L, 0L, 0L); DRConsumerDrIdTracker tracker2 = DRConsumerDrIdTracker.createPartitionTracker(2L, 0L, 0L, 0); expectedMap.add(DRConsumerDrIdTracker.range(2L, 2L)); tracker2.append(11L, 11L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(6L, 11L)); tracker2.append(13L, 15L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(13L, 15L)); tracker.mergeTracker(tracker2); // should not have modified neighbor tracker assertEquals(3, tracker2.size()); // modification to the neighbor tracker should not affect our tracker after the append tracker2.getDrIdRanges().clear(); assertEquals(expectedMap, tracker.getDrIdRanges()); } @Test public void testAppendSparseNeighborTracker() throws Exception { RangeSet<Long> expectedMap = TreeRangeSet.create(); tracker.append(6L, 10L, 0L, 0L); tracker.append(15L, 20L, 0L, 0L); DRConsumerDrIdTracker tracker2 = DRConsumerDrIdTracker.createPartitionTracker(20L, 0L, 0L, 0); expectedMap.add(DRConsumerDrIdTracker.range(20L, 20L)); tracker2.append(22L, 30L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(22L, 30L)); tracker2.append(35L, 40L, 0L, 0L); expectedMap.add(DRConsumerDrIdTracker.range(35L, 40L)); tracker.mergeTracker(tracker2); assertEquals(expectedMap, tracker.getDrIdRanges()); } @Test public void testSerialization() throws Exception { tracker.append(5L, 10L, 0L, 0L); tracker.append(15L, 20L, 0L, 0L); tracker.append(25L, 30L, 100L, 200L); byte[] flattened = new byte[tracker.getSerializedSize()]; tracker.serialize(flattened); DRConsumerDrIdTracker tracker2 = new DRConsumerDrIdTracker(flattened); assertTrue(tracker.getSafePointDrId() == tracker2.getSafePointDrId()); assertTrue(tracker.getLastSpUniqueId() == tracker2.getLastSpUniqueId()); assertTrue(tracker.getLastMpUniqueId() == tracker2.getLastMpUniqueId()); assertTrue(tracker.getDrIdRanges().equals(tracker2.getDrIdRanges())); } @Test public void testJsonSerialization() throws Exception { tracker.append(5L, 5L, 0L, 0L); tracker.append(15L, 20L, 0L, 0L); DRConsumerDrIdTracker tracker2 = DRConsumerDrIdTracker.createPartitionTracker(17L, 0L, 0L, 0); tracker2.append(20L, 25L, 0L, 0L); tracker2.append(28L, 28L, 0L, 0L); Map<Integer, DRConsumerDrIdTracker> perProducerPartitionTrackers = new HashMap<Integer, DRConsumerDrIdTracker>(); perProducerPartitionTrackers.put(0, tracker); perProducerPartitionTrackers.put(1, tracker2); Map<Integer, Map<Integer, DRConsumerDrIdTracker>> perSiteTrackers = new HashMap<Integer, Map<Integer, DRConsumerDrIdTracker>>(); // Insert trackers from cluster 20 perSiteTrackers.put(20, perProducerPartitionTrackers); JSONObject trackersInJSON = ExtensibleSnapshotDigestData.serializeSiteConsumerDrIdTrackersToJSON(perSiteTrackers); JSONStringer stringer = new JSONStringer(); stringer.object(); stringer.key("5"); // ConsumerPartitionId stringer.value(trackersInJSON); stringer.endObject(); String output = stringer.toString(); JSONObject allsiteInfo = new JSONObject(output); JSONObject siteInfo = allsiteInfo.getJSONObject("5"); final Map<Integer, Map<Integer, DRConsumerDrIdTracker>> siteTrackers = ExtensibleSnapshotDigestData.buildConsumerSiteDrIdTrackersFromJSON(siteInfo); DRConsumerDrIdTracker tracker3 = siteTrackers.get(20).get(0); DRConsumerDrIdTracker tracker4 = siteTrackers.get(20).get(1); assertTrue(tracker.getSafePointDrId() == tracker3.getSafePointDrId()); assertTrue(tracker.getLastSpUniqueId() == tracker3.getLastSpUniqueId()); assertTrue(tracker.getLastMpUniqueId() == tracker3.getLastMpUniqueId()); assertTrue(tracker.getDrIdRanges().equals(tracker3.getDrIdRanges())); assertTrue(tracker2.getSafePointDrId() == tracker4.getSafePointDrId()); assertTrue(tracker2.getLastSpUniqueId() == tracker4.getLastSpUniqueId()); assertTrue(tracker2.getLastMpUniqueId() == tracker4.getLastMpUniqueId()); assertTrue(tracker2.getDrIdRanges().equals(tracker4.getDrIdRanges())); } @Test public void testContains() { tracker.append(5L, 10L, 0L, 0L); tracker.append(15L, 20L, 0L, 0L); tracker.append(22L, 30L, 0L, 0L); tracker.append(35L, 40L, 0L, 0L); tracker.truncate(6L); assertTrue(tracker.contains(2L, 2L)); assertTrue(tracker.contains(4L, 10L)); assertTrue(tracker.contains(10L, 10L)); assertFalse(tracker.contains(14L, 33L)); assertTrue(tracker.contains(16L, 19L)); assertFalse(tracker.contains(21L, 21L)); assertTrue(tracker.contains(25L, 25L)); assertTrue(tracker.contains(30L, 30L)); assertTrue(tracker.contains(40L, 40L)); assertFalse(tracker.contains(38L, 45L)); assertFalse(tracker.contains(41L, 45L)); assertFalse(tracker.contains(45L, 45L)); } @Test public void testFirstLastDrId() { tracker.append(5L, 10L, 0L, 0L); tracker.append(15L, 20L, 0L, 0L); tracker.append(25L, 30L, 0L, 0L); tracker.truncate(5L); assertEquals(5L, tracker.getFirstDrId()); assertEquals(30L, tracker.getLastDrId()); } }