Most ID libraries solve one problem well:
uniqueness
But in distributed systems, that is often not enough.
Sometimes you also want an identifier to help with:
- - ordering
- - tracing
- - debugging
- - understanding where it came from
That is why I built EUID, a Java library for generating sortable, decodable, topology-aware UUID v8-style identifiers.
With v0.2.0, I made the biggest change to the project so far: I split generation into two strategies instead of forcing one implementation to handle every workload.
Why EUID exists
Traditional UUIDs are great when you want opaque, globally unique values.
But they do not help much when your system also cares about:
- time ordering
- infrastructure visibility
- decoding metadata from an ID
- understanding distributed generation behavior
EUID is designed for those cases.
It uses a structured 128-bit layout with:
- timestamp
- region
- shard
- node
- sequence
So the ID is not just unique — it can also be decoded and understood.
What changed in v0.2.0
The biggest change in v0.2.0 was the generator model.
Instead of one generation strategy, EUID now provides two:
FastEuidGenerator
Optimized for:
- single-threaded use
- thread-confined use
- minimal overhead
ConcurrentEuidGenerator
Optimized for:
- shared concurrent use
- multi-threaded access to the same generator
- better scalability under contention
This made the library much more honest.
Different workloads do not always need the same generator behavior, so the API now reflects that.
API example
Fast generator
EuidGenerator generator = EuidGenerators.fast(1, 1, 1);
Concurrent generator
EuidGenerator generator = EuidGenerators.concurrent(1, 1, 1);
Concurrent generator with custom block size
EuidGenerator generator = EuidGenerators.concurrent(1, 1, 1, 2048);
Decode support
One of the key goals of EUID is that IDs should be useful to inspect.
UUID id = EuidGenerators.fast(2, 3, 4).generate();
DecodedEuid decoded = EuidDecoder.decode(id);
System.out.println(decoded.getInstant());
System.out.println(decoded.getRegion());
System.out.println(decoded.getShard());
System.out.println(decoded.getNode());
System.out.println(decoded.getSequence());
That is useful for:
- debugging distributed systems
- tracing event origins
- validating routing or sharding behavior
Base58 support
EUID also supports Base58 encoding for more compact external representation.
UUID id = EuidGenerators.fast(1, 1, 1).generate();
String encoded = EuidBase58Codec.encode(id);
UUID decoded = EuidBase58Codec.decode(encoded);
This is useful when you want:
- shorter IDs
- better readability
- easier copy/paste in logs or URLs
Benchmarks
For v0.2.0, I also created a separate JMH benchmark project and benchmarked:
- raw generation
- multi-thread throughput
- generation + toString()
- Base58 encoding/decoding
Highlights for euid-core:0.2.0
Raw generation
- * ConcurrentEuidGenerator: ~262.8M ops/s
- * FastEuidGenerator: ~254.8M ops/s
- * tested UUID v7 library: ~68.4M ops/s
Multi-thread
- * FastEuidGenerator (per thread): ~850.7M ops/s
- * ConcurrentEuidGenerator (per thread): ~848.2M ops/s
- * ConcurrentEuidGenerator (shared): ~826.3M ops/s
- * tested UUID v7 library: ~39.6M ops/s
Generate + toString()
- * FastEuidGenerator: ~68.5M ops/s
- * ConcurrentEuidGenerator: ~59.8M ops/s
- * tested UUID v7 library: ~40.2M ops/s
Base58
- * encode: ~1.4M to ~1.5M ops/s
- * decode: ~2.07M ops/s
The benchmark results helped clarify an important point:
- * raw generation is very fast
- * string conversion changes the overall picture
- * Base58 is useful, but comes with a real cost
What I learned
A few things became clear while working on this release:
- One generator strategy was not enough
Different workloads genuinely benefit from different generation models.
- Benchmarking needs discipline
Focused JMH runs gave much more meaningful results than broad mixed runs.
- IDs can be operational tools
For some systems, an ID that carries structure is more useful than a purely opaque value.
When EUID makes sense
EUID is a good fit if you want:
- sortable IDs
- topology-aware IDs
- decode support
- strong throughput
- more operational visibility from identifiers
It is probably not the right choice if:
- you only need random uniqueness
- you want fully opaque IDs
- UUID v4 or UUID v7 already fully solves your use case
EUID is not trying to replace every UUID scenario.
It is aimed at systems that benefit from sortable, decodable, infrastructure-aware identifiers.
Current status
The project is currently at v0.2.0.
At this stage, I am more focused on:
- documentation
- benchmark communication
- API clarity
than rushing to 1.0.0.
I would rather have a well-understood 0.2.x than a premature “stable” release.
Feedback welcome
I’d be very interested in feedback on:
- the API design
- the benchmark methodology
- tradeoffs vs UUID v7
- tradeoffs vs Snowflake-style IDs
- real distributed-system use cases
louis-franck-moussima
/
euid-java
Core library for EUID — a fast, distributed, sortable unique ID generator for Java.
EUID — Topology-Aware UUID v8 for Java
EUID is a Java library for generating sortable, decodable, infrastructure-aware UUID v8 identifiers.
It is designed for distributed systems that need more than raw uniqueness:
- time-ordered IDs for better database locality
-
embedded topology metadata (
region,shard,node) - deterministic per-node sequencing
- RFC 4122 / UUID v8-compatible layout
- human-friendlier Base58 encoding
- full decode support for observability and debugging
If you need identifiers that are not just unique, but also operationally meaningful, EUID gives you a structured alternative to purely random UUIDs.
Why EUID?
Traditional identifiers solve uniqueness, but they do not always help with:
- ordered inserts in databases
- infrastructure traceability
- decoding where an ID came from
- correlating events across distributed nodes
EUID is built for those cases.
In one sentence
EUID = sortable UUID v8 + topology metadata + deterministic sequencing
When to use EUID
EUID is a good…
Top comments (0)