chore(ux): измерить матрицу WAN и расчёт назначения проб
This commit is contained in:
1 parent
28e2d38ee6
commit
b3da4216e6
6 files changed
+65
-2
No files matched your search
@@ -117,3 +117,10 @@ Statistics pivot в backend/src/services/statistics-aggregate.ts огранич
|
||||
buildLiveOptimizerData/assignProbesForWanJh делает DFS, когда WAN<=7 и WAN<=pool.length. Лимита pool нет. scoreProbeForWanIface допускает srcInterface="" для любого WAN; при7WAN/20 подходящих разных probes полное дерево содержит20P7=390700800 полных назначений (вывод из кода, не измеренный benchmark). Не запускать этот случай через Next/UI: сначала отдельный чистый CPU-профиль в дочернем процессе с жёстким timeout, без DB/RouterOS. Стратегию назначения/оптимальность нельзя менять без доказательства эквивалентности на малых наборах; простое обрезание pool может скрыть лучшее назначение.
|
||||
|
||||
commRecs строятся через filter(sourceServerId).slice(0,24), а HomeCard подписывает BGP community только длиной среза. Нужны полный source count/явное сообщение24 изN; нельзя выдавать24 за весь каталог или менять правила применения. Локальная вкладка карточки сбрасывается при pagination, native OSPF select1000 options остаётся. Общая main/context после delta и реальная внешняя12.5 ещё не закрыты. Этот блок — карта приёмки, не доказательство нагрузочной готовности. Product build a16bfae уже passed; для документации повтор не нужен. Следующий12.3.59 — указанные bounded profiles/CPU timeout. Локальный коммит без push.
|
||||
|
||||
|
||||
## 12.3.59 — отдельные профили матрицы и CPU
|
||||
|
||||
2026-10-04. Live API подменён строгими DTO, без Exit и API mutations. Два браузерных профиля прошли: WAN1000×JH1 — 1000 строк,4 столбца,28019 DOM таблицы,28712 DOM страницы,4977мс dev-ready,108917байт; WAN10×JH100 — 10 строк,103 столбца,9803 DOM таблицы,10595 DOM страницы,2258мс dev-ready,16593байт. Время dev измеряет также загрузку/компиляцию и не является production benchmark.
|
||||
|
||||
Чистый расчёт7WAN×20 wildcard probes воспроизводится scripts/performance/optimizer-probes-profile.mjs, в отдельном child process с жёстким timeout10000мс. Расчёт не завершился за timeout; тяжёлый DFS нельзя воспроизводить в UI. Нет DB/API/сетевых операций. Следующий12.3.60 — страницы50WAN и ограниченное окно JH, поиск по полному каталогу, проверка обновления/сжатия и320px. Семантику назначения probes этим блоком не меняем.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# План реализации UX/UI по пятичасовым окнам
|
||||
|
||||
Актуальное продолжение 2026-10-04: 12.3.58 сохранён — статический аудит WAN matrix/CPU назначения probes/среза24 commRecs. Следующий12.3.59: bounded profile1000 WAN×1JH и10WAN×100JH без Exit, отдельно ограниченный по времени CPU-профиль7WAN×20 wildcard probes. Не запускать тяжёлый DFS через UI. Последний UI-коммит a16bfae; общая регрессия после delta/внешняя12.5 остаются.
|
||||
Актуальное продолжение 2026-10-04: 12.3.59 завершён: отдельные профили WAN1000×JH1, WAN10×JH100 и CPU7WAN×20 probes. Следующий12.3.60: ограничить отображение WAN/JH без среза исходных данных; тяжёлый DFS пока остаётся отдельным риском. Общая регрессия после delta и внешняя12.5 остаются.
|
||||
|
||||
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-04. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# UX/UI: журнал реализации
|
||||
|
||||
Актуальное продолжение 2026-10-04: 12.3.58 сохранён — статический аудит WAN matrix/CPU назначения probes/среза24 commRecs. Следующий12.3.59: bounded profile1000 WAN×1JH и10WAN×100JH без Exit, отдельно ограниченный по времени CPU-профиль7WAN×20 wildcard probes. Не запускать тяжёлый DFS через UI. Последний UI-коммит a16bfae; общая регрессия после delta/внешняя12.5 остаются.
|
||||
Актуальное продолжение 2026-10-04: 12.3.59 завершён: отдельные профили WAN1000×JH1, WAN10×JH100 и CPU7WAN×20 probes. Следующий12.3.60: ограничить отображение WAN/JH без среза исходных данных; тяжёлый DFS пока остаётся отдельным риском. Общая регрессия после delta и внешняя12.5 остаются.
|
||||
|
||||
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения.
|
||||
|
||||
|
||||
@@ -669,3 +669,10 @@ ReUI client pagination50 только раскрытого списка, autoRes
|
||||
buildLiveOptimizerData/assignProbesForWanJh делает DFS, когда WAN<=7 и WAN<=pool.length. Лимита pool нет. scoreProbeForWanIface допускает srcInterface="" для любого WAN; при7WAN/20 подходящих разных probes полное дерево содержит20P7=390700800 полных назначений (вывод из кода, не измеренный benchmark). Не запускать этот случай через Next/UI: сначала отдельный чистый CPU-профиль в дочернем процессе с жёстким timeout, без DB/RouterOS. Стратегию назначения/оптимальность нельзя менять без доказательства эквивалентности на малых наборах; простое обрезание pool может скрыть лучшее назначение.
|
||||
|
||||
commRecs строятся через filter(sourceServerId).slice(0,24), а HomeCard подписывает BGP community только длиной среза. Нужны полный source count/явное сообщение24 изN; нельзя выдавать24 за весь каталог или менять правила применения. Локальная вкладка карточки сбрасывается при pagination, native OSPF select1000 options остаётся. Общая main/context после delta и реальная внешняя12.5 ещё не закрыты. Этот блок — карта приёмки, не доказательство нагрузочной готовности. Product build a16bfae уже passed; для документации повтор не нужен. Следующий12.3.59 — указанные bounded profiles/CPU timeout. Локальный коммит без push.
|
||||
|
||||
|
||||
## 12.3.59 — отдельные профили матрицы и CPU
|
||||
|
||||
2026-10-04. Live API подменён строгими DTO, без Exit и API mutations. Два браузерных профиля прошли: WAN1000×JH1 — 1000 строк,4 столбца,28019 DOM таблицы,28712 DOM страницы,4977мс dev-ready,108917байт; WAN10×JH100 — 10 строк,103 столбца,9803 DOM таблицы,10595 DOM страницы,2258мс dev-ready,16593байт. Время dev измеряет также загрузку/компиляцию и не является production benchmark.
|
||||
|
||||
Чистый расчёт7WAN×20 wildcard probes воспроизводится scripts/performance/optimizer-probes-profile.mjs, в отдельном child process с жёстким timeout10000мс. Расчёт не завершился за timeout; тяжёлый DFS нельзя воспроизводить в UI. Нет DB/API/сетевых операций. Следующий12.3.60 — страницы50WAN и ограниченное окно JH, поиск по полному каталогу, проверка обновления/сжатия и320px. Семантику назначения probes этим блоком не меняем.
|
||||
@@ -0,0 +1,19 @@
|
||||
// Pure calculation in a child process: no API, database, browser or network operations.
|
||||
import { spawnSync } from 'node:child_process'
|
||||
const code = `
|
||||
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 mod={exports:{}};new Function('exports','require','module',source)(mod.exports,require,mod);
|
||||
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 rows=[{...common,id:1,name:'Home',type:'home-router',wanUplinks:Array.from({length:7},(_,i)=>({id:'wan'+i,name:'WAN'+i,isp:'ISP',iface:'ether'+i,ip:'198.51.100.1',maxDl:100,maxUl:100}))},{...common,id:2,name:'Jump',type:'jump-host'}];
|
||||
const probes=Array.from({length:20},(_,i)=>({id:'p'+i,srcServerId:'1',dstServerId:'2',srcInterface:'',dstInterface:'',enabled:true,lastPingRttMs:10,lastPingLossPct:0,lastTxAvgMbps:100,lastRxAvgMbps:100,lastPingAt:null}));
|
||||
const start=performance.now();const result=buildLiveOptimizerData(rows,[],DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS,probes);
|
||||
console.log(JSON.stringify({elapsedMs:performance.now()-start,legs:result.homes[0].wanJhLegs.length}));
|
||||
`;
|
||||
const start=performance.now();
|
||||
const result=spawnSync(process.execPath,['-e',code],{cwd:process.cwd(),timeout:10000,encoding:'utf8',windowsHide:true});
|
||||
const profile={wanCount:7,probeCount:20,timeoutMs:10000,elapsedMs:Math.round(performance.now()-start),status:result.status,timedOut:result.error?.code==='ETIMEDOUT',result:result.stdout.trim(),error:result.stderr.trim()};
|
||||
console.log(JSON.stringify(profile,null,2));
|
||||
if(result.error && !profile.timedOut) throw result.error;
|
||||
if(result.status !== 0 && !profile.timedOut) process.exitCode=1;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { test, expect } from "@playwright/test"
|
||||
import { writeFileSync } from "node:fs"
|
||||
import { optimizerServersSchema } from "../../lib/route-optimizer-response"
|
||||
|
||||
for (const [wanCount,jhCount] of [[1000,1],[10,100]]) {
|
||||
test(`Матрица WAN${wanCount} × JH${jhCount} без Exit`, async ({page},info) => {
|
||||
test.setTimeout(90000)
|
||||
const common = { host: "192.0.2.1", enabled: true, status: "online", latency: 10, country: "RU", site: "TEST", model: "CHR" }
|
||||
const servers = optimizerServersSchema.parse([
|
||||
{ ...common, id: 1, name: "Домашний роутер", type: "home-router", wanUplinks: Array.from({ length: wanCount }, (_, i) => ({ id: `wan${i+1}`, name: `WAN${i+1}`, isp: "ISP", iface: `ether${i+1}`, ip: `198.51.100.${i+1}`, maxDl: 100, maxUl: 100 })) },
|
||||
...Array.from({ length: jhCount }, (_, i) => ({ ...common, id: i+2, name: `Jump ${i+1}`, type: "jump-host" })),
|
||||
])
|
||||
|
||||
const writes: string[] = []
|
||||
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
|
||||
await page.route("**/api/**", r => {
|
||||
const path = new URL(r.request().url()).pathname
|
||||
|
||||
if (r.request().method() !== "GET" && !path.endsWith("/preview")) writes.push(path)
|
||||
return r.fulfill({ json: path === "/api/auth/config" ? { required: false } : path === "/api/servers" ? servers : path === "/api/filters/rules" ? { rulesets: [] } : path === "/api/uptime/speed-probes" ? { probes: [] } : path === "/api/ospf/catalog" ? { interfaces: [], instances: [], neighbors: [], bfdSessions: [], routes: [], failures: [] } : path.endsWith("/ospf/optimize/preview") ? { serverId: 1, serverName: "Домашний роутер", pingWeight: 60, interfacesTotal: 0, changedCount: 0, unchangedCount: 0, interfaces: [], changes: [] } : {} })
|
||||
})
|
||||
|
||||
const started=Date.now(); await page.goto("/route-optimizer?server=1&keep=1")
|
||||
const table=page.getByRole("table").filter({hasText:"WAN-аплинк"})
|
||||
await expect(table.locator("tr[data-row-id]")).toHaveCount(wanCount)
|
||||
const profile={wanCount,jhCount,bytes:Buffer.byteLength(JSON.stringify(servers)),rows:await table.locator("tr[data-row-id]").count(),columns:await table.getByRole("columnheader").count(),tableNodes:await table.locator("*").count(),pageNodes:await page.locator("*").count(),readyMs:Date.now()-started}
|
||||
console.log("WAN matrix",JSON.stringify(profile)); writeFileSync(info.outputPath("matrix-profile.json"),JSON.stringify(profile,null,2))
|
||||
expect(writes).toEqual([])
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user