From 7e080103ccf626ba8423e873dcea4788d359ccea Mon Sep 17 00:00:00 2001 From: Caleb White Date: Thu, 18 Jul 2024 23:07:45 -0500 Subject: [PATCH] docs: update README.md --- README.md | 199 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 120 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index b930491..fb680d2 100755 --- a/README.md +++ b/README.md @@ -1,11 +1,8 @@ -# Laraflake -

- Laraflake + Laraflake

+

Generate X/Twitter Snowflake identifiers in Laravel.

-This package enables a Laravel application to create [Snowflake IDs](https://en.wikipedia.org/wiki/Snowflake_ID). -It is a very thin wrapper around the excellent [godruoyi/php-snowflake](https://github.com/godruoyi/php-snowflake) library. ## What are Snowflakes? @@ -13,126 +10,97 @@ Snowflakes are a form of unique identifier devised by X/Twitter and are used by Some of the benefits of using Snowflakes (over alternatives such as UUID/ULID) include: -- They consist entirely of integers. -- They use less space (16 characters, so it fits in a `BIGINT`). -- Indexing of integers is much faster than indexing a string. -- Keys begin with a timestamp, so are sortable. -- Keys end with a random number, so guessing table size is not possible. -- Databases handle integers more efficiently than strings. -- Generation of new keys is faster (less than 1 ms). +- **Timestamp Component:** Extract creation time directly from the ID. +- **Uniqueness Across Distributed Systems:** Ensures unique IDs without coordination. +- **Orderability:** Roughly ordered by creation time for easy sorting. +- **Compactness:** 64-bit size, more compact than 128-bit UUIDs. +- **Performance:** Faster and less resource-intensive generation. +- **Configurability:** Flexible bit allocation for specific needs. +- **Storage Efficiency:** More efficient storage compared to larger identifiers. +- **Database Indexing:** Faster indexing and query performance. +- **Human Readability:** More compact and readable than longer identifiers. ## Installation -Pull in the package using Composer: +First pull in the package using Composer: ```bash composer require calebdw/laraflake ``` -## Configuration - -Snowflake includes a configuration file with several settings that you can use to initialize the Snowflake service. -You should begin by publishing this configuration file: +And then publish the package's configuration file: ```bash -php artisan vendor:publish +php artisan vendor:publish --provider="CalebDW\Laraflake\ServiceProvider" ``` +## Configuration + ### Snowflake Epoch The 41-bit timestamp encoded in the Snowflake is the difference between the time of creation and a given starting epoch/timestamp. Snowflakes can be generated for up to 69 years past the given epoch. - -The default epoch is `2024-01-01`, but in most cases you should set this value to the current date using a format of `YYYY-MM-DD`. +In most cases you should set this value to the current date using a format of `YYYY-MM-DD`. > **Note**: -> Do not set the timestamp to a date in the future, as that won't achieve anything. -> You should also avoid using a date far in the past (such as the Unix epoch `1970-01-01`), as that may reduce the number of years for which you can generate timestamps. +> Future dates will throw an error and you should avoid using a date far in the past (such as the Unix epoch `1970-01-01`) +as that may reduce the number of years for which you can generate timestamps. ### Data Center & Worker IDs -If using a distributed architectural setup, you'll need to set the data center and worker IDs that the application should use when generating Snowflakes. -These are both set to `0` by default, as that is a good starting point, but you are free to increase these numbers as you add more workers and data centers. - -The maximums for each of these configuration values is `31`. This gives you up to 32 workers per data center, and 32 data centers in total. -Therefore, you can have up `1024` workers each generating unique Snowflakes. - -### Sequence resolver - -In order to handle the generation of unique keys within the same millisecond, the service uses a sequence resolver. -There are several to choose from, however they each have dependencies, such as Redis. -You are free to use any of them, however the default option is a good choice, as it **doesn't** have any dependencies. +If using distributed systems, you'll need to set the data center and worker IDs that the application should use when generating Snowflakes. +These are used to ensure that each worker generates unique Snowflakes and can range from `0` to `31` (up to `1024` unique workers). ## Usage -> **WARNING**: Do not create new instances of the Snowflake service, as doing so risks generating matching keys / introducing collisions. -> Instead, always resolve the Snowflake singleton out of the container. You can also use the global helper method (see below). +> **WARNING**: Do not create new instances of the Snowflake generator (as this could cause collisions), always use the Snowflake singleton from the container. - -You can generate a Snowflake by resolving the service out of the container and calling its `id` method: -Since this is a little cumbersome, the package also registers a global `snowflake()` helper method that you can use anywhere. +You can generate a Snowflake by resolving the singleton from the container and calling its `id` method: ```php -id(); // (string) "5585066784854016" +resolve(Snowflake::class)->id(); // (string) "5585066784854016" +``` +This package also provides a `snowflake` helper function, a `Snowflake` facade, and a `Str` macro for convenience: -declare(strict_types=1); +```php use CalebDW\Laraflake\Facades\Snowflake; use Illuminate\Support\Str; -resolve('snowflake')->id(); // (string) "5585066784854016" -snowflake()->id(); // (string) "5585066784854016" -Snowflake::id(); // (string) "5585066784854016" +snowflake()->id(); // (string) "5585066784854016" +Snowflake::id(); // (string) "5585066784854016" Str::snowflakeId(); // (string) "5585066784854016" ``` -## Databases - -If you want to use Snowflakes in your database e.g. for primary and foreign keys, then you'll need to perform a couple of steps. - -First, modify your migrations so that they use the Snowflake migration methods e.g. - -```php -id(); -$table->foreignId('user_id'); -$table->foreignIdFor(User::class); - -// After -$table->snowflake()->primary(); -$table->foreignSnowflake('user_id'); -$table->foreignSnowflakeFor(User::class); -``` +This package provides a set of migration macros to make it easier to work with Snowflakes in your database schema. Here's an example: ```php -snowflake()->primary(); $table->foreignSnowflake('user_id')->constrained()->cascadeOnDelete(); - $table->string('title', 100); - $table->timestamps(); + $table->foreignSnowflakeFor(Post::class)->constrained(); }); } } ``` -Next, if you're using Eloquent, add the package's `HasSnowflakes` trait to your Eloquent models: - -```php -getKeyName(), 'slug']; + } +} +``` + +If necessary, you can explicitly cast the model's Snowflake columns using the `AsSnowflake` cast: +```php namespace App\Models; use CalebDW\Laraflake\Casts\AsSnowflake; @@ -164,11 +152,63 @@ class Post extends Model protected $casts = [ 'id' => AsSnowflake::class, 'user_id' => AsSnowflake::class, - 'title' => 'string', ]; } ``` + +### Validation + +If you need to validate Snowflakes in your application, you can use the `Snowflake` rule or the `Rule` macro provided by this package: + +```php +use CalebDW\Laraflake\Rules\Snowflake; +use Illuminate\Validation\Rule; + +$request->validate([ + 'id' => ['required', new Snowflake()], + 'user_id' => ['required', Rule::snowflake()], +]); +``` + +You can also just use the `Str` macro to check if a value is a valid Snowflake: + +```php +use Illuminate\Support\Str; + +Str::isSnowflake('5585066784854016'); // (bool) true +``` + +### Sequence Resolver + +The sequence resolver is responsible for generating the sequence component of the Snowflake +to ensure that numbers generated on the same machine within the same millisecond are unique. + +By default, if the application has a cache, then it uses the `LaravelSequenceResolver` +which uses the Laravel cache to store the last sequence number. + +If the application does not have a cache, then it uses the `RandomSequenceResolver` which +has no dependencies **but is not concurrency-safe**. + +You can override the sequence resolver by binding your own implementation in a service provider: + +```php +use Godruoyi\Snowflake\SequenceResolver; +use Illuminate\Support\ServiceProvider; + +class AppServiceProvider extends ServiceProvider +{ + public function register(): void + { + $this->app->bind(SequenceResolver::class, function() { + return new MySequenceResolver(); + }); + } +} +``` + +Please see [godruoyi/php-snowflake](https://github.com/godruoyi/php-snowflake) for more information on the available sequence resolvers and their dependencies. + ## Contributing Thank you for considering contributing! You can read the contribution guide [here](CONTRIBUTING.md). @@ -180,3 +220,4 @@ Laraflake is open-sourced software licensed under the [MIT license](LICENSE). ## Acknowledgements Derived from [caneara/snowflake](https://github.com/caneara/snowflake) which is no longer maintained. +The actual Snowflake generation is handled by the excellent [godruoyi/php-snowflake](https://github.com/godruoyi/php-snowflake) library.