/* 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.iv2; import java.util.HashSet; import java.util.concurrent.atomic.AtomicLong; import junit.framework.TestCase; public class TestUniqueIdGenerator extends TestCase { private UniqueIdGenerator tim; @Override public void setUp() { tim = new UniqueIdGenerator(MpInitiator.MP_INIT_PID, 0); } public void testGetNextUniqueId() { long numIds = 100000; long numBusyWork = 0; HashSet<Long> generatedIds = new HashSet<Long>(); long start = System.nanoTime(); for (int ii = 0; ii < numIds; ii++) { Long id = tim.getNextUniqueId(); assertEquals( false, generatedIds.contains(id)); generatedIds.add(id); //make some busy work for (int zz = 0; zz < numBusyWork; zz++) { long foo1 = zz; Long foo2 = foo1; long foo3 = foo2; Long foo4 = foo3; foo4++; } } generatedIds.clear(); long end = System.nanoTime(); double nanosPerId = (end - start) / numIds; System.out.println("Finished in " + (end - start) + " nanoseconds with " + nanosPerId + " nanoseconds per generated id"); } /** make a good faith effort to wrap the intra-ms counter bits. */ public void testSpinForNextUniqueId() { long lastid = 0; long id = 0; long iters = UniqueIdGenerator.COUNTER_MAX_VALUE * 3; for (int i = 0; i < iters; ++i) { id = tim.getNextUniqueId(); assertTrue(id > lastid); lastid = id; } } public void testSiteIdFromTransactionId() { long siteid = UniqueIdGenerator.getPartitionIdFromUniqueId(tim.getNextUniqueId()); assertEquals(siteid, MpInitiator.MP_INIT_PID); UniqueIdGenerator tim2 = new UniqueIdGenerator(5, -1); siteid = UniqueIdGenerator.getPartitionIdFromUniqueId(tim2.getNextUniqueId()); assertEquals(5, siteid); siteid = UniqueIdGenerator.getPartitionIdFromUniqueId(tim2.getNextUniqueId()); assertEquals(5, siteid); } public void testLastTxnId() { for (int i = 0; i < 1000; i++) { long id = tim.getNextUniqueId(); assertEquals(id, tim.getLastUniqueId()); long id2 = tim.getNextUniqueId(); assertEquals(id2, tim.getLastUniqueId()); assertTrue(id2 > id); } } public void testTimestampFromId() { long then = System.currentTimeMillis(); long tid = tim.getNextUniqueId(); long now = System.currentTimeMillis(); assertTrue(then <= UniqueIdGenerator.getDateFromUniqueId(tid).getTime()); assertTrue(now >= UniqueIdGenerator.getDateFromUniqueId(tid).getTime()); } public void testInAndOut() { long ts1 = 1267732596224L; //long ts1 = TransactionIdManager.VOLT_EPOCH; long seq1 = 2; long init1 = 6; long txnId1 = UniqueIdGenerator.makeIdFromComponents(ts1, seq1, init1); System.out.printf("%20d : %s\n", txnId1, UniqueIdGenerator.toBitString(txnId1)); assertEquals(ts1, UniqueIdGenerator.getTimestampFromUniqueId(txnId1)); assertEquals(seq1, UniqueIdGenerator.getSequenceNumberFromUniqueId(txnId1)); assertEquals(init1, UniqueIdGenerator.getPartitionIdFromUniqueId(txnId1)); long ts2 = 1267732596224L; //long ts2 = TransactionIdManager.VOLT_EPOCH + 1; long seq2 = 4; long init2 = 6; long txnId2 = UniqueIdGenerator.makeIdFromComponents(ts2, seq2, init2); System.out.printf("%20d : %s\n", txnId2, UniqueIdGenerator.toBitString(txnId2)); assertEquals(ts2, UniqueIdGenerator.getTimestampFromUniqueId(txnId2)); assertEquals(seq2, UniqueIdGenerator.getSequenceNumberFromUniqueId(txnId2)); assertEquals(init2, UniqueIdGenerator.getPartitionIdFromUniqueId(txnId2)); assertTrue(txnId2 > txnId1); System.out.printf("%d > %d\n", txnId1, txnId2); } /* * Going back less than 3 seconds exercises a different code path where we block waiting * for time to move forwards far enough to catch up. */ public void testTimeMovesBackwards() { final long goBackTime = 10000; final long goBackQuantity = 2000; UniqueIdGenerator.Clock fakeClock = new UniqueIdGenerator.Clock() { public long currentTime = 2000; public boolean haveGoneBack = false; @Override public long get() { currentTime++; if (currentTime == goBackTime && !haveGoneBack) { haveGoneBack = true; currentTime -= goBackQuantity; } return currentTime; } @Override public void sleep(long millis) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException(); } currentTime += millis; } }; tim = new UniqueIdGenerator(0, 0, fakeClock); long last = 0; for (int ii = 0; ii < 60000; ii++) { last = tim.getNextUniqueId(); } assertTrue(last > goBackTime); } /* * If the clock jumps back more then we are willing to block for * it should still generate IDs by adding an offset to the clock */ public void testTimeMovesBackwardsALot() { final long goBackTime = 400000 + 10000; final long goBackQuantity = 200000; final AtomicLong currentTime = new AtomicLong(400000); UniqueIdGenerator.Clock fakeClock = new UniqueIdGenerator.Clock() { public boolean haveGoneBack = false; @Override public long get() { currentTime.incrementAndGet(); if (currentTime.get() == goBackTime && !haveGoneBack) { haveGoneBack = true; currentTime.addAndGet(-goBackQuantity); } return currentTime.get(); } @Override public void sleep(long millis) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException(); } currentTime.addAndGet(millis); } }; tim = new UniqueIdGenerator(0, 0, fakeClock); long last = 0; for (int ii = 0; ii < 60000; ii++) { last = tim.getNextUniqueId(); } assertTrue(UniqueIdGenerator.getTimestampFromUniqueId(last) > goBackTime); /* * Check that if the clock extracts it's head from it's arse it doesn't leap forward like a madman * and removes the offset */ currentTime.set(UniqueIdGenerator.getTimestampFromUniqueId(last) + 1000); assertTrue( UniqueIdGenerator.getTimestampFromUniqueId(tim.getNextUniqueId()) < UniqueIdGenerator.getTimestampFromUniqueId(last) + 2000); } /* * If the time is coming from another master * and we transition to generating IDs, we have to be sure the IDs are * > then whatever came from the master */ public void testIsGreaterThanMaster() { UniqueIdGenerator.Clock fakeClock = new UniqueIdGenerator.Clock() { long currentTime = 2000; public boolean haveGoneBack = false; @Override public long get() { currentTime++; return currentTime; } @Override public void sleep(long millis) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException(); } currentTime += millis; } }; tim = new UniqueIdGenerator(0, 0, fakeClock); long greatestSeen = UniqueIdGenerator.makeIdFromComponents(500000, 21, 0); tim.updateMostRecentlyGeneratedUniqueId(greatestSeen); assertTrue(tim.getNextUniqueId() > greatestSeen); long last = 0; for (int ii = 0; ii < 60000; ii++) { last = tim.getNextUniqueId(); } assertTrue(last > greatestSeen); } }