uid-pool
Version:
High-performance UUID and unique ID pooling for Node.js. Pre-generate IDs in background worker threads for O(1) synchronous acquisition. Drop-in replacement for uuid.v4() and nanoid() with 10-100x better performance under load.
266 lines (190 loc) β’ 7.49 kB
Markdown
# uid-pool πββοΈ
_Because sometimes you need IDs faster than your generator can make them._
## The Problem Nobody Talks About
Picture this: You're building the next big thingβ’, and suddenly your app needs to generate thousands of unique IDs. Your trusty UUID generator starts sweating bullets, your event loop gets stage fright, and your users are left staring at loading spinners.
Not cool.
## Enter uid-pool
Think of it as a bouncer with a VIP list. While your ID generator is working hard in the background, uid-pool maintains a pre-generated pool of IDs ready to hand out instantly. No waiting, no blocking, just pure O(1) satisfaction.
```typescript
import { IdPool } from "uid-pool";
// Create a pool with your favorite ID generator
const pool = await IdPool.create({
generator: () => crypto.randomUUID(), // or nanoid(), uuid.v7(), etc.
poolSize: 1000, // Maximum pool capacity
minSize: 250, // Refill threshold
});
// Get an ID instantly (seriously, it's O(1))
const id = pool.acquire(); // "550e8400-e29b-41d4-a716-446655440000"
```
## Why Should You Care?
### π **Blazing Fast**
- **O(1) ID acquisition** - Faster than a caffeinated cheetah
- **Non-blocking operations** - Your event loop can finally breathe
- **Smart pre-generation** - IDs ready before you need them
### π§© **Works With Everything**
- **Any ID generator** - UUID, nanoid, or that weird custom ID generator
- **Any runtime** - Node.js, Bun, Deno, Edge workers
- **Zero dependencies**
### π‘οΈ **Battle-Tested Design**
- **Circular buffer magic** - No array shifting nonsense here
- **Worker thread isolation** (Node.js) - True parallelism, not just promises
- **Graceful degradation** - Falls back elegantly when things go sideways
- **TypeScript native** - Your IDE will thank you
## Installation
```bash
npm install uid-pool
# or if you're cool
bun add uid-pool
# or if you're really cool
pnpm add uid-pool
```
## Real-World Examples
### Basic Usage
```typescript
import { IdPool } from "uid-pool";
import { nanoid } from "nanoid";
const pool = await IdPool.create({
generator: () => nanoid(),
poolSize: 500,
});
// Need an ID? Just ask!
// The pool is already refilling in the background
const id = pool.acquire();
```
### Custom ID Generator
```typescript
let counter = 0;
const pool = await IdPool.create({
generator: () => `user-${Date.now()}-${++counter}`,
poolSize: 1000,
minSize: 100,
});
```
### Monitoring Pool Health
```typescript
const pool = await IdPool.create({
generator: () => crypto.randomUUID(),
poolSize: 1000,
minSize: 250,
});
pool.on("refill", () => {
console.log("Pool is thirsty, refilling...");
});
pool.on("error", (error) => {
console.error("Pool party crashed:", error);
// Don't worry, the pool keeps working
});
// Check pool stats
console.log(`Pool size: ${pool.size}`);
console.log(`Pool full: ${pool.isFull}`);
```
### Graceful Cleanup
```typescript
// When you're done partying
await pool.close();
```
## How It Works (The Secret Sauce)
1. **Factory Pattern**: `IdPool.create()` returns a fully initialized pool, pre-filled to your minimum size requirement. No empty pools on day one!
2. **Circular Buffer**: Unlike naive implementations using `Array.shift()` (O(n) complexity), we use a circular buffer for true O(1) operations. Your computer science professor would be proud.
3. **Smart Runtime Detection**: Automatically detects your environment and chooses the optimal strategy:
- **Node.js/Bun**: Spawns a worker thread for true parallel ID generation
- **Edge/Browser**: Uses chunked generation with `setTimeout` to keep the main thread responsive
4. **Background Refilling**: When the pool drops below `minSize`, it automatically starts refilling without blocking your `acquire()` calls. It's like having a helpful bartender who knows when to restock.
## Configuration Options
```typescript
interface IdPoolOptions {
// Your ID generator function
generator: () => string | Promise<string>;
// Maximum number of IDs to keep in the pool
poolSize: number;
// Start refilling when pool drops below this
minSize?: number;
// How many IDs to generate per chunk (edge runtime)
chunkSize?: number;
// Delay between generation chunks in ms (edge runtime)
chunkDelay?: number;
}
```
## Platform Support
- **Node.js**: v18+ (uses worker threads for maximum performance)
- **Bun**: v1.0+ (also uses worker threads, because Bun is cool like that)
- **Deno**: Works great with chunked generation
- **Edge Workers**: Cloudflare, Vercel Edge, etc.
- **Browsers**: Modern browsers with ES2020 support
## Benchmarks
On a typical developer machine (you know, the one with 47 Chrome tabs open):
```
ID Acquisition Performance:
β Array.shift() approach: 2,341 ops/sec
β uid-pool O(1) approach: 8,923,654 ops/sec π
Pool Refill (1000 IDs):
β Blocking generation: 125ms (freezes your app)
β uid-pool background: 0ms (your app keeps running)
```
_Results may vary. Your mileage may depend on how many Slack notifications you're getting._
## Error Handling
The pool is designed to keep swimming even when things go wrong:
```typescript
pool.on("error", (error) => {
if (error instanceof GeneratorError) {
console.log("Generator had a bad day:", error.originalError);
} else if (error instanceof WorkerError) {
console.log("Worker called in sick:", error);
// Don't worry, pool falls back to main thread
}
});
// The pool keeps working even if the generator fails occasionally
const id = pool.acquire(); // Still returns IDs from the pool
```
## Common Patterns
### High-Traffic API Endpoints
```typescript
const requestIdPool = await IdPool.create({
generator: () => nanoid(),
poolSize: 10000, // Handle traffic spikes
minSize: 5000, // Keep plenty ready
});
app.use((req, res, next) => {
req.id = requestIdPool.acquire() || nanoid(); // Fallback for safety
next();
});
```
### Database Record Creation
```typescript
const orderIdPool = await IdPool.create({
generator: async () => {
// Some expensive custom ID generation
const prefix = await getRegionalPrefix();
return `${prefix}-${Date.now()}-${randomBytes(4).toString("hex")}`;
},
poolSize: 1000,
});
async function createOrder(data) {
const id = orderIdPool.acquire();
if (!id) {
throw new Error("Order system overwhelmed, try again");
}
return db.orders.create({ id, ...data });
}
```
## Testing
Run the test suite (it's comprehensive, we promise):
```bash
bun test
```
## Contributing
Found a bug? Have an idea? Pull requests welcome! Just remember:
1. **Performance matters** - If it makes things slower, we need to talk
2. **Keep it simple** - Complexity is the enemy of reliability
3. **Test everything** - Untested code is broken code
4. **Document your why** - Future you will thank present you
## Philosophy
This library follows the Unix philosophy: do one thing and do it well. We generate IDs fast. That's it. No blockchain integration, no AI predictions, no metaverse compatibility. Just fast, reliable ID generation.
## License
MIT - Because sharing is caring.
## Acknowledgments
- The circular buffer implementation was inspired by that computer science textbook you never returned
- Worker threads idea stolen from literally every other pooling library (but we did it better)
- Special thanks to coffee for making this possible
---
_Built with β€οΈ and a severe lack of patience for slow ID generation._