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(serve): leastConns and roundRobin balance strategies

+74
+1
src/serve/index.ts
··· 1 1 export type { ListenArgs, ListenOptions, Balance, ServerThreads, ServerThreadsOptions } from './types.ts'; 2 + export { leastConns, roundRobin } from './strategies.ts';
+30
src/serve/strategies.ts
··· 1 + import type { Balance } from './types.ts'; 2 + 3 + /** Pick the worker with the fewest active connections; ties go to the lowest index. */ 4 + export function leastConns(): Balance { 5 + return { 6 + pick(counters) { 7 + let bestIdx = 0; 8 + let bestCount = counters[0]; 9 + for (let i = 1; i < counters.length; i++) { 10 + if (counters[i] < bestCount) { 11 + bestIdx = i; 12 + bestCount = counters[i]; 13 + } 14 + } 15 + return bestIdx; 16 + }, 17 + }; 18 + } 19 + 20 + /** Cycle through worker indices in order, ignoring counts. */ 21 + export function roundRobin(): Balance { 22 + let cursor = 0; 23 + return { 24 + pick(counters) { 25 + const idx = cursor % counters.length; 26 + cursor = (cursor + 1) % counters.length; 27 + return idx; 28 + }, 29 + }; 30 + }
+43
test/serve/strategies.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { leastConns, roundRobin } from 'moroutine/serve'; 4 + 5 + describe('leastConns', () => { 6 + it('picks the index with the lowest count', () => { 7 + const b = leastConns(); 8 + assert.equal(b.pick([5, 2, 7]), 1); 9 + assert.equal(b.pick([0, 0, 0]), 0); // ties → first min 10 + assert.equal(b.pick([3, 3, 1]), 2); 11 + }); 12 + 13 + it('is stateless across calls', () => { 14 + const b = leastConns(); 15 + assert.equal(b.pick([1, 0, 0]), 1); 16 + assert.equal(b.pick([1, 0, 0]), 1); // same input → same result 17 + }); 18 + }); 19 + 20 + describe('roundRobin', () => { 21 + it('cycles through indices in order', () => { 22 + const b = roundRobin(); 23 + const counters = [0, 0, 0, 0]; 24 + assert.equal(b.pick(counters), 0); 25 + assert.equal(b.pick(counters), 1); 26 + assert.equal(b.pick(counters), 2); 27 + assert.equal(b.pick(counters), 3); 28 + assert.equal(b.pick(counters), 0); 29 + }); 30 + 31 + it('ignores counter values', () => { 32 + const b = roundRobin(); 33 + assert.equal(b.pick([100, 0, 0]), 0); // does not pick the idle one 34 + assert.equal(b.pick([0, 100, 0]), 1); 35 + }); 36 + 37 + it('each instance has independent state', () => { 38 + const b1 = roundRobin(); 39 + const b2 = roundRobin(); 40 + b1.pick([0, 0]); // b1 cursor: 1 41 + assert.equal(b2.pick([0, 0]), 0); // b2 starts fresh 42 + }); 43 + });