/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.util;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.utils.TimeAndCounterIDGenerator;
import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet;
import org.junit.Assert;
import org.junit.Test;
public class TimeAndCounterIDGeneratorTest extends Assert {
// Constants -----------------------------------------------------
// Attributes ----------------------------------------------------
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
// Public --------------------------------------------------------
@Test
public void testCalculation() {
TimeAndCounterIDGenerator seq = new TimeAndCounterIDGenerator();
long max = 11000;
long lastNr = 0;
for (long i = 0; i < max; i++) {
long seqNr = seq.generateID();
Assert.assertTrue("The sequence generator should aways generate crescent numbers", seqNr > lastNr);
lastNr = seqNr;
}
}
@Test
public void testCalculationRefresh() {
TimeAndCounterIDGenerator seq = new TimeAndCounterIDGenerator();
long id1 = seq.generateID();
Assert.assertEquals(1, id1 & 0xffff);
Assert.assertEquals(2, seq.generateID() & 0xffff);
seq.refresh();
long id2 = seq.generateID();
Assert.assertTrue(id2 > id1);
Assert.assertEquals(1, id2 & 0xffff);
}
@Test
public void testCalculationOnMultiThread() throws Throwable {
final ConcurrentHashSet<Long> hashSet = new ConcurrentHashSet<>();
final TimeAndCounterIDGenerator seq = new TimeAndCounterIDGenerator();
System.out.println("Time = " + TimeAndCounterIDGeneratorTest.hex(System.currentTimeMillis()) + ", " + seq);
final int NUMBER_OF_THREADS = 100;
final int NUMBER_OF_IDS = 10;
final CountDownLatch latchAlign = new CountDownLatch(NUMBER_OF_THREADS);
final CountDownLatch latchStart = new CountDownLatch(1);
class T1 extends Thread {
Throwable e;
@Override
public void run() {
try {
latchAlign.countDown();
assertTrue("Latch has got to return within a minute", latchStart.await(1, TimeUnit.MINUTES));
long lastValue = 0L;
for (int i = 0; i < NUMBER_OF_IDS; i++) {
long value = seq.generateID();
Assert.assertTrue(TimeAndCounterIDGeneratorTest.hex(value) + " should be greater than " +
TimeAndCounterIDGeneratorTest.hex(lastValue) +
" on seq " +
seq.toString(), value > lastValue);
lastValue = value;
hashSet.add(value);
}
} catch (Throwable e) {
this.e = e;
}
}
}
T1[] arrays = new T1[NUMBER_OF_THREADS];
for (int i = 0; i < arrays.length; i++) {
arrays[i] = new T1();
arrays[i].start();
}
assertTrue("Latch has got to return within a minute", latchAlign.await(1, TimeUnit.MINUTES));
latchStart.countDown();
for (T1 t : arrays) {
t.join();
if (t.e != null) {
throw t.e;
}
}
Assert.assertEquals(NUMBER_OF_THREADS * NUMBER_OF_IDS, hashSet.size());
hashSet.clear();
}
@Test
public void testWrapID() throws Throwable {
TimeAndCounterIDGenerator seq = new TimeAndCounterIDGenerator();
System.out.println("Current Time = " + TimeAndCounterIDGeneratorTest.hex(System.currentTimeMillis()) + " " + seq);
seq.setInternalDate(System.currentTimeMillis() + 10000L); // 10 seconds in the future
seq.setInternalID(TimeAndCounterIDGenerator.ID_MASK); // 1 ID about to explode
try {
// This is simulating a situation where we generated more than 268 million messages on the same time interval
seq.generateID();
Assert.fail("It was supposed to throw an exception, as the counter was set to explode on this test");
} catch (Exception e) {
}
seq = new TimeAndCounterIDGenerator();
seq.setInternalDate(System.currentTimeMillis() - 10000L); // 10 seconds in the past
long timeMark = seq.getInternalTimeMark();
seq.setInternalID(TimeAndCounterIDGenerator.ID_MASK); // 1 ID about to explode
// This is ok... the time portion would be added to the next one generated 10 seconds ago
seq.generateID();
Assert.assertTrue(TimeAndCounterIDGeneratorTest.hex(timeMark) + " < " +
TimeAndCounterIDGeneratorTest.hex(seq.getInternalTimeMark()), timeMark < seq.getInternalTimeMark());
}
private static String hex(final long value) {
return String.format("%1$X", value);
}
}