middag/chrono-id

Chronological IDs with support for sharding and an accompanying Doctrine type

1.1.1 2019-01-11 15:00 UTC

This package is auto-updated.

Last update: 2020-02-11 18:23:13 UTC


README

Travis (.org) Packagist

ChronoId is a chronological identifier value object and an ideal alternative for auto incrementing primary keys. This library has been inspired on the implementation discussed by Instagram on their engineering blog.

But, why?

We previously used UUIDs as our primary keys but after reading this, this and this we decided that UUIDs were far from ideal. Or at least, when used with MySQL. Using ordered UUIDs would help with the performance issues, but storing binary data in a table and then using workarounds to get readable IDs still felt like just that: a workaround.

Next question: why not rely on auto incrementing IDs? Well, we use command buses in our projects and as part of this architecture, we have to generate IDs without relying on a database. Thus, we needed something like FancyId::generate() to spit out an ID with as few dependencies as possible.

Solution

Meet ChronoId! ID's are generated by so-called builders. The DefaultShardedBuilder generates IDs that are composed of three components: time, a shard number and a suffix. Each component (or block) consists of a given number of bits. The DefaultShardedBuilder generates IDs that fit neatly into 64 bits, structured as follows:

[time][shard][suffix]

Depending on the time and shard providers, the length of the suffix is increased for maximum randomness (so as few collisions as possible).

If you don't need sharding then the DefaultUnshardedBuilder is just fine and also saves you some storage space.

Install

composer require middag/chrono-id

Usage

use MIDDAG\ChronoId\Builder\DefaultShardedBuilder;
use MIDDAG\ChronoId\ChronoId;

$builder = DefaultShardedBuilder::factory();

// Optional: this is done automatically by the builder
ChronoId::setBuilder($builder);

// Generate an ID
$chronoId = ChronoId::generate();

print $chronoId->getInteger();

// -> 21791542821782533

Advanced

TODO

Block sizes

The TimeProvider has the biggest impact on the resulting ID's size. The UnixEpochTimeProvider needs 45 bits to store a timestamp with proper precision (in order to avoid collisions) while the CustomEpochTimeProvider only needs 41 to 45 bits, depending on the chosen block size.