fix(optimizer): убрать лишние поиски и расчёты маршрутов
This commit is contained in:
1 parent
057f0d352a
commit
4fee9e371f
8 files changed
+164
-9
No files matched your search
@@ -190,3 +190,12 @@ Pure child CPU1000: equal1217.5→15.6ms,varied7967.5→14.5ms,freshness10с tim
|
|||||||
16 main browser +2 повторных новых профиля после copy-уточнения прошли:1000JH и1000Exit отдельно (без опасного cross product), repetitions/two sites/1000sites/long80chars/unknown/empty/offline/disabled/503 cache/recovery.320px без overflow, caption≤80px, scoped WCAG axe passed. Non-incremental TypeScript до dev, full page/helper/test lint и окончательный production build прошли. ReUI API/validate_usage проверены; primitive/theme не изменены. Historical9 optimizer PNG восстановлены, новые docs/ux-ui/screens/optimizer-sites-{jump-host,exit-node}-320.png просмотрены.
|
16 main browser +2 повторных новых профиля после copy-уточнения прошли:1000JH и1000Exit отдельно (без опасного cross product), repetitions/two sites/1000sites/long80chars/unknown/empty/offline/disabled/503 cache/recovery.320px без overflow, caption≤80px, scoped WCAG axe passed. Non-incremental TypeScript до dev, full page/helper/test lint и окончательный production build прошли. ReUI API/validate_usage проверены; primitive/theme не изменены. Historical9 optimizer PNG восстановлены, новые docs/ux-ui/screens/optimizer-sites-{jump-host,exit-node}-320.png просмотрены.
|
||||||
|
|
||||||
Далее12.3.68 pure child CPU profile full-route product≤100000/timeout10s/memory cap; не грузить тяжёлый baseline в UI. Остаточные DFS worst-case/full-route memory/native selector и внешняя12.5 не объявляются принятыми. Общая main/context регрессия относится к ранее записанным HEAD; текущий footer delta проверен профильными16+2.
|
Далее12.3.68 pure child CPU profile full-route product≤100000/timeout10s/memory cap; не грузить тяжёлый baseline в UI. Остаточные DFS worst-case/full-route memory/native selector и внешняя12.5 не объявляются принятыми. Общая main/context регрессия относится к ранее записанным HEAD; текущий footer delta проверен профильными16+2.
|
||||||
|
|
||||||
|
|
||||||
|
## 12.3.69 — lookup leg и исключение ненужных расчётов без WAN
|
||||||
|
|
||||||
|
2026-10-07. WAN/JH map сохраняет первый matching leg, lookup вынесен из Exit loop. JH/Exit legs не строятся, если нет настроенного WAN: их не может использовать ни один full route. Порядок WAN/JH/Exit, сортировка/ties, raw source identity, all routes и unknown community indices сохранены.13pure tests прошли, включая1230 assignment oracle fixtures и новые проверки8route probe ownership/ties/zeroWAN.12browser (large homes/routes/observed/tables) passed; non-incremental typecheck до dev, targeted lint и production build успешны.
|
||||||
|
|
||||||
|
CPU final:1000routes13.3ms;100000(10×100×100)49.7ms;100000(100×10×100)44.0ms. До lookup ~284–329ms и267–287ms. Дополнительный zeroWAN baseline0×100×1000 строил100000unused JH/Exit legs:45.6ms/heapDelta24.6MB; после skip11.5ms/0.17MB. Новый profiler допускаетWAN0 и ограничивает также JH×Exit≤100000. HeapDelta не peak/retained memory; полный nonzero product по-прежнему хранится и сортируется в памяти. Evidence docs/ux-ui/acceptance/optimizer-routes-final-2026-10-07.json содержит source SHA256 и точные результаты.
|
||||||
|
|
||||||
|
После чтения реального producer обнаружены следующие вопросы достоверности:source btest выполняется на srcClient,TX/RX экспортируются без переименования,optimizer пока читаетTX какDL. srcInterface передаётся только after-ping, не bandwidthTest; поле не доказывает WAN скоростного теста. Producer parser/fallback отсутствующую скорость превращает в0, а explicit avg0 может заменяться mean series. Следующий12.3.70 фиксирует аудит с первичными RouterOS источниками и планирует точечные исправления данных/подписей. Команды/права/правила сетевых операций не менялись; real PG/RouterOS/integrations12.5 не выполнялись.
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# План реализации UX/UI по пятичасовым окнам
|
# План реализации UX/UI по пятичасовым окнам
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-07: 12.3.68 baseline full routes сохранён: pure product1000/100000 под timeout10s/memory256MB, все route IDs/unknown сохранены. Следующий12.3.69: заменить повторный wanJhLegs.find на map с first-match семантикой и доказать route/source/tie эквивалентность. Затем аудит направлений TX/RX и привязки wildcard-проб; внешняя12.5 остаётся.
|
Актуальное продолжение 2026-10-07: 12.3.69 завершён: first-match WAN/JH map и skip JH/Exit без WAN,13pure+12browser/type/lint/build.100000routes≈44–50ms; zeroWAN heapDelta24.6MB→0.17MB. Следующий12.3.70: зафиксировать аудит TX/RX, WAN-binding и missing→0 producer по RouterOS docs/code; затем адресные исправления, без изменения команд/правил сетевых операций. Внешняя12.5 отдельно.
|
||||||
|
|
||||||
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-04. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-04. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# UX/UI: журнал реализации
|
# UX/UI: журнал реализации
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-07: 12.3.68 baseline full routes сохранён: pure product1000/100000 под timeout10s/memory256MB, все route IDs/unknown сохранены. Следующий12.3.69: заменить повторный wanJhLegs.find на map с first-match семантикой и доказать route/source/tie эквивалентность. Затем аудит направлений TX/RX и привязки wildcard-проб; внешняя12.5 остаётся.
|
Актуальное продолжение 2026-10-07: 12.3.69 завершён: first-match WAN/JH map и skip JH/Exit без WAN,13pure+12browser/type/lint/build.100000routes≈44–50ms; zeroWAN heapDelta24.6MB→0.17MB. Следующий12.3.70: зафиксировать аудит TX/RX, WAN-binding и missing→0 producer по RouterOS docs/code; затем адресные исправления, без изменения команд/правил сетевых операций. Внешняя12.5 отдельно.
|
||||||
|
|
||||||
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения.
|
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения.
|
||||||
|
|
||||||
|
|||||||
@@ -774,3 +774,12 @@ Pure child CPU1000: equal1217.5→15.6ms,varied7967.5→14.5ms,freshness10с tim
|
|||||||
2026-10-07. Pure child process без API/DB/браузера/сети, product≤100000/dimensions1–1000/timeout10s/V8heap256MB.1000WAN×1JH×1Exit1000routes≈16.9ms/heapDelta1.8MB;10×100×100100000routes≈329.6ms/22.0MB;100×10×100≈266.7ms/27.1MB. Первый измеренный прогон; точные повторные значения и source SHA256 в docs/ux-ui/acceptance/optimizer-routes-baseline-2026-10-07.json. Heap delta не peak/retained memory, время не production p95/SLA. Проверены count/first-last IDs/bestFirst/сохранение отсутствующих наблюдений. ESLint скрипта прошёл.
|
2026-10-07. Pure child process без API/DB/браузера/сети, product≤100000/dimensions1–1000/timeout10s/V8heap256MB.1000WAN×1JH×1Exit1000routes≈16.9ms/heapDelta1.8MB;10×100×100100000routes≈329.6ms/22.0MB;100×10×100≈266.7ms/27.1MB. Первый измеренный прогон; точные повторные значения и source SHA256 в docs/ux-ui/acceptance/optimizer-routes-baseline-2026-10-07.json. Heap delta не peak/retained memory, время не production p95/SLA. Проверены count/first-last IDs/bestFirst/сохранение отсутствующих наблюдений. ESLint скрипта прошёл.
|
||||||
|
|
||||||
Baseline в UI не запускался. Повторный Array.find по1000wanJhLegs на каждую из100000комбинаций даёт лишние линейные сканирования. Следующий12.3.69: map lookup с сохранением первого matching leg и порядка routes/ties, профильный repeat. Полный product всё ещё хранится/сортируется в памяти; опасную произвольную размерность этот профиль не принимает. Далее аудит TX/RX и связи wildcard-пробы с конкретным WAN, прежде чем объявлять observed correctness закрытой. Внешняя12.5 отдельно.
|
Baseline в UI не запускался. Повторный Array.find по1000wanJhLegs на каждую из100000комбинаций даёт лишние линейные сканирования. Следующий12.3.69: map lookup с сохранением первого matching leg и порядка routes/ties, профильный repeat. Полный product всё ещё хранится/сортируется в памяти; опасную произвольную размерность этот профиль не принимает. Далее аудит TX/RX и связи wildcard-пробы с конкретным WAN, прежде чем объявлять observed correctness закрытой. Внешняя12.5 отдельно.
|
||||||
|
|
||||||
|
|
||||||
|
## 12.3.69 — lookup leg и исключение ненужных расчётов без WAN
|
||||||
|
|
||||||
|
2026-10-07. WAN/JH map сохраняет первый matching leg, lookup вынесен из Exit loop. JH/Exit legs не строятся, если нет настроенного WAN: их не может использовать ни один full route. Порядок WAN/JH/Exit, сортировка/ties, raw source identity, all routes и unknown community indices сохранены.13pure tests прошли, включая1230 assignment oracle fixtures и новые проверки8route probe ownership/ties/zeroWAN.12browser (large homes/routes/observed/tables) passed; non-incremental typecheck до dev, targeted lint и production build успешны.
|
||||||
|
|
||||||
|
CPU final:1000routes13.3ms;100000(10×100×100)49.7ms;100000(100×10×100)44.0ms. До lookup ~284–329ms и267–287ms. Дополнительный zeroWAN baseline0×100×1000 строил100000unused JH/Exit legs:45.6ms/heapDelta24.6MB; после skip11.5ms/0.17MB. Новый profiler допускаетWAN0 и ограничивает также JH×Exit≤100000. HeapDelta не peak/retained memory; полный nonzero product по-прежнему хранится и сортируется в памяти. Evidence docs/ux-ui/acceptance/optimizer-routes-final-2026-10-07.json содержит source SHA256 и точные результаты.
|
||||||
|
|
||||||
|
После чтения реального producer обнаружены следующие вопросы достоверности:source btest выполняется на srcClient,TX/RX экспортируются без переименования,optimizer пока читаетTX какDL. srcInterface передаётся только after-ping, не bandwidthTest; поле не доказывает WAN скоростного теста. Producer parser/fallback отсутствующую скорость превращает в0, а explicit avg0 может заменяться mean series. Следующий12.3.70 фиксирует аудит с первичными RouterOS источниками и планирует точечные исправления данных/подписей. Команды/права/правила сетевых операций не менялись; real PG/RouterOS/integrations12.5 не выполнялись.
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"baseHead": "057f0d352a58d7a4abe4dca18d64dd3d93a002d6",
|
||||||
|
"sourceSha256": "4f6f3b8b9c82efefdb82a2e02a241f3ca557cc1c5d689eaa079e20178f872947",
|
||||||
|
"runs": [
|
||||||
|
{
|
||||||
|
"wanCount": 1000,
|
||||||
|
"jhCount": 1,
|
||||||
|
"exitCount": 1,
|
||||||
|
"combinations": 1000,
|
||||||
|
"memoryCapMb": 256,
|
||||||
|
"timeoutMs": 10000,
|
||||||
|
"totalMs": 267,
|
||||||
|
"status": 0,
|
||||||
|
"timedOut": false,
|
||||||
|
"result": {
|
||||||
|
"elapsedMs": 13.307500000000005,
|
||||||
|
"heapDeltaBytes": 1925224,
|
||||||
|
"wanJhLegs": 1000,
|
||||||
|
"routes": 1000,
|
||||||
|
"firstId": "1-w0-2-3",
|
||||||
|
"lastId": "1-w999-2-3",
|
||||||
|
"bestMatchesFirst": true,
|
||||||
|
"missingObservations": true
|
||||||
|
},
|
||||||
|
"error": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wanCount": 10,
|
||||||
|
"jhCount": 100,
|
||||||
|
"exitCount": 100,
|
||||||
|
"combinations": 100000,
|
||||||
|
"memoryCapMb": 256,
|
||||||
|
"timeoutMs": 10000,
|
||||||
|
"totalMs": 303,
|
||||||
|
"status": 0,
|
||||||
|
"timedOut": false,
|
||||||
|
"result": {
|
||||||
|
"elapsedMs": 49.748999999999995,
|
||||||
|
"heapDeltaBytes": 27920816,
|
||||||
|
"wanJhLegs": 1000,
|
||||||
|
"routes": 100000,
|
||||||
|
"firstId": "1-w0-2-102",
|
||||||
|
"lastId": "1-w9-101-201",
|
||||||
|
"bestMatchesFirst": true,
|
||||||
|
"missingObservations": true
|
||||||
|
},
|
||||||
|
"error": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wanCount": 100,
|
||||||
|
"jhCount": 10,
|
||||||
|
"exitCount": 100,
|
||||||
|
"combinations": 100000,
|
||||||
|
"memoryCapMb": 256,
|
||||||
|
"timeoutMs": 10000,
|
||||||
|
"totalMs": 288,
|
||||||
|
"status": 0,
|
||||||
|
"timedOut": false,
|
||||||
|
"result": {
|
||||||
|
"elapsedMs": 43.97179999999997,
|
||||||
|
"heapDeltaBytes": 22567496,
|
||||||
|
"wanJhLegs": 1000,
|
||||||
|
"routes": 100000,
|
||||||
|
"firstId": "1-w0-2-12",
|
||||||
|
"lastId": "1-w99-11-111",
|
||||||
|
"bestMatchesFirst": true,
|
||||||
|
"missingObservations": true
|
||||||
|
},
|
||||||
|
"error": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wanCount": 0,
|
||||||
|
"jhCount": 100,
|
||||||
|
"exitCount": 1000,
|
||||||
|
"combinations": 0,
|
||||||
|
"memoryCapMb": 256,
|
||||||
|
"timeoutMs": 10000,
|
||||||
|
"totalMs": 278,
|
||||||
|
"status": 0,
|
||||||
|
"timedOut": false,
|
||||||
|
"result": {
|
||||||
|
"elapsedMs": 11.54419999999999,
|
||||||
|
"heapDeltaBytes": 168744,
|
||||||
|
"wanJhLegs": 0,
|
||||||
|
"routes": 0,
|
||||||
|
"bestMatchesFirst": true,
|
||||||
|
"missingObservations": true
|
||||||
|
},
|
||||||
|
"error": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -512,8 +512,17 @@ export function buildLiveOptimizerData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const wanJhById = new Map<string, Map<string, WanJhLeg>>()
|
||||||
|
for (const leg of wanJhLegs) {
|
||||||
|
let byHost = wanJhById.get(leg.wanId)
|
||||||
|
if (!byHost) { byHost = new Map(); wanJhById.set(leg.wanId, byHost) }
|
||||||
|
// Keep the first match, as the former Array.find did.
|
||||||
|
if (!byHost.has(leg.jhId)) byHost.set(leg.jhId, leg)
|
||||||
|
}
|
||||||
|
|
||||||
const jhExMap = new Map<string, JhExLeg>()
|
const jhExMap = new Map<string, JhExLeg>()
|
||||||
for (const jh of jumpHosts) {
|
// Without a configured WAN, no full route can consume a JH→Exit leg.
|
||||||
|
for (const jh of home.wans.length ? jumpHosts : []) {
|
||||||
for (const ex of exitNodes) {
|
for (const ex of exitNodes) {
|
||||||
const jhRow = byId.get(jh.id)
|
const jhRow = byId.get(jh.id)
|
||||||
const exRow = byId.get(ex.id)
|
const exRow = byId.get(ex.id)
|
||||||
@@ -544,10 +553,11 @@ export function buildLiveOptimizerData(
|
|||||||
const fullRoutes: FullRoute[] = []
|
const fullRoutes: FullRoute[] = []
|
||||||
for (const wan of home.wans) {
|
for (const wan of home.wans) {
|
||||||
for (const jh of jumpHosts) {
|
for (const jh of jumpHosts) {
|
||||||
|
const hw = wanJhById.get(wan.id)?.get(jh.id)
|
||||||
|
if (!hw) continue
|
||||||
for (const ex of exitNodes) {
|
for (const ex of exitNodes) {
|
||||||
const hw = wanJhLegs.find((l) => l.wanId === wan.id && l.jhId === jh.id)
|
|
||||||
const je = jhExMap.get(`${jh.id}::${ex.id}`)
|
const je = jhExMap.get(`${jh.id}::${ex.id}`)
|
||||||
if (!hw || !je) continue
|
if (!je) continue
|
||||||
const totalPing = hw.pingMs + je.pingMs
|
const totalPing = hw.pingMs + je.pingMs
|
||||||
const dl = Math.min(hw.dlMbps, je.dlMbps)
|
const dl = Math.min(hw.dlMbps, je.dlMbps)
|
||||||
const ul = Math.min(hw.ulMbps, je.ulMbps)
|
const ul = Math.min(hw.ulMbps, je.ulMbps)
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import {test} from "node:test"
|
||||||
|
import assert from "node:assert/strict"
|
||||||
|
import {buildLiveOptimizerData,DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS,type OptimizerApiServer,type RouteOptimizerSpeedProbe} from "./route-optimizer-data"
|
||||||
|
const common={host:"192.0.2.1",enabled:true,status:"online" as const,latency:10,country:"RU",site:"TEST",model:"CHR"}
|
||||||
|
const wans=Array.from({length:2},(_,i)=>({id:`w${i+1}`,name:`WAN${i+1}`,iface:`ether${i+1}`,isp:"ISP",ip:"198.51.100.1",maxDl:100,maxUl:100}))
|
||||||
|
const rows:OptimizerApiServer[]=[{...common,id:1,name:"Home",type:"home-router",wanUplinks:wans},...Array.from({length:2},(_,i)=>({...common,id:i+2,name:`Jump${i}`,type:"jump-host" as const})),...Array.from({length:2},(_,i)=>({...common,id:i+4,name:`Exit${i}`,type:"exit-node" as const}))]
|
||||||
|
const probe=(id:string,src:number,dst:number,iface:string):RouteOptimizerSpeedProbe=>({id,srcServerId:String(src),dstServerId:String(dst),srcInterface:iface,dstInterface:"",enabled:true,lastPingRttMs:10,lastPingLossPct:0,lastTxAvgMbps:100,lastRxAvgMbps:100,lastPingAt:"2026-10-07T00:00:00Z"})
|
||||||
|
|
||||||
|
test("full route keeps the matching WAN/JH and JH/Exit probe snapshots",()=>{
|
||||||
|
const probes=[...wans.flatMap(wan=>[2,3].map(jh=>probe(`wan-${wan.id}-${jh}`,1,jh,wan.iface))),...[2,3].flatMap(jh=>[4,5].map(exit=>probe(`exit-${jh}-${exit}`,jh,exit,"")))]
|
||||||
|
const entry=buildLiveOptimizerData(rows,[],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS,probes).homes[0]
|
||||||
|
assert.equal(entry.fullRoutes.length,8);assert.equal(new Set(entry.fullRoutes.map(route=>route.id)).size,8)
|
||||||
|
for(const route of entry.fullRoutes){
|
||||||
|
assert.equal(route.hw.observed?.probeId,`wan-${route.wan.id}-${route.jh.id}`)
|
||||||
|
assert.equal(route.je.observed?.probeId,`exit-${route.jh.id}-${route.exit.id}`)
|
||||||
|
assert.equal(route.hw.wanId,route.wan.id);assert.equal(route.hw.jhId,route.jh.id)
|
||||||
|
assert.equal(route.je.jhId,route.jh.id);assert.equal(route.je.exitId,route.exit.id)
|
||||||
|
}
|
||||||
|
assert.equal(entry.bestRoute,entry.fullRoutes[0])
|
||||||
|
})
|
||||||
|
|
||||||
|
test("equal modeled routes keep original WAN/JH/Exit order and unknown observations",()=>{
|
||||||
|
const entry=buildLiveOptimizerData(rows,[],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS).homes[0]
|
||||||
|
assert.deepEqual(entry.fullRoutes.map(route=>route.id),wans.flatMap(wan=>[2,3].flatMap(jh=>[4,5].map(exit=>`1-${wan.id}-${jh}-${exit}`))))
|
||||||
|
assert.ok(entry.fullRoutes.every(route=>route.hw.observed?.probeId===null&&route.je.observed?.probeId===null))
|
||||||
|
})
|
||||||
|
|
||||||
|
test("no WAN leaves routes and community indices unknown without generating JH/Exit legs",()=>{
|
||||||
|
const inventory=rows.map(row=>row.id===1?{...row,wanUplinks:[]}:row)
|
||||||
|
const entry=buildLiveOptimizerData(inventory,[{serverId:"1",rules:[{id:"a",community:"65001:1",gateway:"192.0.2.1",gatewayTunnelId:"",description:""}]}],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS).homes[0]
|
||||||
|
assert.deepEqual(entry.wanJhLegs,[]);assert.deepEqual(entry.fullRoutes,[]);assert.equal(entry.bestRoute,null)
|
||||||
|
assert.equal(entry.commRecs.length,1);assert.equal(entry.commRecs[0].current,null);assert.equal(entry.commRecs[0].recommended,null)
|
||||||
|
})
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
// Bounded pure full-route calculation: no API, database, network or browser.
|
// Bounded pure full-route calculation: no API, database, network or browser.
|
||||||
import {spawnSync} from 'node:child_process'
|
import {spawnSync} from 'node:child_process'
|
||||||
import assert from 'node:assert/strict'
|
import assert from 'node:assert/strict'
|
||||||
const [wanCount,jhCount,exitCount]=process.argv.slice(2).length?process.argv.slice(2).map(Number):[10,100,100]
|
const args=process.argv.slice(2)
|
||||||
|
if(args.length&&args.length!==3)throw new Error('Provide exactly three dimensions')
|
||||||
|
const [wanCount,jhCount,exitCount]=args.length?args.map(Number):[10,100,100]
|
||||||
const combinations=wanCount*jhCount*exitCount
|
const combinations=wanCount*jhCount*exitCount
|
||||||
if([wanCount,jhCount,exitCount].some(n=>!Number.isInteger(n)||n<1||n>1000)||!Number.isFinite(combinations)||combinations>100000)throw new Error('Three dimensions 1..1000; product <=100000')
|
if([wanCount,jhCount,exitCount].some(n=>!Number.isInteger(n)||n<0||n>1000)||jhCount<1||exitCount<1||!Number.isFinite(combinations)||combinations>100000||jhCount*exitCount>100000)throw new Error('WAN0..1000, JH/Exit1..1000; products <=100000')
|
||||||
const code=`
|
const code=`
|
||||||
const fs=require('node:fs'),ts=require('typescript');
|
const fs=require('node:fs'),ts=require('typescript');
|
||||||
const source=ts.transpileModule(fs.readFileSync('lib/route-optimizer-data.ts','utf8'),{compilerOptions:{module:ts.ModuleKind.CommonJS,target:ts.ScriptTarget.ES2022}}).outputText;
|
const source=ts.transpileModule(fs.readFileSync('lib/route-optimizer-data.ts','utf8'),{compilerOptions:{module:ts.ModuleKind.CommonJS,target:ts.ScriptTarget.ES2022}}).outputText;
|
||||||
@@ -12,12 +14,12 @@ const {buildLiveOptimizerData,DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS}=mod.exports;
|
|||||||
const common={host:'192.0.2.1',enabled:true,status:'online',latency:10,country:'RU',site:'TEST',model:'CHR'};
|
const common={host:'192.0.2.1',enabled:true,status:'online',latency:10,country:'RU',site:'TEST',model:'CHR'};
|
||||||
const rows=[{...common,id:1,name:'Home',type:'home-router',wanUplinks:Array.from({length:${wanCount}},(_,i)=>({id:'w'+i,name:'WAN'+(i+1),iface:'ether'+(i+1),isp:'ISP',ip:'198.51.100.1',maxDl:100,maxUl:100}))},...Array.from({length:${jhCount}},(_,i)=>({...common,id:i+2,name:'Jump'+i,type:'jump-host'})),...Array.from({length:${exitCount}},(_,i)=>({...common,id:i+2+${jhCount},name:'Exit'+i,type:'exit-node'}))];
|
const rows=[{...common,id:1,name:'Home',type:'home-router',wanUplinks:Array.from({length:${wanCount}},(_,i)=>({id:'w'+i,name:'WAN'+(i+1),iface:'ether'+(i+1),isp:'ISP',ip:'198.51.100.1',maxDl:100,maxUl:100}))},...Array.from({length:${jhCount}},(_,i)=>({...common,id:i+2,name:'Jump'+i,type:'jump-host'})),...Array.from({length:${exitCount}},(_,i)=>({...common,id:i+2+${jhCount},name:'Exit'+i,type:'exit-node'}))];
|
||||||
const before=process.memoryUsage().heapUsed,start=performance.now();const entry=buildLiveOptimizerData(rows,[],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS).homes[0];
|
const before=process.memoryUsage().heapUsed,start=performance.now();const entry=buildLiveOptimizerData(rows,[],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS).homes[0];
|
||||||
console.log(JSON.stringify({elapsedMs:performance.now()-start,heapDeltaBytes:process.memoryUsage().heapUsed-before,wanJhLegs:entry.wanJhLegs.length,routes:entry.fullRoutes.length,firstId:entry.fullRoutes[0]?.id,lastId:entry.fullRoutes.at(-1)?.id,bestMatchesFirst:entry.bestRoute===entry.fullRoutes[0],missingObservations:entry.fullRoutes.every(route=>route.hw.observed.probeId===null&&route.je.observed.probeId===null)}));
|
console.log(JSON.stringify({elapsedMs:performance.now()-start,heapDeltaBytes:process.memoryUsage().heapUsed-before,wanJhLegs:entry.wanJhLegs.length,routes:entry.fullRoutes.length,firstId:entry.fullRoutes[0]?.id,lastId:entry.fullRoutes.at(-1)?.id,bestMatchesFirst:entry.bestRoute===(entry.fullRoutes[0]??null),missingObservations:entry.fullRoutes.every(route=>route.hw.observed.probeId===null&&route.je.observed.probeId===null)}));
|
||||||
`;
|
`;
|
||||||
const start=performance.now(),r=spawnSync(process.execPath,['--max-old-space-size=256','-e',code],{cwd:process.cwd(),timeout:10000,encoding:'utf8',windowsHide:true,maxBuffer:16384});
|
const start=performance.now(),r=spawnSync(process.execPath,['--max-old-space-size=256','-e',code],{cwd:process.cwd(),timeout:10000,encoding:'utf8',windowsHide:true,maxBuffer:16384});
|
||||||
const result=r.status===0?JSON.parse(r.stdout):null;
|
const result=r.status===0?JSON.parse(r.stdout):null;
|
||||||
const profile={wanCount,jhCount,exitCount,combinations,memoryCapMb:256,timeoutMs:10000,totalMs:Math.round(performance.now()-start),status:r.status,timedOut:r.error?.code==='ETIMEDOUT',result,error:r.stderr.slice(0,2000)};
|
const profile={wanCount,jhCount,exitCount,combinations,memoryCapMb:256,timeoutMs:10000,totalMs:Math.round(performance.now()-start),status:r.status,timedOut:r.error?.code==='ETIMEDOUT',result,error:r.stderr.slice(0,2000)};
|
||||||
console.log(JSON.stringify(profile,null,2));
|
console.log(JSON.stringify(profile,null,2));
|
||||||
if(result){assert.equal(result.routes,combinations);assert.equal(result.wanJhLegs,wanCount*jhCount);assert.equal(result.firstId,`1-w0-2-${jhCount+2}`);assert.equal(result.lastId,`1-w${wanCount-1}-${jhCount+1}-${jhCount+exitCount+1}`);assert.equal(result.bestMatchesFirst,true);assert.equal(result.missingObservations,true)}
|
if(result){assert.equal(result.routes,combinations);assert.equal(result.wanJhLegs,wanCount*jhCount);assert.equal(result.firstId,combinations?`1-w0-2-${jhCount+2}`:undefined);assert.equal(result.lastId,combinations?`1-w${wanCount-1}-${jhCount+1}-${jhCount+exitCount+1}`:undefined);assert.equal(result.bestMatchesFirst,true);assert.equal(result.missingObservations,true)}
|
||||||
if(r.error&&!profile.timedOut)throw r.error;
|
if(r.error&&!profile.timedOut)throw r.error;
|
||||||
if(r.status!==0&&!profile.timedOut)process.exitCode=1;
|
if(r.status!==0&&!profile.timedOut)process.exitCode=1;
|
||||||
Reference in new issue
Block a user