Engineering notes

NOTE Laravel · Redis · Distributed systems

Designing distributed IDs for Laravel with Redis

A practical look at time-sortable 64-bit identifiers, generator coordination, storage boundaries, and the operational constraints behind Snowflake-style IDs.

A distributed identifier looks like a number, but its real job is coordination. The generator must preserve uniqueness across processes and machines without turning the primary database into a central sequence bottleneck. It must also survive application restarts, clock drift, and the different integer semantics used by PHP, SQL, and JavaScript.

Snowflake-style IDs solve the common case by packing time, generator identity, and a per-timestamp sequence into one 64-bit value. The layout used by Snowflake for Laravel reserves 41 bits for time, 10 bits for the generator, and 12 bits for the sequence.

The bit layout is an operating contract

The three fields are not arbitrary implementation details:

  • Timestamp bits make IDs approximately sortable by creation time.
  • Generator bits let independent workers issue IDs during the same millisecond.
  • Sequence bits let one generator issue multiple IDs during that millisecond.
$id = Snowflake::id();

// Preserve the full 64-bit value at external boundaries.
return response()->json([
    'id' => (string) $id,
]);

A wider generator field supports more concurrent generators but shortens another field. A wider sequence supports larger bursts from one generator but consumes the same fixed 64-bit budget. The correct split follows deployment topology and expected throughput, not aesthetic preference.

Coordinate generators outside the primary database

Every active generator needs an identity that is unique for the period in which it can produce IDs. Static machine IDs work in fixed infrastructure, but become awkward when containers are replaced or scaled dynamically.

Redis offers a small shared coordination surface. A generator can claim a slot with a bounded lease, refresh that lease while active, and release or expire it when the process disappears. This keeps ID generation independent from row insertion while avoiding hard-coded worker identities.

The failure rule matters: if a process cannot establish a safe generator identity, it must not guess. Failing closed is less convenient than issuing a collision that may appear much later in a different table.

Keep 64-bit IDs as strings at web boundaries

A signed 64-bit integer fits comfortably in PHP on a 64-bit runtime and in a SQL BIGINT. JavaScript numbers are different: integer precision is only guaranteed through $2^{53}-1$.

That makes the transport rule simple:

{
    "id": "7428754921098649600"
}

Store the value as BIGINT, work with the integer where the runtime guarantees it, and serialize it as a string in JSON, HTML attributes, and browser-facing state. This is not formatting trivia; it prevents silent identifier corruption.

Treat clock behavior as part of correctness

Time-sortable IDs depend on a clock. If the observed clock moves backward, blindly generating from the new timestamp can overlap an earlier sequence window. A robust generator needs an explicit clock-regression policy: wait for a small drift, retain the last safe timestamp, or stop and surface an operational error.

The choice should be observable. Operators need enough context to distinguish exhausted sequence capacity, unavailable Redis coordination, and clock regression.

Integrate at framework boundaries

A Laravel package becomes useful when the identifier policy appears consistently across the framework:

  • Schema helpers create the correct BIGINT columns and indexes.
  • Eloquent casts preserve IDs without accidental numeric conversion.
  • Validation rules reject malformed or out-of-range values.
  • Artisan commands expose generator state and operational checks.
  • API resources serialize identifiers as strings.

The generator algorithm is only one part. The engineering value comes from making the safe behavior the default at every boundary where an ID is created, stored, validated, or transported.

Verification priorities

The most valuable tests target invariants rather than examples:

  1. IDs remain unique across concurrent generators.
  2. Sequence rollover cannot duplicate a value.
  3. Clock regression follows the selected failure policy.
  4. JSON serialization never converts the ID to a floating-point number.
  5. Database round trips preserve the exact 64-bit value.

A distributed ID system is small code with a large blast radius. Its design should be boring, explicit, and difficult to misuse.

RELATED public project

Snowflake for Laravel

Distributed, time-sortable 64-bit IDs for Laravel with Redis coordination, Eloquent integration, validation, and Artisan tooling.

Inspect the project