diff --git a/src/main/java/space/b00tload/services/snowflake/SnowflakeIDGenerator.java b/src/main/java/space/b00tload/services/snowflake/SnowflakeIDGenerator.java index cdbcdb7..7627b06 100644 --- a/src/main/java/space/b00tload/services/snowflake/SnowflakeIDGenerator.java +++ b/src/main/java/space/b00tload/services/snowflake/SnowflakeIDGenerator.java @@ -9,8 +9,9 @@ import java.util.Objects; import static java.lang.System.currentTimeMillis; /** - * Generates the SnowflakeIDs. Must be initialized with a machine ID between 0 and 1023. - * Can generate up to 4096 IDs per millisecond. If this limit is hit, generation will be halted until the next millisecond. + * Generates the SnowflakeIDs. Must be initialized with a machine ID between 0 and 1023.
+ * Can generate up to 4096 IDs per millisecond. If this limit is hit, generation will be halted until the next millisecond.
+ * Snowflakes always have a max size of 63 bits and can therefore be saved as a {@code long} (64-bit integer). * * @author Alix von Schirp * @since 1.0.0 @@ -37,7 +38,11 @@ public class SnowflakeIDGenerator { * Default is 12. */ private final long SEQUENCE_BITS; - + /** + * Amount of bits reserved for timestamp. + * Default is 41. + */ + private final long TIMESTAMP_BITS; /** * The calculated maximum machine ID. * Defaults to 1023. @@ -48,7 +53,11 @@ public class SnowflakeIDGenerator { * Defaults to 4095. */ private final long MAX_SEQUENCE; - + /** + * The calculated maximum timestamp. + * Defaults to 2199023255551, which gives roughly 69 years. + */ + private final long MAX_TIMESTAMP; /** * How far to shift the machine ID (timestamp + machine + sequence) */ @@ -72,17 +81,16 @@ public class SnowflakeIDGenerator { */ private long lastTimestamp = -1L; - // Constructor - /** + * Used for initiating constants and state and performing bound checks on {@code EPOCH}, {@code MAX_TIMESTAMP} and {@code machineId}. * - * - * @param machineId The machineID used for generation - * @param epoch The snowflake epoch used for generation - * @param machineIdBits The amount of bits used for the machineID - * @param sequenceBits The amount of bits used for sequence counter + * @param machineId The machineID used for generation // expected to be null if not initialized by orchestrator, loaded from config + * @param epoch The snowflake epoch used for generation // expected to be null if not initialized by orchestrator, loaded from config + * @param machineIdBits The amount of bits used for the machineID // expected to be null if not initialized by orchestrator, loaded from config + * @param sequenceBits The amount of bits used for sequence counter // expected to be null if not initialized by orchestrator, loaded from config + * @throws IllegalArgumentException if any bound checks fail. */ - private SnowflakeIDGenerator(long machineId, @Nullable Long epoch, @Nullable Long machineIdBits, @Nullable Long sequenceBits) { + private SnowflakeIDGenerator(@Nullable Long machineId, @Nullable Long epoch, @Nullable Long machineIdBits, @Nullable Long sequenceBits) throws IllegalArgumentException{ //init constants MACHINE_ID_BITS = Objects.requireNonNullElse(machineIdBits, Long.parseLong(Configuration.getInstance().get(ConfigurationValues.MACHINE_ID_BITS))); @@ -92,23 +100,63 @@ public class SnowflakeIDGenerator { Long.parseLong(Configuration.getInstance().get(ConfigurationValues.SEQUENCE_BITS))); MAX_SEQUENCE = (1L << SEQUENCE_BITS) - 1; + TIMESTAMP_BITS = 63L - MACHINE_ID_BITS - SEQUENCE_BITS; + MAX_TIMESTAMP = (1L << TIMESTAMP_BITS) -1; + EPOCH = Objects.requireNonNullElse(epoch, Long.parseLong(Configuration.getInstance().get(ConfigurationValues.EPOCH))); MACHINE_ID_SHIFT = SEQUENCE_BITS; TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS; + //init state + this.machineId = Objects.requireNonNullElse(machineId, + Long.parseLong(Configuration.getInstance().get(ConfigurationValues.MACHINE_ID))); + + //Bound checks if(EPOCH > currentTimeMillis()){ throw new IllegalArgumentException("Epoch may not be in the future."); } - - if (machineId < 0 || machineId > MAX_MACHINE_ID) { + if(EPOCH+MAX_TIMESTAMP <= currentTimeMillis()){ + throw new IllegalArgumentException("Latest possible timestamp may not be in the past."); + } + if (this.machineId < 0 || this.machineId > MAX_MACHINE_ID) { throw new IllegalArgumentException("Machine ID must be between 0 and " + MAX_MACHINE_ID); } - this.machineId = machineId; } - // Method to generate a new ID + /** + * Generates a new snowflake ID.
+ * It uses a long (64-bit signed int) made up of (in default configuration): + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
bit(s)content
00 (signed positive)
1-41timestamp (current unix time - epoch)
42-51machineID
52-63sequence
+ * Generation will be halted + * + * @return the generated ID + */ public synchronized long generateID() { long currentTimestamp = currentTimeMillis(); @@ -130,7 +178,7 @@ public class SnowflakeIDGenerator { lastTimestamp = currentTimestamp; - return ((currentTimestamp - EPOCH) << TIMESTAMP_SHIFT) + return ((currentTimestamp - EPOCH) << TIMESTAMP_SHIFT) | (machineId << MACHINE_ID_SHIFT) | sequence; } @@ -150,8 +198,8 @@ public class SnowflakeIDGenerator { INSTANCE = new SnowflakeIDGenerator(machineId, epoch, machineIdBits, sequenceBits); } - public static void init(long machineId){ - INSTANCE = new SnowflakeIDGenerator(machineId, null, null, null); + public static void init(){ + INSTANCE = new SnowflakeIDGenerator(null, null, null, null); } public static SnowflakeIDGenerator getInstance() { @@ -190,6 +238,14 @@ public class SnowflakeIDGenerator { public long getMachineId() { return machineId; } + + public long getTIMESTAMP_BITS() { + return TIMESTAMP_BITS; + } + + public long getMAX_TIMESTAMP() { + return MAX_TIMESTAMP; + } }