1.0.0-rc1

- README.md: Updated documentation to reflect project scope and functionality
- pom.xml: logback bump, rem okhttp3, add javalin, brotli4j, minor beautification, version number
- logback-{docker, bare}.xml: Added additional rolling files for debug and trace
- SnowflakeService.java: Added documentation, removed orchestration, added webserver
- SnowflakeIDGenerator.java: added documentation, removed unused methods
- ConfigurationValues.java: added documentation
This commit is contained in:
2024-05-20 19:11:02 +02:00
parent 6c0882eb1a
commit 97ee513e21
7 changed files with 174 additions and 185 deletions

View File

@@ -2,12 +2,29 @@ package space.b00tload.services.snowflake;
import space.b00tload.utils.configuration.ConfigValues;
/**
* All values to be configures
* @author Alix von Schirp
* @since 1.0.0
* @version 1.0.0
*/
public enum ConfigurationValues implements ConfigValues {
/**
* Bits reserved for machine id
*/
MACHINE_ID_BITS("machinebits", "M", "MACHINE_ID_BITS", "bitcount.machine", "10"),
/**
* Bits reserved for sequence
*/
SEQUENCE_BITS("sequencebits", "S", "SEQUENCE_BITS", "bitcount.sequence", "12"),
/**
* Selected epoch
*/
EPOCH("epoch", "E", "EPOCH", "epoch", "1704067200000"),
MACHINE_ID("machineid", "I", "MACHINE_ID", "machineid", "-1"),
ORCHESTRATOR_IP("orchestrator", "O", "ORCHESTRATOR_IP", "orchestrator.ip", "http://disabled"),
/**
* this machines id
*/
MACHINE_ID("machineid", "I", "MACHINE_ID", "machineid", "0"),
;
private final String cliFlag;

View File

@@ -20,6 +20,9 @@ import static java.lang.System.currentTimeMillis;
public class SnowflakeIDGenerator {
//Singleton instance
/**
* Singleton instance
*/
private static SnowflakeIDGenerator INSTANCE;
// Constants
@@ -84,10 +87,10 @@ public class SnowflakeIDGenerator {
/**
* 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 // 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
* @param machineId The machineID used for generation // expected to be null, loaded from config
* @param epoch The snowflake epoch used for generation // expected to be null, loaded from config
* @param machineIdBits The amount of bits used for the machineID // expected to be null, loaded from config
* @param sequenceBits The amount of bits used for sequence counter // expected to be null, loaded from config
* @throws IllegalArgumentException if any bound checks fail.
*/
private SnowflakeIDGenerator(@Nullable Long machineId, @Nullable Long epoch, @Nullable Long machineIdBits, @Nullable Long sequenceBits) throws IllegalArgumentException{
@@ -129,6 +132,7 @@ public class SnowflakeIDGenerator {
* Generates a new snowflake ID.<br>
* It uses a long (64-bit signed int) made up of (in default configuration):
* <table>
* <caption>Bit layout</caption>
* <tr>
* <th>bit(s)</th>
* <th>content</th>
@@ -152,7 +156,7 @@ public class SnowflakeIDGenerator {
* </table>
* Generation will be halted
* <ul>
* <li>if the clock moves backwards (e.g. fast clock after ntp synchronization) until {@code last generated milli}<{@code current milli}</li>
* <li>if the clock moves backwards (e.g. fast clock after ntp synchronization) until {@code last generated milli}{@literal <}{@code current milli}</li>
* <li>if {@code count(generated IDs this millisecond)} is greater than {@code MAX_SEQUENCE} until next millisecond</li>
* </ul>
* @return the generated ID
@@ -186,6 +190,13 @@ public class SnowflakeIDGenerator {
// Wait for the next timestamp
/**
* Blocks thread until next timestemp (=next milli)
*
* @param currentTimestamp the current milli
* @return the awaited milli
*/
private long waitForNextTimestamp(long currentTimestamp) {
long nextTimestamp = currentTimeMillis();
while (nextTimestamp <= currentTimestamp) {
@@ -194,58 +205,19 @@ public class SnowflakeIDGenerator {
return nextTimestamp;
}
public static void init(long machineId, long epoch, long machineIdBits, long sequenceBits){
INSTANCE = new SnowflakeIDGenerator(machineId, epoch, machineIdBits, sequenceBits);
}
public static void init(){
INSTANCE = new SnowflakeIDGenerator(null, null, null, null);
}
/**
* Getter for the singleton instance. Inits singleton if needed.
* @return the singleton instance.
*/
public static SnowflakeIDGenerator getInstance() {
if(Objects.isNull(INSTANCE)) throw new UnsupportedOperationException("SnowflakeIDGenerator is not initialized.");
// if(Objects.isNull(INSTANCE)) throw new UnsupportedOperationException("SnowflakeIDGenerator is not initialized.");
if(Objects.isNull(INSTANCE)) init();
return INSTANCE;
}
public long getEPOCH() {
return EPOCH;
}
public long getMACHINE_ID_BITS() {
return MACHINE_ID_BITS;
}
public long getSEQUENCE_BITS() {
return SEQUENCE_BITS;
}
public long getMAX_MACHINE_ID() {
return MAX_MACHINE_ID;
}
public long getMAX_SEQUENCE() {
return MAX_SEQUENCE;
}
public long getMACHINE_ID_SHIFT() {
return MACHINE_ID_SHIFT;
}
public long getTIMESTAMP_SHIFT() {
return TIMESTAMP_SHIFT;
}
public long getMachineId() {
return machineId;
}
public long getTIMESTAMP_BITS() {
return TIMESTAMP_BITS;
}
public long getMAX_TIMESTAMP() {
return MAX_TIMESTAMP;
}
}

View File

@@ -4,31 +4,45 @@ import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter2;
import okhttp3.*;
import com.google.gson.JsonObject;
import io.javalin.Javalin;
import io.javalin.http.ContentType;
import io.javalin.http.HandlerType;
import io.javalin.router.Endpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import space.b00tload.utils.configuration.Configuration;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.Random;
/**
* Main class orchestrating between generator, webserver and config
*
* @author Alix von Schirp
* @version 1.0.0
* @since 1.0.0
*/
public class SnowflakeService {
/**
* This software's version
*/
private static String SOFTWARE_VERSION;
/**
* Base dir for config and tmp data
*/
private static String APPLICATION_BASE;
private static String USER_AGENT;
private static Logger LOGGER;
private static String INSTANCE_NAME;
/**
* Initializes tool, generator and webserver.
* @param args cli args
*/
public static void main(String[] args) {
//Set up constants
SOFTWARE_VERSION = Objects.requireNonNullElse(SnowflakeService.class.getPackage().getImplementationVersion(), "0.0.1-indev");
USER_AGENT = "SnowflakeService " + SOFTWARE_VERSION + "(" + System.getProperty("os.name") + "; " + System.getProperty("os.arch") + ") Java/" + System.getProperty("java.version");
APPLICATION_BASE = List.of(args).contains("--docker") ? Paths.get("data", "b00tload-tools", "snowflake").toString() : Paths.get(System.getProperty("user.home"), ".b00tload-tools", "snowflake").toString();
INSTANCE_NAME = getHostname() + "_" + getPid() + "_" + new Random().nextInt(9999);
//Set up logger
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
@@ -45,52 +59,25 @@ public class SnowflakeService {
(new StatusPrinter2()).printInCaseOfErrorsOrWarnings(loggerContext);
//Set up logger
LOGGER = LoggerFactory.getLogger(SnowflakeService.class);
Logger LOGGER = LoggerFactory.getLogger(SnowflakeService.class);
//Init config
Configuration.init(args, SOFTWARE_VERSION, APPLICATION_BASE, ConfigurationValues.values());
long machineID;
long sequenceBits;
long machineBits;
long epoch;
if(!Configuration.getInstance().get(ConfigurationValues.ORCHESTRATOR_IP).equals(ConfigurationValues.ORCHESTRATOR_IP.getDefaultValue())){
OkHttpClient httpClient = new OkHttpClient();
try {
try (Response r = httpClient.newCall(
new Request.Builder()
.url(Configuration.getInstance().get(ConfigurationValues.ORCHESTRATOR_IP))
.post(
new FormBody.Builder().addEncoded("name", INSTANCE_NAME).build()
)
.build()
).execute()){
if(r.code() == 200){
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
} else if((Long.parseLong(Configuration.getInstance().get(ConfigurationValues.MACHINE_ID))) != -1L) {
} else {
}
//Init webserver
Javalin endpointServer = Javalin.create(config -> {
config.http.brotliAndGzipCompression();
config.http.prefer405over404 = true;
config.requestLogger.http((ctx, executionTimeMs) -> {
LoggerFactory.getLogger(config.getClass()).info("{} served in {}ms to {}(UA: \"{}\")", ctx.fullUrl(), executionTimeMs, ctx.req().getRemoteAddr(), ctx.userAgent());
});
});
endpointServer.addEndpoint(new Endpoint(HandlerType.GET, "generate", ctx -> {
JsonObject ret = new JsonObject();
ret.addProperty("id", SnowflakeIDGenerator.getInstance().generateID());
ctx.status(200).result(ret.toString()).contentType(ContentType.APPLICATION_JSON);
}));
endpointServer.start(95674);
}
private static String getHostname(){
String hostname = System.getenv("COMPUTERNAME"); // On Windows
if (hostname == null || hostname.isEmpty()) {
hostname = System.getenv("HOSTNAME"); // On Unix/Linux
}
return hostname;
}
private static long getPid(){
return ProcessHandle.current().pid();
}
}

View File

@@ -14,6 +14,9 @@
<level>ERROR</level>
<onMatch>DENY</onMatch>
</filter>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
<appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
@@ -26,16 +29,45 @@
<appender name="RFAOUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/snowflake.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_ARCHIVE}/rollingfile.log%d{yyy-MM-dd}.lol.gz</fileNamePattern>
<fileNamePattern>${LOG_ARCHIVE}/snowflake.rolling.%d{yyy-MM-dd}.log.gz</fileNamePattern>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<appender name="debugOUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/snowflake.debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_ARCHIVE}/snowflake.debug.rolling.%d{yyy-MM-dd}.log.gz</fileNamePattern>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<appender name="traceOUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/snowflake.trace.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_ARCHIVE}/snowflake.trace.rolling.%d{yyy-MM-dd}.log.gz</fileNamePattern>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>TRACE</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<root level="trace">
<appender-ref ref="STDOUT" />
<appender-ref ref="STDERR" />
<appender-ref ref="RFAOUT" />
<appender-ref ref="debugOUT" />
<appender-ref ref="traceOUT" />
</root>
</configuration>

View File

@@ -14,6 +14,9 @@
<level>ERROR</level>
<onMatch>DENY</onMatch>
</filter>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
<appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
@@ -26,16 +29,45 @@
<appender name="RFAOUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/snowflake.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_ARCHIVE}/rollingfile.log%d{yyy-MM-dd}.lol.gz</fileNamePattern>
<fileNamePattern>${LOG_ARCHIVE}/snowflake.rolling.%d{yyy-MM-dd}.log.gz</fileNamePattern>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<appender name="debugOUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/snowflake.debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_ARCHIVE}/snowflake.debug.rolling.%d{yyy-MM-dd}.log.gz</fileNamePattern>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<appender name="traceOUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/snowflake.trace.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_ARCHIVE}/snowflake.trace.rolling.%d{yyy-MM-dd}.log.gz</fileNamePattern>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>TRACE</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<root level="trace">
<appender-ref ref="STDOUT" />
<appender-ref ref="STDERR" />
<appender-ref ref="RFAOUT" />
<appender-ref ref="debugOUT" />
<appender-ref ref="traceOUT" />
</root>
</configuration>