- C# 100%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| PolymerLabs.Druid | ||
| PolymerLabs.Druid.Benchmarks | ||
| PolymerLabs.Druid.Examples | ||
| PolymerLabs.Druid.Generators | ||
| PolymerLabs.Druid.Generators.Tests | ||
| PolymerLabs.Druid.Tests | ||
| .gitignore | ||
| PolymerLabs.Druid.sln | ||
| README.md | ||
Druid
Druid is a compact identifier library for .NET. A Druid packs a domain, shard, timestamp, and sequence into one 64-bit value and offers serialization to a 11-character base64url string.
DuIKCSHQ5bB
UuIKCSHQ5bB
The first character is the domain. That keeps IDs readable in logs, URLs, traces, and API payloads without expanding the stored value.
Packages
| Package | Use it for |
|---|---|
Druid |
Provides the Druid value type, also with component types, parsing, formatting, comparison, and conversion. |
Druid.Generators |
Configurable Druid generation tooling. |
Installation
dotnet add package Druid
dotnet add package Druid.Generators
Quick Start
using PolymerLabs.Druid;
using PolymerLabs.Druid.Generators;
var generator = new DruidGeneratorBuilder()
.Build();
Druid userId = generator.Create(Domain.U);
Druid designId = generator.Create(Domain.D);
Console.WriteLine(userId); // UuI5fD79cb7
Console.WriteLine(designId); // DuIKCSHQ5bB
Use domains for the type of thing being identified. Use shards for the source that generated the ID, such as a service, worker, tenant, import job, or region.
Use Cases
- Public IDs where IDs need to be short and readable.
- Multi-entity systems where ID types need to be easily distinguishable.
- Distributed, concurrent generators across multiple generation instances.
- Database keys or indexed columns where size and sortability matters.
String Form
Druid strings are 11 characters long and use the base64url alphabet: A-Z, a-z, 0-9, -, and _.
using PolymerLabs.Druid;
using PolymerLabs.Druid.Parts;
var timestamp = Timestamp.Create(30541986);
var sequence = Sequence.Create(23525);
var designId = Druid.Create(Domain.D, Shard.Create(46), timestamp, sequence);
var userId = Druid.Create(Domain.U, Shard.Create(46), timestamp, sequence);
Console.WriteLine(designId); // DuIKCSHQ5bB
Console.WriteLine(userId); // UuIKCSHQ5bB
Only the first character changed because only the domain changed. That makes mixed-domain data easier to read:
User UuIKCSHQ5bB
Design DuIKCSHQ5bB
Domain checks can be made explicitly:
var id = Druid.ParseExact("UuIKCSHQ5bB");
if (id.Domain != Domain.U)
{
throw new ArgumentException("Expected a user ID.", nameof(id));
}
```:wq:
## Layout
A Druid is packed into 64 bits:
```text
[ sequence:18 ][ timestamp:32 ][ shard:8 ][ domain:6 ]
| Part | Size | Purpose |
|---|---|---|
| Domain | 6 bits | Separates IDs by type. Examples: Domain.U for users, Domain.D for designs. |
| Shard | 8 bits | Identifies the generator source. Use it for nodes, regions, tenants, workers, or import streams. |
| Timestamp | 32 bits | Stores seconds since the configured epoch. |
| Sequence | 18 bits | Adds per-generator variation for the same timestamp. |
The packed value is 8 bytes. The string value is 11 characters.
Generating IDs
Druid.Generators provides DruidGeneratorBuilder for application code:
using PolymerLabs.Druid.Generators;
using PolymerLabs.Druid.Parts;
var generator = new DruidGeneratorBuilder()
.UseShard(Shard.Create(12))
.UseEpoch(DateTimeOffset.UnixEpoch)
.UseIncrementalSequencer()
.Build();
var userId = generator.Create(Domain.U);
var userIds = generator.Create(Domain.U, count: 10).ToArray();
Available generator options:
| Option | Purpose |
|---|---|
UseShard(Shard.Create(value)) |
Sets the shard for generated IDs. Defaults to Shard.First. |
UseEpoch(DateTimeOffset) |
Sets the epoch used for timestamp calculation. Defaults to Unix epoch. |
UseTimeProvider(TimeProvider) |
Sets the time source for tests and deterministic imports. |
UseIncrementalSequencer() |
Uses ascending sequence values. This is the default. |
UseRandomSequencer() |
Uses random sequence values. |
UseRandomSequencer(seed) |
Uses deterministic random sequence values from a seed. |
UseSequencer<T>() / UseSequencer(instance) |
Uses a custom ISequencer. |
Manual construction is available when the exact parts matter:
var id = Druid.Create(
Domain.U,
Shard.Create(5),
Timestamp.Create(new DateTimeOffset(2026, 4, 29, 0, 0, 0, TimeSpan.Zero)),
Sequence.Create(563));
Parsing and Storage
var id = Druid.ParseExact("DuIKCSHQ5bB");
string text = id.ToString(); // "DuIKCSHQ5bB"
ulong packed = id.ToUnsignedInt64();
long signed = id.ToSignedInt64();
byte[] bytes = id.ToByteArray();
Use ParseExact when the input must contain only the ID. Use TryParseExact for non-throwing validation.
For storage, use the 64-bit value for compact database columns. ToSignedInt64() provides an order-preserving signed long representation for systems that store signed integer columns.
Examples
See the examples project for complete usage:
- GeneratorExample.cs shows the recommended generator flow.
- ManualGenerationExample.cs shows deterministic construction for tests, fixtures, and imports.
- ShardExample.cs shows shard usage for multiple ID sources.
License
No license file is currently included in this repository.