Offload functions to worker threads with shared memory primitives for Node.js.
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: roundRobin() and leastBusy() balancer factories

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+73 -1
+35
src/balancers.ts
··· 1 + import type { Balancer, WorkerHandle } from './runner.ts'; 2 + 3 + /** 4 + * Creates a round-robin balancer that cycles through workers in order. 5 + * @returns A fresh Balancer instance. 6 + */ 7 + export function roundRobin(): Balancer { 8 + let next = 0; 9 + return { 10 + select(workers: readonly WorkerHandle[]): WorkerHandle { 11 + const worker = workers[next % workers.length]; 12 + next++; 13 + return worker; 14 + }, 15 + }; 16 + } 17 + 18 + /** 19 + * Creates a least-busy balancer that picks the worker with the lowest activeCount. 20 + * Ties are broken by index (first wins). 21 + * @returns A fresh Balancer instance. 22 + */ 23 + export function leastBusy(): Balancer { 24 + return { 25 + select(workers: readonly WorkerHandle[]): WorkerHandle { 26 + let best = workers[0]; 27 + for (let i = 1; i < workers.length; i++) { 28 + if (workers[i].activeCount < best.activeCount) { 29 + best = workers[i]; 30 + } 31 + } 32 + return best; 33 + }, 34 + }; 35 + }
+2 -1
src/index.ts
··· 7 7 export { workers } from './worker-pool.ts'; 8 8 export { transfer } from './transfer.ts'; 9 9 export { assign } from './assign.ts'; 10 - export type { Runner, WorkerHandle, WorkerOptions } from './runner.ts'; 10 + export { roundRobin, leastBusy } from './balancers.ts'; 11 + export type { Balancer, Runner, WorkerHandle, WorkerOptions } from './runner.ts'; 11 12 export { 12 13 shared, 13 14 int8,
+36
test/balancers.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { roundRobin, leastBusy } from 'moroutine'; 4 + import type { WorkerHandle } from 'moroutine'; 5 + 6 + function mockHandle(activeCount: number): WorkerHandle { 7 + return { activeCount, thread: {} as any, exec: {} as any }; 8 + } 9 + 10 + describe('roundRobin()', () => { 11 + it('cycles through workers in order', () => { 12 + const b = roundRobin(); 13 + const handles = [mockHandle(0), mockHandle(0), mockHandle(0)]; 14 + const task = { id: 'test', args: [], uid: 0 } as any; 15 + assert.equal(b.select(handles, task), handles[0]); 16 + assert.equal(b.select(handles, task), handles[1]); 17 + assert.equal(b.select(handles, task), handles[2]); 18 + assert.equal(b.select(handles, task), handles[0]); 19 + }); 20 + }); 21 + 22 + describe('leastBusy()', () => { 23 + it('picks worker with lowest activeCount', () => { 24 + const b = leastBusy(); 25 + const handles = [mockHandle(3), mockHandle(1), mockHandle(2)]; 26 + const task = { id: 'test', args: [], uid: 0 } as any; 27 + assert.equal(b.select(handles, task), handles[1]); 28 + }); 29 + 30 + it('breaks ties by index', () => { 31 + const b = leastBusy(); 32 + const handles = [mockHandle(1), mockHandle(1), mockHandle(1)]; 33 + const task = { id: 'test', args: [], uid: 0 } as any; 34 + assert.equal(b.select(handles, task), handles[0]); 35 + }); 36 + });