All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.activemq.artemis.utils.UUIDTimer Maven / Gradle / Ivy

There is a newer version: 2.38.0
Show newest version
/*
 * 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.utils;

import java.util.Random;

/**
 * UUIDTimer produces the time stamps required for time-based UUIDs. It works as
 * outlined in the UUID specification, with following implementation:
 * 
    *
  • Java classes can only product time stamps with maximum resolution of one * millisecond (at least before JDK 1.5). To compensate, an additional counter * is used, so that more than one UUID can be generated between java clock * updates. Counter may be used to generate up to 10000 UUIDs for each distinct * java clock value. *
  • Due to even lower clock resolution on some platforms (older Windows * versions use 55 msec resolution), timestamp value can also advanced ahead of * physical value within limits (by default, up 100 millisecond ahead of * reported), iff necessary (ie. 10000 instances created before clock time * advances). *
  • As an additional precaution, counter is initialized not to 0 but to a * random 8-bit number, and each time clock changes, lowest 8-bits of counter * are preserved. The purpose it to make likelihood of multi-JVM multi-instance * generators to collide, without significantly reducing max. UUID generation * speed. Note though that using more than one generator (from separate JVMs) is * strongly discouraged, so hopefully this enhancement isn't needed. This 8-bit * offset has to be reduced from total max. UUID count to preserve ordering * property of UUIDs (ie. one can see which UUID was generated first for given * UUID generator); the resulting 9500 UUIDs isn't much different from the * optimal choice. *
  • Finally, as of version 2.0 and onwards, optional external timestamp * synchronization can be done. This is done similar to the way UUID * specification suggests; except that since there is no way to lock the whole * system, file-based locking is used. This works between multiple JVMs and Jug * instances. *
*

* Some additional assumptions about calculating the timestamp: *

    *
  • System.currentTimeMillis() is assumed to give time offset in UTC, or at * least close enough thing to get correct timestamps. The alternate route would * have to go through calendar object, use TimeZone offset to get to UTC, and * then modify. Using currentTimeMillis should be much faster to allow rapid * UUID creation. *
  • Similarly, the constant used for time offset between 1.1.1970 and start * of Gregorian calendar is assumed to be correct (which seems to be the case * when testing with Java calendars). *
*

* Note about synchronization: this class is assumed to always be called from a * synchronized context (caller locks on either this object, or a similar timer * lock), and so has no method synchronization. */ public class UUIDTimer { /** * Since System.longTimeMillis() returns time from january 1st 1970, and * UUIDs need time from the beginning of gregorian calendar (15-oct-1582), * need to apply the offset: */ private static final long kClockOffset = 0x01b21dd213814000L; /** * Also, instead of getting time in units of 100nsecs, we get something with * max resolution of 1 msec... and need the multiplier as well */ private static final long kClockMultiplier = 10000; private static final long kClockMultiplierL = 10000L; /** * Let's allow "virtual" system time to advance at most 100 milliseconds * beyond actual physical system time, before adding delays. */ private static final long kMaxClockAdvance = 100L; // // // Configuration private final Random mRnd; // // // Clock state: /** * Additional state information used to protect against anomalous cases * (clock time going backwards, node id getting mixed up). Third byte is * actually used for seeding counter on counter overflow. */ private final byte[] mClockSequence = new byte[3]; /** * Last physical timestamp value System.currentTimeMillis() * returned: used to catch (and report) cases where system clock goes * backwards. Is also used to limit "drifting", that is, amount timestamps * used can differ from the system time value. This value is not guaranteed * to be monotonically increasing. */ private long mLastSystemTimestamp = 0L; /** * Timestamp value last used for generating a UUID (along with * {@link #mClockCounter}. Usually the same as {@link #mLastSystemTimestamp}, * but not always (system clock moved backwards). Note that this value is * guaranteed to be monotonically increasing; that is, at given absolute time * points t1 and t2 (where t2 is after t1), t1 <= t2 will always hold true. */ private long mLastUsedTimestamp = 0L; /** * Counter used to compensate inadequate resolution of JDK system timer. */ private int mClockCounter = 0; UUIDTimer(final Random rnd) { mRnd = rnd; initCounters(rnd); mLastSystemTimestamp = 0L; // This may get overwritten by the synchronizer mLastUsedTimestamp = 0L; } private void initCounters(final Random rnd) { /* * Let's generate the clock sequence field now; as with counter, this * reduces likelihood of collisions (as explained in UUID specs) */ rnd.nextBytes(mClockSequence); /* * Ok, let's also initialize the counter... Counter is used to make it * slightly less likely that two instances of UUIDGenerator (from separate * JVMs as no more than one can be created in one JVM) would produce * colliding time-based UUIDs. The practice of using multiple generators, * is strongly discouraged, of course, but just in case... */ mClockCounter = mClockSequence[2] & 0xFF; } public void getTimestamp(final byte[] uuidData) { // First the clock sequence: uuidData[UUID.INDEX_CLOCK_SEQUENCE] = mClockSequence[0]; uuidData[UUID.INDEX_CLOCK_SEQUENCE + 1] = mClockSequence[1]; long systime = System.currentTimeMillis(); /* * Let's first verify that the system time is not going backwards; * independent of whether we can use it: */ if (systime < mLastSystemTimestamp) { // Logger.logWarning("System time going backwards! (got value // "+systime+", last "+mLastSystemTimestamp); // Let's write it down, still mLastSystemTimestamp = systime; } /* * But even without it going backwards, it may be less than the last one * used (when generating UUIDs fast with coarse clock resolution; or if * clock has gone backwards over reboot etc). */ if (systime <= mLastUsedTimestamp) { /* * Can we just use the last time stamp (ok if the counter hasn't hit * max yet) */ if (mClockCounter < UUIDTimer.kClockMultiplier) { // yup, still have room systime = mLastUsedTimestamp; } else { // nope, have to roll over to next value and maybe wait long actDiff = mLastUsedTimestamp - systime; long origTime = systime; systime = mLastUsedTimestamp + 1L; // Logger.logWarning("Timestamp over-run: need to reinitialize // random sequence"); /* * Clock counter is now at exactly the multiplier; no use just * anding its value. So, we better get some random numbers * instead... */ initCounters(mRnd); /* * But do we also need to slow down? (to try to keep virtual time * close to physical time; ie. either catch up when system clock has * been moved backwards, or when coarse clock resolution has forced * us to advance virtual timer too far) */ if (actDiff >= UUIDTimer.kMaxClockAdvance) { UUIDTimer.slowDown(origTime, actDiff); } } } else { /* * Clock has advanced normally; just need to make sure counter is reset * to a low value (need not be 0; good to leave a small residual to * further decrease collisions) */ mClockCounter &= 0xFF; } mLastUsedTimestamp = systime; /* * Now, let's translate the timestamp to one UUID needs, 100ns unit offset * from the beginning of Gregorian calendar... */ systime *= UUIDTimer.kClockMultiplierL; systime += UUIDTimer.kClockOffset; // Plus add the clock counter: systime += mClockCounter; // and then increase ++mClockCounter; /* * Time fields are nicely split across the UUID, so can't just linearly * dump the stamp: */ int clockHi = (int) (systime >>> 32); int clockLo = (int) systime; uuidData[UUID.INDEX_CLOCK_HI] = (byte) (clockHi >>> 24); uuidData[UUID.INDEX_CLOCK_HI + 1] = (byte) (clockHi >>> 16); uuidData[UUID.INDEX_CLOCK_MID] = (byte) (clockHi >>> 8); uuidData[UUID.INDEX_CLOCK_MID + 1] = (byte) clockHi; uuidData[UUID.INDEX_CLOCK_LO] = (byte) (clockLo >>> 24); uuidData[UUID.INDEX_CLOCK_LO + 1] = (byte) (clockLo >>> 16); uuidData[UUID.INDEX_CLOCK_LO + 2] = (byte) (clockLo >>> 8); uuidData[UUID.INDEX_CLOCK_LO + 3] = (byte) clockLo; } private static final int MAX_WAIT_COUNT = 50; /** * Simple utility method to use to wait for couple of milliseconds, to let * system clock hopefully advance closer to the virtual timestamps used. * Delay is kept to just a millisecond or two, to prevent excessive blocking; * but that should be enough to eventually synchronize physical clock with * virtual clock values used for UUIDs. */ private static void slowDown(final long startTime, final long actDiff) { /* * First, let's determine how long we'd like to wait. This is based on how * far ahead are we as of now. */ long ratio = actDiff / UUIDTimer.kMaxClockAdvance; long delay; if (ratio < 2L) { // 200 msecs or less delay = 1L; } else if (ratio < 10L) { // 1 second or less delay = 2L; } else if (ratio < 600L) { // 1 minute or less delay = 3L; } else { delay = 5L; } // Logger.logWarning("Need to wait for "+delay+" milliseconds; virtual // clock advanced too far in the future"); long waitUntil = startTime + delay; int counter = 0; do { try { Thread.sleep(delay); } catch (InterruptedException ie) { } delay = 1L; /* * This is just a sanity check: don't want an "infinite" loop if clock * happened to be moved backwards by, say, an hour... */ if (++counter > UUIDTimer.MAX_WAIT_COUNT) { break; } } while (System.currentTimeMillis() < waitUntil); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy