-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exclude reserved IDs - how? #35
Comments
Thank you for the suggestion! The scenario you described is indeed valid, and I understand the need for certain IDs to be reserved. However, implementing built-in support for "avoid-these-values" in the TSID generation process would introduce additional complexity to the library. For your use case, a good solution would be to implement a custom wrapper around the TSID generator. This approach keeps the core library lean and allows you to tailor the behavior to your specific needs. For example: package com.example;
import java.util.Arrays;
import java.util.List;
// import com.github.f4b6a3.tsid.Tsid;
import com.github.f4b6a3.tsid.TsidCreator;
public class CustomTsidCreator {
// Add other reserved values if needed
private static final Tsid NIL = Tsid.from(0x0000000000000000L);
private static final List<Tsid> RESERVED_VALUES = Arrays.asList(NIL);
public static Tsid getTsidExcludingReserved() {
Tsid tsid;
do {
// tsid = Tsid.fast();
tsid = TsidCreator.getTsid();
} while (RESERVED_VALUES.contains(tsid));
return tsid;
}
public static void main(String[] args) {
System.out.println(CustomTsidCreator.getTsidExcludingReserved());
}
} |
Thanks, I already came up with something similar - so ... maybe just add something in the FAQ? Feel free to close this issue otherwise. public record TsidGenerator(long excludeStart, long excludeEnd, Supplier<Tsid> generator) {
public TsidGenerator {
Objects.requireNonNull(generator);
if(excludeStart > excludeEnd)
throw new IllegalArgumentException("start:"+excludeStart+" should be less than end:"+excludeEnd);
}
static public TsidGenerator fast(long excludeStart, long excludeEnd) {
return new TsidGenerator(excludeStart, excludeEnd, Tsid::fast);
}
public Tsid generate() {
Tsid tsid;
long tsidLong;
do {
tsid = generator.get();
tsidLong = tsid.toLong();
} while(tsidLong >= excludeStart && tsidLong <= excludeEnd);
return tsid;
}
} |
Very good example! I also came up with a solution using a factory builder, but it's very limited to timestamp. TsidFactory factory = TsidFactory.builder().withTimeFunction(() -> {
final long startTime = Tsid.TSID_EPOCH + 1;
long time = System.currentTimeMillis();
if (time < startTime) time = startTime;
return time;
}).build(); The function above would work similarly to this PostgreSQL's sequence creation command: CREATE SEQUENCE my_sequence START WITH 1; Your Another solution I came up with is to add a builder method to include custom behavior. But this adds more complexity to the library. Here is the diff: diff --git a/src/main/java/com/github/f4b6a3/tsid/TsidFactory.java b/src/main/java/com/github/f4b6a3/tsid/TsidFactory.java
index f8bd2af..0166b0a 100644
--- a/src/main/java/com/github/f4b6a3/tsid/TsidFactory.java
+++ b/src/main/java/com/github/f4b6a3/tsid/TsidFactory.java
@@ -34,6 +34,7 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntFunction;
import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
+import java.util.function.LongUnaryOperator;
/**
* A factory that actually generates Time-Sorted Unique Identifiers (TSID).
@@ -81,6 +82,7 @@ public final class TsidFactory {
private final long customEpoch;
private final LongSupplier timeFunction;
+ private final LongUnaryOperator afterFunction;
private final IRandom random;
private final int randomBytes;
@@ -139,6 +141,7 @@ public final class TsidFactory {
this.nodeBits = builder.getNodeBits();
this.random = builder.getRandom();
this.timeFunction = builder.getTimeFunction();
+ this.afterFunction = builder.getAfterFunction();
// setup constants that depend on node bits
this.counterBits = RANDOM_BITS - nodeBits;
@@ -232,8 +235,8 @@ public final class TsidFactory {
final long _time = getTime() << RANDOM_BITS;
final long _node = (long) this.node << this.counterBits;
final long _counter = (long) this.counter & this.counterMask;
-
- return new Tsid(_time | _node | _counter);
+
+ return new Tsid(this.afterFunction.applyAsLong(_time | _node | _counter));
} finally {
lock.unlock();
}
@@ -329,6 +332,19 @@ public final class TsidFactory {
private Long customEpoch;
private IRandom random;
private LongSupplier timeFunction;
+ private LongUnaryOperator afterFunction;
+
+ public Builder withAfterFunction(LongUnaryOperator afterFunction) {
+ this.afterFunction = afterFunction;
+ return this;
+ }
+
+ protected LongUnaryOperator getAfterFunction() {
+ if (this.afterFunction == null) {
+ this.afterFunction = (tsid) -> tsid;
+ }
+ return this.afterFunction;
+ }
/**
* Set the node identifier. I'll let for a while until I put something in the FAQ or README about it. |
Thanks @fabiolimace - I think you have enough material to put something in the README or FAQ, or change this - otherwise very awesome library! |
Thanks for this well-design library!
There are use cases where the database system need to be bootstrapped with a few "well known" ids, while the rest of the data entities can be generated tsid as usual. (Example: you are creating an user database where the "admin" user must be with id=0, all other users can be get their IDs with
Tsid::fast
, orTsidCreator::getTsid
, but should never receive 0)Is there an easy way to generating tsids by excluding certain reserved values? I can see a way how to do that with some sort of skipping factory, but was hoping for something built-in, a "creator" that takes a list of "avoid-these-values" argument..
The text was updated successfully, but these errors were encountered: