unique-id-generator-ts
Version:
A high-performance unique ID generator based on Snowflake architecture. Supports up to 4096 unique IDs per millisecond per machine.
148 lines (147 loc) • 5.93 kB
JavaScript
;
/**
* UniqueIdGenerator
*
* A high-performance unique ID generator based on Snowflake architecture.
* Generates a 64-bit numeric ID composed of:
*
* - 41 bits for timestamp (in milliseconds since a custom epoch)
* - 10 bits for machine ID (auto-generated from OS network interface)
* - 12 bits for sequence number (increments within the same millisecond)
*
* Features:
* - Generates up to 4096 unique IDs per millisecond per machine
* - Ensures time-ordered and compact identifiers
* - Automatically derives a consistent machine ID using Node.js `os` module
* - Includes decoding method to extract timestamp, machine ID, and sequence
*
* Example ID structure:
* (timestamp << 22) | (machineId << 12) | sequence
*
* Suitable for distributed systems where unique, sortable, and compact IDs are needed.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.UniqueIdGenerator = void 0;
const os = __importStar(require("os"));
class UniqueIdGenerator {
constructor(epoch = 1700000000000) {
this.epoch = epoch;
this.machineId = this.generateUniqueMachineId(); // 10 bits for machineId (0-1023)
this.sequence = 1;
this.lastTimestamp = -1;
}
/**
* Generates a unique 64-bit ID based on timestamp, machine ID, and sequence.
* Returns a `bigint` ID.
*/
generateUniqueId() {
let timestamp = this.currentTimestamp();
if (timestamp < this.lastTimestamp) {
throw new Error("Clock moved backwards. Refusing to generate ID.");
}
if (timestamp === this.lastTimestamp) {
this.sequence = (this.sequence + 1) & 0xfff; // 12 bits for sequence
if (this.sequence === 1) {
while (timestamp <= this.lastTimestamp) {
timestamp = this.currentTimestamp();
}
}
}
else {
this.sequence = 1;
}
this.lastTimestamp = timestamp;
// Combine parts to create a unique ID
return ((BigInt(timestamp) << BigInt(22)) |
(BigInt(this.machineId) << BigInt(12)) | // 10 bits for machineId (shifted to the appropriate position)
BigInt(this.sequence)).toString();
}
/**
* Decodes a 64-bit ID into timestamp, machine ID, and sequence.
* @param id - The ID to decode
* @param epoch - (Optional) Custom epoch used during ID generation. Defaults to the instance's epoch.
* @returns An object with timestamp, machineId, and sequence
*/
decodeId(id, epoch) {
if (!epoch) {
epoch = 1700000000000;
}
// Convert the ID to a BigInt
const bigIntId = BigInt(id);
// 41 bits for timestamp, shift the ID by 22 to get the timestamp
const timestamp = Number(bigIntId >> BigInt(22));
// Extract the machineId (next 10 bits), shift the ID by 12 bits (timestamp + sequence) and mask with 0x3FF (10 bits)
const machineId = Number((bigIntId >> BigInt(12)) & BigInt(0x3ff));
// Extract the sequence (last 12 bits), mask with 0xFFF (12 bits)
const sequence = Number(bigIntId & BigInt(0xfff));
// Calculate the actual timestamp by adding the epoch back
const actualTimestamp = epoch + timestamp;
return {
timestamp: actualTimestamp,
machineId: machineId,
sequence: sequence,
};
}
currentTimestamp() {
return Date.now() - this.epoch;
}
generateUniqueMachineId() {
// Step 1: Get the MAC address of the first non-internal network interface
const networkInterfaces = os.networkInterfaces();
let macAddress = null;
for (const interfaceName in networkInterfaces) {
const interfaces = networkInterfaces[interfaceName];
if (!interfaces)
continue;
for (const net of interfaces) {
if (!net.internal && net.mac !== "00:00:00:00:00:00") {
macAddress = net.mac;
break;
}
}
if (macAddress)
break;
}
if (!macAddress) {
throw new Error("Unable to find a valid MAC address.");
}
// Step 2: Convert MAC address to a number
const macAsNumber = parseInt(macAddress.replace(/:/g, ""), 16); // Convert MAC address to a number
const uniqueId = macAsNumber % 1024; // 10-bit range
return uniqueId;
}
}
exports.UniqueIdGenerator = UniqueIdGenerator;