fair-queue
Version:
A fair, queue-based rate-limiting library with configurable selection modes, balancing queue size and wait time to prioritize task processing.
140 lines (106 loc) • 5.33 kB
Markdown
A fair, queue-based rate-limiting library with configurable selection modes, balancing queue size and wait time to prioritize task processing.
[](https://www.npmjs.com/package/fair-queue)
[](https://bundlephobia.com/package/fair-queue)
[](https://bundlephobia.com/package/fair-queue)
- **Fair Queue Prioritization**: Balances queue size and wait time for equitable resource allocation
- **Dual Selection Modes**:
- Randomized mode for better perceptual responsiveness
- Deterministic mode for predictable, highest-weight-first selection
- **New User Responsiveness**: Assigns new users an initial duration boost for quick service under load
- **FCFS for New Users**: Maintains first-come, first-served ordering for users with equal weights
- **TypeScript Support**: Full type safety and autocompletion
- **Isomorphic**: Works in both Node.js and browsers
- **Debug Mode**: Optional logging for development and troubleshooting
- **Lightweight**: Efficient queue management using linked lists
```bash
npm install fair-queue
```
You can also install `fair-queue` with [pnpm](https://pnpm.io/), [yarn](https://yarnpkg.com/), or [slnpm](https://github.com/beenotung/slnpm)
```typescript
import { createFairQueue } from 'fair-queue' // this library
import { MINUTE, SECOND } from '@beenotung/tslib/time' // external library (not included)
// Mock Date.now for testing
function mockTime(time: number) {
Date.now = () => time
}
// Create a fair queue with 5-minute initial duration and debug mode
const queue = createFairQueue<string>({
initial_duration: 5 * MINUTE,
debug: true,
})
const zero = Date.now()
// Set time to 1 minute after start
mockTime(zero + 1 * MINUTE)
// Enqueue tasks for user1 (4 tasks) and user2 (3 tasks)
queue.enqueue('user1', 'user1-task1')
queue.enqueue('user1', 'user1-task2')
queue.enqueue('user1', 'user1-task3')
queue.enqueue('user1', 'user1-task4')
queue.enqueue('user2', 'user2-task1')
queue.enqueue('user2', 'user2-task2')
queue.enqueue('user2', 'user2-task3')
// Dequeue tasks over time, simulating 1-second intervals
for (let i = 1; i <= 8; i++) {
mockTime(zero + 1 * MINUTE + i * SECOND)
console.log(queue.dequeue())
}
```
**Expected Output** (with `debug: true`):
```
dequeue { now: 1746012104949 }
{ user_id: 'user1', queue_size: 4, weight: 75250 }
{ user_id: 'user2', queue_size: 3, weight: 100333.33333333333 }
{ total_weight: 175583.3333333333, dice: 88826.2618780697 }
user2-task1
dequeue { now: 1746012105949 }
{ user_id: 'user1', queue_size: 4, weight: 75500 }
{ user_id: 'user2', queue_size: 2, weight: 500 }
{ total_weight: 76000, dice: 13869.930289720725 }
user1-task1
...
```
This example shows how `fair-queue` prioritizes `user2` initially due to a shorter queue (3 tasks vs. 4), with weights reflecting wait time and queue size.
## Typescript Signature
```typescript
export type FairQueueConfig = {
/** Initial duration (in milliseconds) for new users, e.g., 5 * 60 * 1000 for 5 minutes */
initial_duration: number
/** Enable debug logging (default: false) */
debug?: boolean
/** Selection mode: 'randomized' (default) or 'deterministic' */
mode?: 'randomized' | 'deterministic'
}
export function createFairQueue<Task>(config: FairQueueConfig): FairQueue<Task>
export class FairQueue<Task> {
constructor(config: FairQueueConfig)
/** Enqueue a task for a user */
enqueue(user_id: string | number, task: Task): void
/** Dequeue and return the next task based on weighted random selection */
dequeue(): Task | null
}
```
- `initial_duration`: Initial duration (ms) assigned to new users to boost their priority
- `mode`: Selection mode
- `'randomized'` (default): Weighted random selection for better perspective responsiveness under load
- `'deterministic'`: Always selects the user with highest weight
- `debug`: When `true`, logs queue sizes, weights, and selection details
## Notes
- **Weight Function**: Weight is calculated as `duration * (1 / queue_size)` where:
- `duration`: Time passed since last served (ms)
- `queue_size`: Number of pending tasks for the user
- **Selection Modes**:
- Randomized mode provides better overall responsiveness by giving all users a chance proportional to their weights
- Deterministic mode ensures the highest-weighted user always gets served next, useful for strict priority ordering
- **Abuse Mitigation**: Consider adding request pattern tracking (e.g., by IP) to prevent abuse through multiple small queues
## License
This project is licensed with [BSD-2-Clause](./LICENSE)
This is free, libre, and open-source software. It comes down to four essential freedoms [[ref]](https://seirdy.one/2021/01/27/whatsapp-and-the-domestication-of-users.html#fnref:2):
- The freedom to run the program as you wish, for any purpose
- The freedom to study how the program works, and change it so it does your computing as you wish
- The freedom to redistribute copies so you can help others
- The freedom to distribute copies of your modified versions to others