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

com.fluxtion.agrona.concurrent.SnowflakeIdGenerator Maven / Gradle / Ivy

/*
 * Copyright 2014-2024 Real Logic Limited.
 *
 * Licensed 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
 *
 * https://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 com.fluxtion.agrona.concurrent;

import com.fluxtion.agrona.hints.ThreadHints;

import java.util.concurrent.atomic.AtomicLongFieldUpdater;

abstract class AbstractSnowflakeIdGeneratorPaddingLhs
{
    byte p000, p001, p002, p003, p004, p005, p006, p007, p008, p009, p010, p011, p012, p013, p014, p015;
    byte p016, p017, p018, p019, p020, p021, p022, p023, p024, p025, p026, p027, p028, p029, p030, p031;
    byte p032, p033, p034, p035, p036, p037, p038, p039, p040, p041, p042, p043, p044, p045, p046, p047;
    byte p048, p049, p050, p051, p052, p053, p054, p055, p056, p057, p058, p059, p060, p061, p062, p063;
}

abstract class AbstractSnowflakeIdGeneratorValue extends AbstractSnowflakeIdGeneratorPaddingLhs
{
    static final AtomicLongFieldUpdater TIMESTAMP_SEQUENCE_UPDATER =
        AtomicLongFieldUpdater.newUpdater(AbstractSnowflakeIdGeneratorValue.class, "timestampSequence");

    volatile long timestampSequence;
}

abstract class AbstractSnowflakeIdGeneratorPaddingRhs extends AbstractSnowflakeIdGeneratorValue
{
    byte p064, p065, p066, p067, p068, p069, p070, p071, p072, p073, p074, p075, p076, p077, p078, p079;
    byte p080, p081, p082, p083, p084, p085, p086, p087, p088, p089, p090, p091, p092, p093, p094, p095;
    byte p096, p097, p098, p099, p100, p101, p102, p103, p104, p105, p106, p107, p108, p109, p110, p111;
    byte p112, p113, p114, p115, p116, p117, p118, p119, p120, p121, p122, p123, p124, p125, p126, p127;
}

/**
 * Generate unique identifiers based on the Twitter
 * Snowflake algorithm.
 * 

* This implementation is lock-less resulting in greater throughput plus less contention and latency jitter. *

* Note: ntpd, or alternative clock source, should be setup correctly to ensure the clock does not go backwards. */ public final class SnowflakeIdGenerator extends AbstractSnowflakeIdGeneratorPaddingRhs implements IdGenerator { /** * High order 2's compliment bit which is unused. */ public static final int UNUSED_BITS = 1; /** * Number of bits used for the timestamp giving 69 years from {@link #timestampOffsetMs()}. */ public static final int EPOCH_BITS = 41; /** * Total number of bits used to represent the distributed node and the sequence within a millisecond. */ public static final int MAX_NODE_ID_AND_SEQUENCE_BITS = 22; /** * Default number of bits used to represent the distributed node or application which is {@code 10} bits allowing * for 1024 nodes (0-1023). */ public static final int NODE_ID_BITS_DEFAULT = 10; /** * Default number of bits used to represent the sequence within a millisecond which is {@code 12} bits supporting * 4,096,000 ids per-second per-node. */ public static final int SEQUENCE_BITS_DEFAULT = 12; private final int nodeIdAndSequenceBits; private final int sequenceBits; private final long maxNodeId; private final long maxSequence; private final long nodeBits; private final long timestampOffsetMs; private final EpochClock clock; /** * Construct a new Snowflake id generator for a given node with a provided offset and {@link EpochClock}. * * @param nodeIdBits number of bits used to represent the distributed node or application. * @param sequenceBits number of bits used to represent the sequence within a millisecond. * @param nodeId for the node generating ids. * @param timestampOffsetMs to adjust the base offset from 1 Jan 1970 UTC to extend the 69-year range. * @param clock to provide timestamps. */ public SnowflakeIdGenerator( final int nodeIdBits, final int sequenceBits, final long nodeId, final long timestampOffsetMs, final EpochClock clock) { if (nodeIdBits < 0) { throw new IllegalArgumentException("must be >= 0: nodeIdBits=" + nodeIdBits); } if (sequenceBits < 0) { throw new IllegalArgumentException("must be >= 0: sequenceBits=" + sequenceBits); } final int nodeIdAndSequenceBits = (nodeIdBits + sequenceBits); if (nodeIdAndSequenceBits > MAX_NODE_ID_AND_SEQUENCE_BITS) { throw new IllegalArgumentException("too many bits used:" + " nodeIdBits=" + nodeIdBits + " + sequenceBits=" + sequenceBits + " > " + MAX_NODE_ID_AND_SEQUENCE_BITS); } final long maxNodeId = (long)(Math.pow(2, nodeIdBits) - 1); if (nodeId < 0 || nodeId > maxNodeId) { throw new IllegalArgumentException("must be >= 0 && <= " + maxNodeId + ": nodeId=" + nodeId); } if (timestampOffsetMs < 0) { throw new IllegalArgumentException("must be >= 0: timestampOffsetMs=" + timestampOffsetMs); } final long nowMs = clock.time(); if (timestampOffsetMs > nowMs) { throw new IllegalArgumentException("timestampOffsetMs=" + timestampOffsetMs + " > nowMs=" + nowMs); } this.nodeIdAndSequenceBits = nodeIdAndSequenceBits; this.maxNodeId = maxNodeId; this.sequenceBits = sequenceBits; this.maxSequence = (long)(Math.pow(2, sequenceBits) - 1); this.nodeBits = nodeId << sequenceBits; this.timestampOffsetMs = timestampOffsetMs; this.clock = clock; } /** * Construct a new Snowflake id generator for a given node with a 0 offset from 1 Jan 1970 UTC and use * {@link SystemEpochClock#INSTANCE} with {@link #NODE_ID_BITS_DEFAULT} node ID bits and * {@link #SEQUENCE_BITS_DEFAULT} sequence bits. * * @param nodeId for the node generating ids. */ public SnowflakeIdGenerator(final long nodeId) { this(NODE_ID_BITS_DEFAULT, SEQUENCE_BITS_DEFAULT, nodeId, 0, SystemEpochClock.INSTANCE); } /** * Node identity which scopes the id generation. This is limited to {@link #maxNodeId()}. * * @return the node identity which scopes the id generation. */ public long nodeId() { return nodeBits >>> sequenceBits; } /** * Offset in milliseconds from Epoch of 1 Jan 1970 UTC which is subtracted to give 69 years of ids. *

* To offset from 1 Jan 2015 UTC then 1420070400000 can be used. * * @return offset in milliseconds from Epoch of 1 Jan 1970 UTC which is subtracted to give 69 years of ids. */ public long timestampOffsetMs() { return timestampOffsetMs; } /** * The max node identity value possible given the configured number of node ID bits. * * @return max node identity value. */ public long maxNodeId() { return maxNodeId; } /** * The max sequence value possible given the configured number of sequence bits. * * @return max sequence value. */ public long maxSequence() { return maxSequence; } /** * Generate the next id in sequence. If {@link #maxSequence()} is reached within the same millisecond then this * implementation will busy spin until the next millisecond using {@link ThreadHints#onSpinWait()} and checking * for {@link Thread#isInterrupted()}. * * @return the next unique id for this node. */ public long nextId() { while (true) { final long oldTimestampSequence = timestampSequence; final long timestampMs = clock.time() - timestampOffsetMs; final long oldTimestampMs = oldTimestampSequence >>> nodeIdAndSequenceBits; if (timestampMs > oldTimestampMs) { final long newTimestampSequence = timestampMs << nodeIdAndSequenceBits; if (TIMESTAMP_SEQUENCE_UPDATER.compareAndSet(this, oldTimestampSequence, newTimestampSequence)) { return newTimestampSequence | nodeBits; } } else { final long oldSequence = oldTimestampSequence & maxSequence; if (oldSequence < maxSequence) { final long newTimestampSequence = oldTimestampSequence + 1; if (TIMESTAMP_SEQUENCE_UPDATER.compareAndSet(this, oldTimestampSequence, newTimestampSequence)) { return newTimestampSequence | nodeBits; } } } if (Thread.currentThread().isInterrupted()) { throw new IllegalStateException("unexpected thread interrupt"); } ThreadHints.onSpinWait(); } } long extractTimestamp(final long id) { return id >>> nodeIdAndSequenceBits; } long extractNodeId(final long id) { return (id >>> sequenceBits) & maxNodeId; } long extractSequence(final long id) { return id & maxSequence; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy