IdGenerator { public long getNextId() { synchronized (syncObject) { long timestamp = timeProvider.getTimeInMillis(); if (prevTimestamp < timestamp) { sequence = 0L; prevTimestamp = timestamp; return buildKey(timestamp, workerId, sequence); } timestamp = prevTimestamp; sequence = ((sequence + 1) & sequenceBitMask); if (0L == sequence) { timestamp++; log.info("Cheating, advancing timestamp by 1ms. workerId = " + workerId); prevTimestamp = timestamp; } return buildKey(timestamp, workerId, sequence); } } IdGenerator(final long workerId);  IdGenerator(final long workerId, final TimeProvider timeProvider);  IdGenerator(final long workerId, final int numBitsWorkerId, final int numBitsSequence);  IdGenerator(final long workerId, final TimeProvider timeProvider,
                       final int numBitsWorkerId, final int numBitsSequence); long getNextId(); String getNextIdHex(); long getWorkerId();  }
@Test (timeout=500) public void testMicroBenchmark() { IdGenerator idgen = new IdGenerator(0L); int numIterations = 10000; long start = System.currentTimeMillis(); for (int i = 0; i < numIterations; ++i) { idgen.getNextId(); } long duration = System.currentTimeMillis() - start; log.info("Microbenchmark: iterations = " + numIterations + ", time = " + duration + "ms"); }
@Test public void testBackwardsClock() { int numIterations = 10000; Set<Long> idSet = new HashSet<Long>(numIterations); TimeProvider tp = new TimeProvider() { private int counter = 0; @Override public long getTimeInMillis() { if ((counter++ % 109) == 0) { return System.currentTimeMillis() - 5; } return System.currentTimeMillis(); } }; IdGenerator idgen = new IdGenerator(0L, tp); for (int i = 0; i < numIterations; ++i) { assertTrue(idSet.add(idgen.getNextId())); } assertEquals(numIterations, idSet.size()); }
@Test public void testMultiBits() { final int sequenceBits = 8; final int workerBits = 14; final IdGenerator workers[] = new IdGenerator[10]; for (int i = 0; i < workers.length; i++) { workers[i] = new IdGenerator(i * 100, workerBits, sequenceBits); } final Set<Long> ids = new HashSet<Long>(); for (long i = 0; i < (long) (Math.pow(2, sequenceBits) * 10); i++) { for (int j = 0; j < workers.length; j++) { long id = workers[j].getNextId(); if (ids.contains(id)) { fail(id + " is already generated by the IdGenerator"); } ids.add(workers[j].getNextId()); } } }
@Test public void testMultithreadGenerator() { final int numSequenceBits = 12; final int numWorkerBits = 12; final IdGenerator worker = new IdGenerator(1, numWorkerBits, numSequenceBits); final int numThreads = 10; final int numRunnables = 20; final int numLoops = (int) (Math.pow(2, numSequenceBits) * 10); final List<long[]> ids = new ArrayList<long[]>(); for (int i = 0; i < numRunnables; i++) { ids.add(new long[numLoops]); } final CountDownLatch startLatch = new CountDownLatch(numThreads); final CountDownLatch doneLatch = new CountDownLatch(numRunnables); ExecutorService executor = Executors.newFixedThreadPool(numThreads); for (int i = 0; i < numRunnables; i++) { final long[] loopData = ids.get(i); Runnable r = new Runnable() { public void run() { startLatch.countDown(); try { startLatch.await(); } catch (InterruptedException ie) { throw new RuntimeException(ie); } for (int j = 0; j < numLoops; j++) { loopData[j] = worker.getNextId(); } doneLatch.countDown(); } }; executor.execute(r); } try { doneLatch.await(); } catch (InterruptedException ie) { throw new RuntimeException(ie); } executor.shutdown(); Set<Long> allids = new HashSet<Long>(); for (int i = 0; i < ids.size(); i++) { for (Long id : ids.get(i)) { assertFalse("index [" + i + "] not set earlier (" + id.longValue() + ")", allids.contains(id.longValue())); allids.add(id.longValue()); } } }
@Test public void testCustomWorkerId() { IdGenerator worker100 = new IdGenerator(100); IdGenerator worker200 = new IdGenerator(200); assertTrue("Different worker id yields different id", worker100.getNextId() != worker200.getNextId()); }