/* * 브로드캐스팅 주기는 **재 본 비용에 맞춘다** (2026-08-21). * * 한 번의 브로드캐스팅은 상태 크기에 비례한다(실측: 물품 2,400 개 트윈 하나가 4.5ms). 트윈이 스무 개면 * 200ms 주기에서 90ms 가 브로드캐스팅이고, 그 시간에는 HTTP 도 구독도 서지 못한다. 그래서 브로드캐스팅이 주기의 * 일정 몫을 넘게 쓰면 주기를 늘리고, 여유가 생기면 되돌린다. * * 지키는 것: * ① 무거우면 늘린다 — 상한까지만. * ② 가벼워지면 되돌린다 — 원래 주기 아래로는 내려가지 않는다. * ③ 경계에서 늘리고 줄이기를 반복하지 않는다(문턱이 둘이다). * ④ **늘렸다는 사실을 센다** — 알리지 않고 늦추면 사람은 화면이 낡은 이유를 알 수 없다. */ import { test, beforeEach } from 'node:test' import assert from 'node:assert/strict' import { createRequire } from 'node:module' const req = createRequire(import.meta.url) const { TwinEngine } = req('../dist-server/engine/twin-engine.js') /** 주기 조정만 부른다 — 실제 브로드캐스팅(pubsub·DB)은 이 판정과 무관하다. */ const adjust = (flushMs: number) => (TwinEngine as any).adjustBroadcastPeriod(flushMs) beforeEach(() => { TwinEngine.broadcastPeriodMs = TwinEngine.BROADCAST_COALESCE_MS TwinEngine.broadcastBackoffs = 0 }) test('브로드캐스팅이 주기의 몫을 넘게 쓰면 주기를 늘린다 — 상한까지만', () => { const base = TwinEngine.BROADCAST_COALESCE_MS adjust(base) // 주기를 통째로 쓴 셈 assert.equal(TwinEngine.broadcastPeriodMs, base * 2) for (let i = 0; i < 10; i++) adjust(TwinEngine.broadcastPeriodMs) assert.equal(TwinEngine.broadcastPeriodMs, TwinEngine.BROADCAST_MAX_COALESCE_MS, '상한 위로는 올라가지 않는다') }) test('늘렸다는 사실을 센다 — 알리지 않고 늦추지 않는다', () => { adjust(TwinEngine.BROADCAST_COALESCE_MS) assert.equal(TwinEngine.broadcastBackoffs, 1) }) test('가벼워지면 되돌린다 — 원래 주기 아래로는 내려가지 않는다', () => { adjust(TwinEngine.BROADCAST_COALESCE_MS) // 200 → 400 assert.ok(TwinEngine.broadcastPeriodMs > TwinEngine.BROADCAST_COALESCE_MS) for (let i = 0; i < 10; i++) adjust(0.5) assert.equal(TwinEngine.broadcastPeriodMs, TwinEngine.BROADCAST_COALESCE_MS) }) test('경계에서 늘리고 줄이기를 반복하지 않는다 — 문턱이 둘이다', () => { const base = TwinEngine.BROADCAST_COALESCE_MS /* 늘리는 문턱(몫)과 되돌리는 문턱(몫의 절반) 사이의 값 — 어느 쪽도 아니어야 한다. */ const between = base * TwinEngine.BROADCAST_LOAD_RATIO * 0.75 for (let i = 0; i < 5; i++) adjust(between) assert.equal(TwinEngine.broadcastPeriodMs, base, '문턱 사이의 값은 주기를 바꾸지 않는다') assert.equal(TwinEngine.broadcastBackoffs, 0) })