UNPKG

snowflakify

Version:

The most complete Snowflake ID generator in TypeScript

348 lines (275 loc) 8.59 kB
<a href="https://github.com/CatWithAWand/snowflakify"> <p align="center"> <img width=500 src="https://raw.githubusercontent.com/CatWithAWand/snowflakify/main/logo.svg"/> </p> </a> <p align="center"> <a href="https://www.npmjs.com/package/snowflakify"><img src="https://img.shields.io/npm/v/snowflakify?style=flat" alt="snowflakify - npm" /></a> <a href="https://catwithawand.github.io/snowflakify/"><img src="https://img.shields.io/github/deployments/CatWithAWand/snowflakify/github-pages?label=github-pages&style=flat" alt="GitHub-pages deployment" /></a> <a href="http://commitizen.github.io/cz-cli/"><img src="https://img.shields.io/badge/commitizen-friendly-brightgreen?style=flat" alt="Commitizen friendly" /></a> <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-007ACC?style=flat&logo=typescript&logoColor=white" alt="TypeScript" /></a> <br> <strong>A complete Snowflake ID generator in TypeScript. <br> Generation, destructuring, custom snowflake structure, and more...</strong> </p> --- <br> ## About Snowflakify is a complete [Node.js](https://nodejs.org) module for distributed systems to generate [snowflake IDs](https://en.wikipedia.org/wiki/Snowflake_ID), written in [TypeScript](https://www.typescriptlang.org/). - IDs based on worker threads/cluster, <br> machine IPv4/MAC addresses - Circular/Ring Buffer for increased output - Snowflake destructuring - BigInt based - Custom epoch - Custom snowflake fragments <br> with customizable bit length: - Timestamp - Random - Worker ID - Process ID - IPv4 Address - MAC Address - Sequence <br> ### Circular/Ring Buffer performance | useBuffer | workerCount | Time Period (s) | Generated/Main | Generated/Workers | IDs/ms | | :-------: | :---------: | :-------------: | :------------: | :---------------: | :----: | | false | 0 | 10 | 3453950 | 0 | 345.4 | | true | 1 | 10 | 1190497 | 5700275 | 689.1 | | true | 2 | 10 | 0 | 8677260 | 867.7 | | true | 3 | 10 | 0 | 9726306 | 972.6 | <br><br> ## Installation ### Requires Node.js 14.5.0 or newer. **npm:** ```bash $ npm install snowflakify ``` **yarn:** ```bash $ yarn add snowflakify ``` **pnpm:** ```bash $ pnpm add snowflakify ``` <br><br> ## Usage Creating a new Snowflakify ID generator ```js import Snowflakify from 'snowflakify'; // with default options const snowflakify = new Snowflakify(); // or with a custom epoch and one of the three presets const CUSTOM_EPOCH = 1262304001000; const snowflakify = new Snowflakify({ epoch: CUSTOM_EPOCH, preset: 'ipv4' }); ``` Generating a snowflake ID ```js snowflakify.nextId(); // 1000920566716264449n ``` Destructuring a snowflake ID ```js const snowflakeId = snowflakify.nextId(); // 1000920566716264449n snowflakify.destructure(snowflakeId); // [ // { identifier: 'TimestampFragment', value: 1658708459310n }, // { identifier: 'WorkerFragment', value: 0 }, // { identifier: 'ProcessFragment', value: 23 }, // { identifier: 'SequenceFragment', value: 1 } // ] ``` Hexadecimal snowflake ID ```js const id = snowflakify.nextHexId(); snowflakify.destructureHex(id); ``` Creating a custom snowflake structure ```js import Snowflakify, { TimestampFragment, NetworkFragment, SequenceFragment, } from 'snowflakify'; const CUSTOM_EPOCH = 1262304001000; const snowflakify = new Snowflakify({ fragmentArray: [ new TimestampFragment(42, CUSTOM_EPOCH), new NetworkFragment(10, 'ipv4'), new SequenceFragment(12), ], }); const snowflakeId = snowflakify.nextId(); // 1662643509670989825n snowflakify.destructure(snowflakeId); // [ // { identifier: 'TimestampFragment', value: 1658709104128n }, // { identifier: 'NetworkFragment:ipv4', value: 197 }, // { identifier: 'SequenceFragment', value: 1 } // ] ``` <br><br> > ## **Note:** Snowflakify must be instantiated inside the worker when working with worker_threads or cluster, else it won't be able to see the worker. <br> ## Example with worker_threads index.js ```js import { Worker } from 'worker_threads'; const numWorkers = 3; for (let i = 0; i < numWorkers; i += 1) { const worker = new Worker('./worker.js'); worker.on('message', (msg) => { console.log(`Worker ${worker.threadId} generated snowflake:`); console.log(msg); }); } ``` worker.js ```js import { parentPort } from 'worker_threads'; import Snowflakify from 'snowflakify'; const snowflakify = new Snowflakify(); const snowflakeId = () => { const snowflake = snowflakify.nextId(); const destructuredSnowflake = snowflakify.destructure(snowflake); return { snowflake, destructuredSnowflake, }; }; parentPort.postMessage(snowflakeId()); ``` Console output ```bash Worker 1 generated snowflake: { snowflake: 1001124197361188865n, destructuredSnowflake: [ { identifier: 'TimestampFragment', value: 1658757008639n }, { identifier: 'WorkerFragment', value: 1 }, { identifier: 'ProcessFragment', value: 16 }, { identifier: 'SequenceFragment', value: 1 } ] } Worker 2 generated snowflake: { snowflake: 1001124197382291457n, destructuredSnowflake: [ { identifier: 'TimestampFragment', value: 1658757008644n }, { identifier: 'WorkerFragment', value: 2 }, { identifier: 'ProcessFragment', value: 16 }, { identifier: 'SequenceFragment', value: 1 } ] } Worker 3 generated snowflake: { snowflake: 1001124197378228225n, destructuredSnowflake: [ { identifier: 'TimestampFragment', value: 1658757008643n }, { identifier: 'WorkerFragment', value: 3 }, { identifier: 'ProcessFragment', value: 16 }, { identifier: 'SequenceFragment', value: 1 } ] } ``` <br> ## Example with cluster index.js ```js import cluster from 'node:cluster'; import Snowflakify from 'snowflakify'; const numWorkers = 3; if (cluster.isPrimary) { for (let i = 0; i < numWorkers; i += 1) { cluster.fork(); } } else { const snowflakify = new Snowflakify(); const snowflake = snowflakify.nextId(); const destructuredSnowflake = snowflakify.destructure(snowflake); console.log(`Worker ${cluster.worker.id} generated snowflake:`); console.log({ snowflake, destructuredSnowflake }); } ``` Console output ```bash Worker 1 generated snowflake: { snowflake: 1001124291087147009n, destructuredSnowflake: [ { identifier: 'TimestampFragment', value: 1658757030985n }, { identifier: 'WorkerFragment', value: 1 }, { identifier: 'ProcessFragment', value: 26 }, { identifier: 'SequenceFragment', value: 1 } ] } Worker 2 generated snowflake: { snowflake: 1001124291099865089n, destructuredSnowflake: [ { identifier: 'TimestampFragment', value: 1658757030988n }, { identifier: 'WorkerFragment', value: 2 }, { identifier: 'ProcessFragment', value: 27 }, { identifier: 'SequenceFragment', value: 1 } ] } Worker 3 generated snowflake: { snowflake: 1001124291150331905n, destructuredSnowflake: [ { identifier: 'TimestampFragment', value: 1658757031000n }, { identifier: 'WorkerFragment', value: 3 }, { identifier: 'ProcessFragment', value: 28 }, { identifier: 'SequenceFragment', value: 1 } ] } ``` <br> ## Using the Circular/Ring Buffer ```js import Snowflakify, { TimestampFragment, WorkerFragment, ProcessFragment, SequenceFragment, } from 'snowflakify'; const CUSTOM_EPOCH = 1262304001000; const snowflakify = new Snowflakify({ useBuffer: true, bufferSize: 2 ** 21, workerCount: 2, fragmentArray: [ new TimestampFragment(42, CUSTOM_EPOCH), new WorkerFragment(5), new ProcessFragment(5), new SequenceFragment(12), ], }); // ... ``` Console output (next 3 snowflake IDs generated and destructured) ```bash 1662657179066654721n [ { identifier: 'TimestampFragment', value: 1658712363166n }, { identifier: 'WorkerFragment', value: 2 }, { identifier: 'ProcessFragment', value: 22 }, { identifier: 'SequenceFragment', value: 1 } ] 1662657179066654722n [ { identifier: 'TimestampFragment', value: 1658712363166n }, { identifier: 'WorkerFragment', value: 2 }, { identifier: 'ProcessFragment', value: 22 }, { identifier: 'SequenceFragment', value: 2 } ] 1662657179066654723n [ { identifier: 'TimestampFragment', value: 1658712363166n }, { identifier: 'WorkerFragment', value: 2 }, { identifier: 'ProcessFragment', value: 22 }, { identifier: 'SequenceFragment', value: 3 } ] ```