dekuan/dedid
dekuan/dedid is a PHP package for generating distributed, unique IDs. It provides a lightweight API for creating sortable identifiers across nodes or services, making it suitable for Laravel and other PHP applications that need collision-resistant IDs without central coordination.
dekuan/dedid is a Composer package that generates 64-bit, roughly time-ordered unique IDs suitable for primary keys in a distributed/sharded database. It is a Snowflake-style generator (timestamp + worker/machine identifier + per-millisecond sequence), which means the ID doubles as a sortable key and a lightweight source of creation-time metadata.
Minimal steps:
Install via Composer:
composer require dekuan/dedid
Look first at the package README and its src/ directory — this package predates Laravel auto-discovery and does not ship a service provider or facade. You instantiate the generator directly. Confirm the exact class name/namespace from the source (typically something like Dekuan\Dedid\Dedid) before wiring it in.
Configure the generator once, at bootstrap. The two values that matter are the worker/machine id (must be unique per running process host) and, if exposed, the epoch (a fixed start timestamp baked into the ID layout). Keep both in environment config, not in code.
First use case — replace an auto-increment id on a sharded table:
use Dekuan\Dedid\Dedid;
$id = Dedid::getInstance()->generate(); // 64-bit integer
Then persist it into a BIGINT UNSIGNED column and stop relying on the database's AUTO_INCREMENT.
In Laravel, bind it as a singleton so you don't re-initialize per request:
// AppServiceProvider::register()
$this->app->singleton(Dedid::class, function () {
return Dedid::getInstance(); // or new Dedid($workerId, $epoch)
});
A reasonable first milestone: generate IDs for one new table, verify they are unique across two app servers, and confirm they sort in insertion order.
Singleton / container binding. The generator is cheap to construct but should be one instance per process so its in-memory sequence counter stays coherent. Bind it as a singleton and inject it; avoid new inside loops.
Auto-fill primary keys on Eloquent models. The cleanest integration is a model trait or a creating hook so callers never touch the generator:
protected static function booted()
{
static::creating(function ($model) {
if (empty($model->getKey())) {
$model->{$model->getKeyName()} = app(Dedid::class)->generate();
}
});
}
Set public $incrementing = false; and protected $keyType = 'int'; (or 'string' — see gotchas) on the model.
Batch generation for bulk inserts. If the generator exposes a batch method, prefer it over calling generate() in a loop: it lets the sequence counter advance in one lock and reduces per-ID overhead. If no batch API exists, wrap the loop in a small service method so you can swap implementations later.
Sharding / routing from the ID. Because the high bits are a timestamp, you can decode an ID to recover creation time and (depending on the layout) the worker id. Use this for:
WHERE id BETWEEN ...),created_at index.Decode once and cache the result rather than parsing on every read path.
Service layer abstraction. Wrap the package behind your own IdGenerator interface. The package is unmaintained; an interface lets you swap to symfony/uid, a Snowflake library, or a Redis INCR scheme without touching domain code.
Queues and jobs. Generate the ID at dispatch time when the ID must be known before the job runs (e.g., returning it to the client). Generate at execution time when you only care about insertion order. Do not generate in a job and assume it reflects enqueue time — it reflects the worker's clock.
Coexistence with auto-increment. For a migration path, keep the old integer column and add the new ID column; backfill in batches, then cut over. Don't mix both as the primary key.
64-bit integers break JavaScript and JSON. IDs exceed Number.MAX_SAFE_INTEGER (2^53), so JSON.parse will silently corrupt them in the browser. Always serialize as a string ($keyType = 'string' or a cast to string) and document it in your API contract. This is the single most common production bug with Snowflake IDs.
Database column type is not negotiable. Use BIGINT UNSIGNED (or DECIMAL(20,0)) — a plain INT or signed BIGINT will overflow or accept negative values depending on the layout. MySQL's BIGINT signed max is fine, but unsigned avoids surprises if the top bit is ever set.
Worker/machine id must be unique across every process that generates IDs. Two hosts with the same worker id can emit duplicate IDs within the same millisecond. Derive it from the hostname hash, an orchestrator-provided ordinal, or a per-node env var — and add a startup assertion that catches obviously wrong values (0 on every node is a classic misconfiguration).
Clock skew is the failure mode to plan for. If the system clock jumps backward (NTP correction, VM snapshot restore), a Snowflake generator can either block, emit duplicates, or throw. Keep NTP healthy, monitor for the thrown exception explicitly, and decide whether to fail the request or fall back. Never silently catch and retry without a bound.
Sequence overflow within one millisecond. Under heavy load a 12-bit sequence (4096 values) can exhaust before the clock ticks. Well-written implementations spin until the next millisecond; verify this package does, because a naive implementation wraps and duplicates. Load-test at your expected peak IDs/ms per process.
No Laravel auto-discovery. There is no DedidServiceProvider registered in composer.json in the 2017-era package layout. You must bind it yourself, and php artisan vendor:publish likely won't find anything useful. Check composer.json's extra.laravel block — if empty, wire it manually.
Unmaintained since December 2017. Expect PHP 7-era code: no return types, possibly each()/old idioms, an old PHPUnit constraint in require-dev, and no PHP 8 testing. Run it against your PHP version early; if composer install fights you, fork and patch rather than pinning to an ancient PHP. Audit it before relying on it for a key generator — key correctness is security-adjacent.
Don't depend on IDs for business ordering. They are roughly monotonic, not strictly increasing across workers or across a backward clock step. Sort by created_at (or a dedicated sequence) when order matters for logic.
Testing strategy. Freeze the clock (Carbon's setTestNow) and inject a fixed worker id so IDs are deterministic in unit tests. For integration tests, assert uniqueness and monotonicity within a single process rather than exact ID values. Keep a fake IdGenerator in your interface so feature tests don't touch the real generator.
Extension points. The epoch and worker id are the intended knobs; changing the epoch changes the ID layout and can invalidate IDs already issued — pick one and never move it. If the package allows swapping the sequence/counter backend, resist unless you have a concrete need; the file/getmypid()-based coordination is where race conditions live.
Debugging tip. When an ID looks "wrong," decode it: extract the timestamp bits, compare to wall-clock time, and extract the worker bits to confirm the node. A mismatch usually points to a wrong epoch constant or a misconfigured worker id rather than a generator bug.
How can I help you explore Laravel packages today?