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 | + *
|---|---|
| 0 | + *0 (signed positive) | + *
| 1-41 | + *timestamp (current unix time - epoch) |
+ *
| 42-51 | + *machineID | + *
| 52-63 | + *sequence | + *