fix(traffic): согласовать опрос между процессами backend

This commit is contained in:
Denozordec committed 2026-10-13 14:35:00 +07:00
1 parent a68bd61e58
commit b525e70655
19 files changed
+1411 -19

No files matched your search

+1 -1
View File
@@ -19,7 +19,7 @@
"test:exec": "tsx src/services/mikrotik-logs.test.ts && tsx src/routes/exec.test.ts && tsx src/plugins/auth.exec.test.ts",
"test:auth": "tsx src/lib/permissions.test.ts && tsx src/plugins/auth.smoke.test.ts && tsx src/plugins/auth.ospf-optimizer.test.ts",
"test:wireguard": "npx tsx src/services/wireguard-config.test.ts && npx tsx src/services/wireguard-ipv6.test.ts && npx tsx --test src/services/wireguard-import.test.ts src/services/wireguard-import-apply.test.ts src/services/wireguard-import-revision.test.ts src/services/wireguard-delete.test.ts && npx tsx src/routes/wireguard-import.test.ts",
"test:traffic-rate": "tsx src/services/traffic-rate.test.ts && tsx src/services/traffic-observation.test.ts && tsx src/services/traffic-freshness.test.ts && tsx src/services/traffic-live.test.ts && tsx src/services/traffic-observation-sql.test.ts && tsx src/services/traffic-history-sql.test.ts && tsx src/services/traffic-live-sql.test.ts && tsx src/services/traffic-volume-sql.test.ts && tsx src/services/traffic-binding-status.test.ts && tsx src/services/traffic-status-sql.test.ts && tsx src/services/traffic-gap.test.ts && tsx src/services/traffic-identity.test.ts && tsx src/services/traffic-membership.test.ts && tsx src/services/traffic-aggregate-peak.test.ts && tsx src/services/traffic-users-cache.test.ts",
"test:traffic-rate": "tsx src/services/traffic-rate.test.ts && tsx src/services/traffic-observation.test.ts && tsx src/services/traffic-freshness.test.ts && tsx src/services/traffic-live.test.ts && tsx src/services/traffic-observation-sql.test.ts && tsx src/services/traffic-history-sql.test.ts && tsx src/services/traffic-live-sql.test.ts && tsx src/services/traffic-volume-sql.test.ts && tsx src/services/traffic-binding-status.test.ts && tsx src/services/traffic-status-sql.test.ts && tsx src/services/traffic-gap.test.ts && tsx src/services/traffic-identity.test.ts && tsx src/services/traffic-membership.test.ts && tsx src/services/traffic-aggregate-peak.test.ts && tsx src/services/traffic-users-cache.test.ts && tsx src/services/traffic-collection-lock.test.ts && tsx src/services/traffic-concurrency-sql.test.ts",
"test:traffic-flow": "tsx src/routes/traffic-flow-query.test.ts && tsx src/services/traffic-flow-month.test.ts && tsx src/services/traffic-flow-parse.test.ts && tsx src/services/traffic-flow-ipv6-pipeline.test.ts && tsx src/services/traffic-flow-map-exporter.test.ts && tsx src/services/traffic-flow-ifaces.test.ts && tsx src/services/traffic-flow-ifindex.test.ts && tsx src/services/traffic-flow-dedup.test.ts && tsx src/services/traffic-flow-planes.test.ts && tsx src/services/traffic-flow-ip.test.ts && tsx src/services/traffic-flow-dest.test.ts && tsx src/services/traffic-flow-classify.test.ts && tsx src/services/traffic-flow-ripe.test.ts && tsx src/services/traffic-flow-brands.test.ts && tsx src/services/traffic-flow-ingest.test.ts && tsx src/services/traffic-flow-analytics.test.ts && tsx src/services/traffic-flow-map-hops.test.ts && tsx src/services/traffic-flow-purge.test.ts && tsx src/services/traffic-flow-geoip.test.ts && tsx src/services/traffic-flow-facts.test.ts && tsx src/services/traffic-flow-facts-filter.test.ts && tsx src/services/traffic-flow-facts-rebuild.test.ts && tsx src/services/statistics-aggregate.test.ts",
"test:users": "tsx src/modules/users/iface-type.test.ts && tsx src/modules/users/bindings.test.ts && tsx src/modules/users/interface-inventory.test.ts && tsx src/routes/users-catalog.test.ts",
"test:pg": "tsx src/db/pg-pool.test.ts && tsx src/db/sql-bind.test.ts && tsx src/db/sqlite-json.test.ts && tsx src/db/traffic-flags.test.ts && tsx src/db/pg-schema.test.ts && tsx src/services/config-revisions.test.ts",
@@ -0,0 +1,17 @@
import assert from 'node:assert/strict'
import {withTrafficCollectionLock} from './traffic-collection-lock.js'
for(const state of ['busy','missing','ok','run-error','unlock-false','unlock-error','acquire-error']){
let runs=0,released:boolean|undefined,queries=0
const client={query:async()=>{
queries++
if(queries===1){if(state==='acquire-error')throw Error('connectivity');return {rows:state==='missing'?[]:[{acquired:state!=='busy'}]}}
if(state==='unlock-error')throw Error('unlock')
return {rows:[{released:state!=='unlock-false'}]}
},release:(destroy?:boolean)=>{released=destroy}}
const run=()=>withTrafficCollectionLock(async()=>client,async()=>{runs++;if(state==='run-error')throw Error('run');return 42})
if(['missing','run-error','acquire-error'].includes(state))await assert.rejects(run)
else {const result=await run();assert.equal(result.acquired,state!=='busy');if(result.acquired)assert.equal(result.value,42)}
assert.equal(runs,['busy','missing','acquire-error'].includes(state)?0:1)
assert.equal(released,['missing','unlock-false','unlock-error','acquire-error'].includes(state))
}
console.log('traffic lock: busy/no work/acquisition uncertainty/run failure/unlock failure/destruction passed')
@@ -0,0 +1,24 @@
type Client = {
query: (sql:string,params:number[])=>Promise<{rows:Record<string,unknown>[]}>
release: (destroy?:boolean)=>void
}
export const TRAFFIC_COLLECTION_LOCK_KEY=8_723_105
/** Coordinate collectors in the same database without queueing stale polling runs. */
export async function withTrafficCollectionLock<T>(connect:()=>Promise<Client>,run:()=>Promise<T>):Promise<{acquired:false}|{acquired:true;value:T}> {
const client=await connect()
let owned=false,destroy=true
try {
const result=await client.query('SELECT pg_try_advisory_lock($1) AS acquired',[TRAFFIC_COLLECTION_LOCK_KEY])
if(result.rows[0]?.acquired===false){destroy=false;return {acquired:false}}
if(result.rows[0]?.acquired!==true)throw Error('Блокировка опроса трафика не подтверждена')
owned=true;destroy=false
return {acquired:true,value:await run()}
}finally{
if(owned){
try{destroy=(await client.query('SELECT pg_advisory_unlock($1) AS released',[TRAFFIC_COLLECTION_LOCK_KEY])).rows[0]?.released!==true}
catch{destroy=true}
}
client.release(destroy)
}
}
+16 -3
View File
@@ -1,5 +1,6 @@
import { and, asc, desc, eq, gte } from "drizzle-orm"
import { db } from "../db/index.js"
import { db, pool } from "../db/index.js"
import { withTrafficCollectionLock } from "./traffic-collection-lock.js"
import { servers, trafficSamples, trafficSettings } from "../db/schema.js"
import { decodeTrafficSampleFlags, encodeTrafficFlags, trafficCounterContinuity } from "../db/traffic-flags.js"
import type { TrafficRunSnapshot } from "../types/scheduler-run-snapshot.js"
@@ -115,6 +116,20 @@ export async function collectTrafficOnce(): Promise<TrafficRunSnapshot> {
}
}
collecting = true
try {
const result = await withTrafficCollectionLock(() => pool.connect(), () => collectTrafficRun(sampledAt))
return result.acquired ? result.value : {
v:SCHEDULER_RUN_SNAPSHOT_VERSION,job:"traffic",sampledAt,skipped:true,servers:[],
}
} catch (error) {
return {v:SCHEDULER_RUN_SNAPSHOT_VERSION,job:"traffic",sampledAt,servers:[],
fatalError:error instanceof Error ? error.message : String(error)}
} finally {
collecting = false
}
}
async function collectTrafficRun(sampledAt:string):Promise<TrafficRunSnapshot> {
const startedAt = Date.now()
const now = sampledAt
const snapshot: TrafficRunSnapshot = {
@@ -258,8 +273,6 @@ export async function collectTrafficOnce(): Promise<TrafficRunSnapshot> {
lastError: msg,
updatedAt: now,
}).where(eq(trafficSettings.id, 1))
} finally {
collecting = false
}
return snapshot
}
@@ -0,0 +1,57 @@
import assert from 'node:assert/strict'
import {createServer} from 'node:http'
import {spawn} from 'node:child_process'
import {pool,closePool} from '../db/index.js'
import {initDatabase} from '../db/bootstrap.js'
const database=process.env.MMAPP_OWN_SQL_DB
if(!database)console.log('traffic concurrency SQL: skipped; explicit owned database required')
else {
assert(/^mm_accept_166_\d+$/.test(database));assert.equal(new URL(process.env.DATABASE_URL!).pathname,'/'+database)
assert.equal((await pool.query('SELECT current_database() AS name')).rows[0].name,database)
const baseline=process.env.MMAPP_TRAFFIC_CONCURRENCY_EXPECT==='overlap'
let serverId:number|undefined,interfaceRequests=0,releaseFirst:()=>void=()=>{},firstSeen:()=>void=()=>{},secondSeen:()=>void=()=>{}
const firstRequest=new Promise<void>(resolve=>firstSeen=resolve),secondRequest=new Promise<void>(resolve=>secondSeen=resolve)
const children:ReturnType<typeof spawn>[]=[]
const http=createServer((req,res)=>{
res.setHeader('Content-Type','application/json')
const respond=()=>res.end(JSON.stringify(req.url?.includes('/wireguard/')?[]:[{'.id':'*1',name:'ether1',running:true,disabled:false,'rx-byte':'1000','tx-byte':'1000'}]))
if(req.url?.includes('/interface')&&!req.url.includes('/wireguard/')){
interfaceRequests++
if(interfaceRequests===1){releaseFirst=respond;firstSeen();return}
secondSeen()
}
respond()
})
await new Promise<void>(resolve=>http.listen(0,'127.0.0.1',resolve))
const address=http.address();assert(address&&typeof address==='object')
const extension=import.meta.url.endsWith('.ts')?'ts':'js'
const childCode=`import assert from 'node:assert/strict';import {pool,closePool} from ${JSON.stringify(new URL('../db/index.'+extension,import.meta.url).href)};import {collectTrafficOnce} from ${JSON.stringify(new URL('./traffic-collector.'+extension,import.meta.url).href)};assert.equal((await pool.query('SELECT current_database() AS name')).rows[0].name,process.env.MMAPP_OWN_SQL_DB);try{console.log(JSON.stringify(await collectTrafficOnce()))}finally{await closePool()}`
const run=()=>new Promise<{skipped?:boolean;servers:unknown[]}>( (resolve,reject)=>{
const child=spawn(process.execPath,[...process.execArgv,'--input-type=module','-e',childCode],{env:process.env,stdio:['ignore','pipe','pipe']});children.push(child)
let output='',error='';child.stdout.on('data',b=>output+=b);child.stderr.on('data',b=>error+=b)
child.on('error',reject);child.on('exit',code=>{if(code!==0)return reject(Error('Owned collector child failed: '+error));try{resolve(JSON.parse(output.trim()))}catch(e){reject(e)}})
})
let timeout:ReturnType<typeof setTimeout>|undefined
try {
await initDatabase()
serverId=Number((await pool.query("INSERT INTO servers(name,host,port,use_ssl,enabled,username,password) VALUES('own-concurrency194','127.0.0.1',$1,false,true,'fixture','fixture') RETURNING id",[address.port])).rows[0].id)
await pool.query('INSERT INTO traffic_settings(id,enabled,interval_sec) VALUES(1,true,30) ON CONFLICT(id) DO UPDATE SET enabled=true,interval_sec=30')
const deadline=()=>new Promise((_,reject)=>{timeout=setTimeout(()=>reject(Error('Collector deadline exceeded')),8000)})
const first=run();await Promise.race([firstRequest,first.then(()=>{throw Error('First collector finished before its held request')}),deadline()]);clearTimeout(timeout)
const second=run()
await Promise.race([secondRequest,second,deadline()])
clearTimeout(timeout);releaseFirst()
const [a,b]=await Promise.all([first,second]);assert.equal(a.skipped,undefined)
assert.equal(b.skipped,baseline?undefined:true)
assert.equal(interfaceRequests,baseline?2:1)
assert.equal(Number((await pool.query('SELECT count(*) AS n FROM traffic_samples WHERE server_id=$1',[serverId])).rows[0].n),baseline?2:1)
const recovery=await run();assert.equal(recovery.skipped,undefined);assert.equal(interfaceRequests,baseline?3:2)
console.log(`traffic concurrency owned SQL: ${baseline?'overlap baseline':'cross-process skip/release/recovery'} passed; interface requests ${interfaceRequests}`)
}finally{
clearTimeout(timeout);releaseFirst();for(const child of children)if(child.exitCode===null)child.kill()
await new Promise<void>(resolve=>http.close(()=>resolve()))
if(serverId!==undefined){await pool.query('DELETE FROM traffic_samples WHERE server_id=$1',[serverId]);await pool.query('DELETE FROM servers WHERE id=$1',[serverId])}
await closePool()
}
}
@@ -37,11 +37,12 @@ console.log("traffic observation counters/rates/source confidence tests ok")
const collector = fs.readFileSync(new URL("./traffic-collector.ts", import.meta.url), "utf8")
const ast = ts.createSourceFile("traffic-collector.ts", collector, ts.ScriptTarget.Latest, true)
const functions = ast.statements.filter(n => ts.isFunctionDeclaration(n) && n.name &&
["waveKey", "sampleRate", "toNum", "collectTrafficOnce"].includes(n.name.text)).map(n => n.getText(ast)).join("\n")
["waveKey", "sampleRate", "toNum", "collectTrafficOnce", "collectTrafficRun"].includes(n.name.text)).map(n => n.getText(ast)).join("\n")
assert.equal(ast.statements.filter(n => ts.isFunctionDeclaration(n) && n.name && n.name.text === "collectTrafficOnce").length, 1)
let inserted: unknown[] = [], wave = new Map<string, typeof previous & {nativeCounterIdentity?:string|null}>(), counters: unknown = "1000"
let included = true
const context = vm.createContext({
pool:{connect:()=>{}},withTrafficCollectionLock:async(_connect:unknown,run:()=>Promise<unknown>)=>({acquired:true,value:await run()}),
Date, nativeTrafficCounterIdentity,parseTrafficPeers, observedTrafficRate, parseTrafficCounter, parseTrafficFlag, bpsToMbps, shouldIncludeIface,
SCHEDULER_RUN_SNAPSHOT_VERSION: 1, servers: {}, trafficSamples: {}, trafficSettings: {}, eq: () => ({}),
getSettingsRow: async () => ({}), readPreviousWave: async () => wave, rememberServerIfaces: () => {},
+16 -2
View File
@@ -1,6 +1,6 @@
# UX/UI: протокол внешней приёмки 12.5
Актуальное продолжение 2026-10-13: завершены191 и193 — общий пик подтверждённых интервалов и одно чтение истории сервера на запрос пользователей. Runtime backend ux-peak193/frontend ux-peak191 healthy/API/live UI2 passed.192: backend24 профиля passed321237ms; отдельные Monthly SQL/IPv6 UDP-worker-SQL/Ping replay passed, frontend59 test files accepted after corrected stale assertion; полный E2E1286 ещё выполняется.12.5 открыта.
Актуальное продолжение 2026-10-13: завершены191/193/194 — temporal peak, request cache, межпроцессная блокировка опроса трафика. Runtime backend ux-peak194/frontend ux-peak191 healthy/API/live UI2 passed; traffic ownSQL71+cross-process sequence passed53237ms.192: общая backend24 профиля/отдельные SQL controls/frontend59 test files приняты; полный E2E1286 ещё выполняется, итог ещё не принят.12.5 открыта.
IPsec78: реальный PSK responder established, mode-config198.18.78.10 и online=true подтверждены API; точные собственные объекты удалены. Сертификатный/DN online, late failure и history restore остаются открытыми. [Отчёт](ux-ui/acceptance/ipsec-mode-config-2026-10-09.json), [журнал](ux-ui-progress.md).
@@ -696,7 +696,7 @@ flowchart LR
## 12.5.77 — основной блок IPsec (2026-10-09)
Актуальное продолжение 2026-10-13: завершены191 и193 — общий пик подтверждённых интервалов и одно чтение истории сервера на запрос пользователей. Runtime backend ux-peak193/frontend ux-peak191 healthy/API/live UI2 passed.192: backend24 профиля passed321237ms; отдельные Monthly SQL/IPv6 UDP-worker-SQL/Ping replay passed, frontend59 test files accepted after corrected stale assertion; полный E2E1286 ещё выполняется.12.5 открыта.
Актуальное продолжение 2026-10-13: завершены191/193/194 — temporal peak, request cache, межпроцессная блокировка опроса трафика. Runtime backend ux-peak194/frontend ux-peak191 healthy/API/live UI2 passed; traffic ownSQL71+cross-process sequence passed53237ms.192: общая backend24 профиля/отдельные SQL controls/frontend59 test files приняты; полный E2E1286 ещё выполняется, итог ещё не принят.12.5 открыта.
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
@@ -2326,3 +2326,17 @@ Backend build passed. Production backend ux-peak193/version1.0.0-test.peak193 р
Протоколы: [cache](ux-ui/acceptance/traffic-users-cache-2026-10-13.json), [SQL](ux-ui/acceptance/traffic-users193-sql193-result.json), [source](ux-ui/acceptance/traffic-users193-source193-result.json), [compiled](ux-ui/acceptance/traffic-users193-compiled193-result.json), [runtime](ux-ui/acceptance/traffic-users193-runtime193-result.json), [API](ux-ui/acceptance/traffic-users193-peak193-api-result.json), [live UI](ux-ui/acceptance/traffic-users-cache-deployed-2026-10-13.json).
Общая регрессия192 начата на backend191; её24 backend профиля passed, текущий cache193 отдельно принят полным traffic-rate профилем. Не повторять общие неизменённые разделы. Полный браузерный прогон1286 ещё выполняется — не считать его завершённым. Исправлена ссылка live UI191: его evidence теперь хранится отдельно от исторического190, исходный протокол190 восстановлен. Следующий шаг — завершить192, разобрать ошибки полного E2E и восстановить только снимки текущего тестового запуска с сохранением8 исходно изменённых пользовательских PNG. Затем multi-instance/scale и внешние группы матрицы.12.5 открыта.
## 12.5.194 — трафик: координация опроса разных backend-процессов (2026-10-13)
На независимых Node-процессах подтверждён baseline: локальный collecting не препятствовал двум одновременным опросам одного источника и двум записанным волнам общей БД. Проверка использует собственную временную PostgreSQL БД и HTTP-эмулятор RouterOS на127.0.0.1, реальные CHR не менялись. [Baseline](ux-ui/acceptance/traffic-concurrency-baseline-2026-10-13.json).
Добавлена nonblocking session advisory lock8723105 на весь collectTrafficOnce в пределах общей PostgreSQL БД. Второй процесс возвращает существующий skipped snapshot до чтения настроек/истории, native GET и записи samples/receipt. Manual API уже преобразует skipped в409 «Сбор трафика уже выполняется», существующая семантика scheduler сохранена. Отложенная очередь опросов не создаётся. Локальный collecting сохранён для быстрых повторов внутри процесса. Неопределённый ответ acquisition не запускает run, соединение уничтожается; unconfirmed/failed unlock тоже уничтожает соединение вместо возврата в pool. Ошибка получения блокировки возвращает fatal snapshot без ложного успешного сбора.
Изменены traffic-collector.ts, добавлен traffic-collection-lock.ts и профильные unit/owned SQL tests. Actual collector body test обновлён под wrapper, не заменён копией алгоритма. Проверены busy/no work, malformed acquisition, run error, unlock false/error и destroy. Два настоящих процесса: первый удерживает HTTP-чтение, второй завершается skipped без второго запроса и samples; после первого следующий процесс выполняет новый опрос — lock released/recovery. SQL profile принял все71 ранее определённых cases и дополнительную cross-process sequence53237ms без unavailable skip; не считать её произвольным количеством новых тестов. DB/role/test container удалены. Production backend build passed;465 source files и1044 compiled artifacts совпали.
Backend ux-peak194/version1.0.0-test.peak194 развёрнут, frontend ux-peak191 сохранён. Backend/PG healthy, frontend HTTP/browser passed. API200, current measurements обоих CHR, live UI1440/320 без page errors/overflow. Native конфигурация/пароли не менялись, messages0.
Протоколы: [SQL](ux-ui/acceptance/traffic-lock194-sql194-result.json), [source](ux-ui/acceptance/traffic-lock194-source194-result.json), [compiled](ux-ui/acceptance/traffic-lock194-compiled194-result.json), [runtime](ux-ui/acceptance/traffic-lock194-runtime194-result.json), [API](ux-ui/acceptance/traffic-lock194-peak194-api-result.json), [live UI](ux-ui/acceptance/traffic-collection-lock-deployed-2026-10-13.json).
Границы: принято отсутствие параллельного traffic run при двух исправных process/session в одной БД, не координация всех writers приложения. Физическая потеря lock connection во время native GET/записи других pool connections не проверена; session lock не является fencing token. Не считать этот блок полной multi-instance/restore-drain/failover приёмкой. Следующий шаг — закончить и разобрать общую E2E192; затем отдельные multi-instance failures и внешние зависимости матрицы.12.5 открыта.
+3 -3
View File
@@ -1,12 +1,12 @@
# План реализации UX/UI по пятичасовым окнам
Актуальное продолжение 2026-10-13: завершены191 и193 — общий пик подтверждённых интервалов и одно чтение истории сервера на запрос пользователей. Runtime backend ux-peak193/frontend ux-peak191 healthy/API/live UI2 passed.192: backend24 профиля passed321237ms; отдельные Monthly SQL/IPv6 UDP-worker-SQL/Ping replay passed, frontend59 test files accepted after corrected stale assertion; полный E2E1286 ещё выполняется.12.5 открыта.
Актуальное продолжение 2026-10-13: завершены191/193/194 — temporal peak, request cache, межпроцессная блокировка опроса трафика. Runtime backend ux-peak194/frontend ux-peak191 healthy/API/live UI2 passed; traffic ownSQL71+cross-process sequence passed53237ms.192: общая backend24 профиля/отдельные SQL controls/frontend59 test files приняты; полный E2E1286 ещё выполняется, итог ещё не принят.12.5 открыта.
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-13. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
## Актуальная точка продолжения — 2026-10-13
Актуальное продолжение 2026-10-13: завершены191 и193 — общий пик подтверждённых интервалов и одно чтение истории сервера на запрос пользователей. Runtime backend ux-peak193/frontend ux-peak191 healthy/API/live UI2 passed.192: backend24 профиля passed321237ms; отдельные Monthly SQL/IPv6 UDP-worker-SQL/Ping replay passed, frontend59 test files accepted after corrected stale assertion; полный E2E1286 ещё выполняется.12.5 открыта.
Актуальное продолжение 2026-10-13: завершены191/193/194 — temporal peak, request cache, межпроцессная блокировка опроса трафика. Runtime backend ux-peak194/frontend ux-peak191 healthy/API/live UI2 passed; traffic ownSQL71+cross-process sequence passed53237ms.192: общая backend24 профиля/отдельные SQL controls/frontend59 test files приняты; полный E2E1286 ещё выполняется, итог ещё не принят.12.5 открыта.
Этапы0–11 завершены в локальных границах журнала. История выполненных внешних блоков и доказательства сохранены в [журнале](ux-ui-stage-12.md) и [протоколе](ux-ui-external-acceptance.md). Реальная Telegram доставка, Portal SSO, multi-instance и оставшиеся профильные сетевые ограничения открыты.
@@ -371,6 +371,6 @@ flowchart LR
## 12.5.77 — основной блок IPsec (2026-10-09)
Актуальное продолжение 2026-10-13: завершены191 и193 — общий пик подтверждённых интервалов и одно чтение истории сервера на запрос пользователей. Runtime backend ux-peak193/frontend ux-peak191 healthy/API/live UI2 passed.192: backend24 профиля passed321237ms; отдельные Monthly SQL/IPv6 UDP-worker-SQL/Ping replay passed, frontend59 test files accepted after corrected stale assertion; полный E2E1286 ещё выполняется.12.5 открыта.
Актуальное продолжение 2026-10-13: завершены191/193/194 — temporal peak, request cache, межпроцессная блокировка опроса трафика. Runtime backend ux-peak194/frontend ux-peak191 healthy/API/live UI2 passed; traffic ownSQL71+cross-process sequence passed53237ms.192: общая backend24 профиля/отдельные SQL controls/frontend59 test files приняты; полный E2E1286 ещё выполняется, итог ещё не принят.12.5 открыта.
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
+16 -2
View File
@@ -1,6 +1,6 @@
# UX/UI: журнал реализации
Актуальное продолжение 2026-10-13: завершены191 и193 — общий пик подтверждённых интервалов и одно чтение истории сервера на запрос пользователей. Runtime backend ux-peak193/frontend ux-peak191 healthy/API/live UI2 passed.192: backend24 профиля passed321237ms; отдельные Monthly SQL/IPv6 UDP-worker-SQL/Ping replay passed, frontend59 test files accepted after corrected stale assertion; полный E2E1286 ещё выполняется.12.5 открыта.
Актуальное продолжение 2026-10-13: завершены191/193/194 — temporal peak, request cache, межпроцессная блокировка опроса трафика. Runtime backend ux-peak194/frontend ux-peak191 healthy/API/live UI2 passed; traffic ownSQL71+cross-process sequence passed53237ms.192: общая backend24 профиля/отдельные SQL controls/frontend59 test files приняты; полный E2E1286 ещё выполняется, итог ещё не принят.12.5 открыта.
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
@@ -36,7 +36,21 @@
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–191 и193;192 в проверке и ограничения ниже. Runtime backend ux-peak193/frontend ux-peak191. |
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–191 и193–194;192 в проверке и ограничения ниже. Runtime backend ux-peak194/frontend ux-peak191. |
## 12.5.194 — трафик: координация опроса разных backend-процессов (2026-10-13)
На независимых Node-процессах подтверждён baseline: локальный collecting не препятствовал двум одновременным опросам одного источника и двум записанным волнам общей БД. Проверка использует собственную временную PostgreSQL БД и HTTP-эмулятор RouterOS на127.0.0.1, реальные CHR не менялись. [Baseline](ux-ui/acceptance/traffic-concurrency-baseline-2026-10-13.json).
Добавлена nonblocking session advisory lock8723105 на весь collectTrafficOnce в пределах общей PostgreSQL БД. Второй процесс возвращает существующий skipped snapshot до чтения настроек/истории, native GET и записи samples/receipt. Manual API уже преобразует skipped в409 «Сбор трафика уже выполняется», существующая семантика scheduler сохранена. Отложенная очередь опросов не создаётся. Локальный collecting сохранён для быстрых повторов внутри процесса. Неопределённый ответ acquisition не запускает run, соединение уничтожается; unconfirmed/failed unlock тоже уничтожает соединение вместо возврата в pool. Ошибка получения блокировки возвращает fatal snapshot без ложного успешного сбора.
Изменены traffic-collector.ts, добавлен traffic-collection-lock.ts и профильные unit/owned SQL tests. Actual collector body test обновлён под wrapper, не заменён копией алгоритма. Проверены busy/no work, malformed acquisition, run error, unlock false/error и destroy. Два настоящих процесса: первый удерживает HTTP-чтение, второй завершается skipped без второго запроса и samples; после первого следующий процесс выполняет новый опрос — lock released/recovery. SQL profile принял все71 ранее определённых cases и дополнительную cross-process sequence53237ms без unavailable skip; не считать её произвольным количеством новых тестов. DB/role/test container удалены. Production backend build passed;465 source files и1044 compiled artifacts совпали.
Backend ux-peak194/version1.0.0-test.peak194 развёрнут, frontend ux-peak191 сохранён. Backend/PG healthy, frontend HTTP/browser passed. API200, current measurements обоих CHR, live UI1440/320 без page errors/overflow. Native конфигурация/пароли не менялись, messages0.
Протоколы: [SQL](ux-ui/acceptance/traffic-lock194-sql194-result.json), [source](ux-ui/acceptance/traffic-lock194-source194-result.json), [compiled](ux-ui/acceptance/traffic-lock194-compiled194-result.json), [runtime](ux-ui/acceptance/traffic-lock194-runtime194-result.json), [API](ux-ui/acceptance/traffic-lock194-peak194-api-result.json), [live UI](ux-ui/acceptance/traffic-collection-lock-deployed-2026-10-13.json).
Границы: принято отсутствие параллельного traffic run при двух исправных process/session в одной БД, не координация всех writers приложения. Физическая потеря lock connection во время native GET/записи других pool connections не проверена; session lock не является fencing token. Не считать этот блок полной multi-instance/restore-drain/failover приёмкой. Следующий шаг — закончить и разобрать общую E2E192; затем отдельные multi-instance failures и внешние зависимости матрицы.12.5 открыта.
## 12.5.193 — трафик пользователей: одно чтение истории общего сервера (2026-10-13)
+18 -4
View File
@@ -1,10 +1,10 @@
# Оставшаяся внешняя приёмка — 2026-10-13
Актуальное продолжение 2026-10-13: завершены191 и193 — общий пик подтверждённых интервалов и одно чтение истории сервера на запрос пользователей. Runtime backend ux-peak193/frontend ux-peak191 healthy/API/live UI2 passed.192: backend24 профиля passed321237ms; отдельные Monthly SQL/IPv6 UDP-worker-SQL/Ping replay passed, frontend59 test files accepted after corrected stale assertion; полный E2E1286 ещё выполняется.12.5 открыта.
Актуальное продолжение 2026-10-13: завершены191/193/194 — temporal peak, request cache, межпроцессная блокировка опроса трафика. Runtime backend ux-peak194/frontend ux-peak191 healthy/API/live UI2 passed; traffic ownSQL71+cross-process sequence passed53237ms.192: общая backend24 профиля/отдельные SQL controls/frontend59 test files приняты; полный E2E1286 ещё выполняется, итог ещё не принят.12.5 открыта.
Это актуальная матрица после 12.5.193. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
Это актуальная матрица после 12.5.194. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-peak193`, frontend `ux-peak191`; production lab использует AUTH_REQUIRED=false. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-peak194`, frontend `ux-peak191`; production lab использует AUTH_REQUIRED=false. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
| Раздел | Подтверждено | Осталось / условие завершения |
| --- | --- | --- |
@@ -26,7 +26,7 @@
## Следующая последовательность — 2026-10-13
1. **192 — общая регрессия:** завершить полный E2E1286, разобрать ошибки; backend24 профиля/отдельные SQL controls и frontend59 test files уже проверены.191 temporal peak и193 request cache приняты, runtime backend193/frontend191.
1. **192 — общая регрессия:** завершить полный E2E1286, разобрать ошибки; backend24 профиля/отдельные SQL controls и frontend59 test files уже проверены.191 temporal peak и193 request cache приняты, runtime backend194/frontend191;194 cross-process traffic skip/recovery принят в указанных границах.
2. **Профильный контроль:** проверить изменённые readers/API/UI и актуальный runtime; полную регрессию выполнить один раз после согласованного завершения доработок.
3. **Внешние зависимости:** Portal issuer/login/revoke, Telegram bot/chat/разрешённое сообщение, клиентские ОС, независимые WAN/public IPv6 и scale/multi-instance — отдельные группы по таблице выше. Ограничения текущего стенда не превращать в обещание приёмки всех конфигураций.
@@ -1242,3 +1242,17 @@ Backend build passed. Production backend ux-peak193/version1.0.0-test.peak193 р
Протоколы: [cache](ux-ui/acceptance/traffic-users-cache-2026-10-13.json), [SQL](ux-ui/acceptance/traffic-users193-sql193-result.json), [source](ux-ui/acceptance/traffic-users193-source193-result.json), [compiled](ux-ui/acceptance/traffic-users193-compiled193-result.json), [runtime](ux-ui/acceptance/traffic-users193-runtime193-result.json), [API](ux-ui/acceptance/traffic-users193-peak193-api-result.json), [live UI](ux-ui/acceptance/traffic-users-cache-deployed-2026-10-13.json).
Общая регрессия192 начата на backend191; её24 backend профиля passed, текущий cache193 отдельно принят полным traffic-rate профилем. Не повторять общие неизменённые разделы. Полный браузерный прогон1286 ещё выполняется — не считать его завершённым. Исправлена ссылка live UI191: его evidence теперь хранится отдельно от исторического190, исходный протокол190 восстановлен. Следующий шаг — завершить192, разобрать ошибки полного E2E и восстановить только снимки текущего тестового запуска с сохранением8 исходно изменённых пользовательских PNG. Затем multi-instance/scale и внешние группы матрицы.12.5 открыта.
## 12.5.194 — трафик: координация опроса разных backend-процессов (2026-10-13)
На независимых Node-процессах подтверждён baseline: локальный collecting не препятствовал двум одновременным опросам одного источника и двум записанным волнам общей БД. Проверка использует собственную временную PostgreSQL БД и HTTP-эмулятор RouterOS на127.0.0.1, реальные CHR не менялись. [Baseline](ux-ui/acceptance/traffic-concurrency-baseline-2026-10-13.json).
Добавлена nonblocking session advisory lock8723105 на весь collectTrafficOnce в пределах общей PostgreSQL БД. Второй процесс возвращает существующий skipped snapshot до чтения настроек/истории, native GET и записи samples/receipt. Manual API уже преобразует skipped в409 «Сбор трафика уже выполняется», существующая семантика scheduler сохранена. Отложенная очередь опросов не создаётся. Локальный collecting сохранён для быстрых повторов внутри процесса. Неопределённый ответ acquisition не запускает run, соединение уничтожается; unconfirmed/failed unlock тоже уничтожает соединение вместо возврата в pool. Ошибка получения блокировки возвращает fatal snapshot без ложного успешного сбора.
Изменены traffic-collector.ts, добавлен traffic-collection-lock.ts и профильные unit/owned SQL tests. Actual collector body test обновлён под wrapper, не заменён копией алгоритма. Проверены busy/no work, malformed acquisition, run error, unlock false/error и destroy. Два настоящих процесса: первый удерживает HTTP-чтение, второй завершается skipped без второго запроса и samples; после первого следующий процесс выполняет новый опрос — lock released/recovery. SQL profile принял все71 ранее определённых cases и дополнительную cross-process sequence53237ms без unavailable skip; не считать её произвольным количеством новых тестов. DB/role/test container удалены. Production backend build passed;465 source files и1044 compiled artifacts совпали.
Backend ux-peak194/version1.0.0-test.peak194 развёрнут, frontend ux-peak191 сохранён. Backend/PG healthy, frontend HTTP/browser passed. API200, current measurements обоих CHR, live UI1440/320 без page errors/overflow. Native конфигурация/пароли не менялись, messages0.
Протоколы: [SQL](ux-ui/acceptance/traffic-lock194-sql194-result.json), [source](ux-ui/acceptance/traffic-lock194-source194-result.json), [compiled](ux-ui/acceptance/traffic-lock194-compiled194-result.json), [runtime](ux-ui/acceptance/traffic-lock194-runtime194-result.json), [API](ux-ui/acceptance/traffic-lock194-peak194-api-result.json), [live UI](ux-ui/acceptance/traffic-collection-lock-deployed-2026-10-13.json).
Границы: принято отсутствие параллельного traffic run при двух исправных process/session в одной БД, не координация всех writers приложения. Физическая потеря lock connection во время native GET/записи других pool connections не проверена; session lock не является fencing token. Не считать этот блок полной multi-instance/restore-drain/failover приёмкой. Следующий шаг — закончить и разобрать общую E2E192; затем отдельные multi-instance failures и внешние зависимости матрицы.12.5 открыта.
+15 -1
View File
@@ -1,6 +1,6 @@
# Этап 12: итоговая приёмка UX/UI
Актуальное продолжение 2026-10-13: завершены191 и193 — общий пик подтверждённых интервалов и одно чтение истории сервера на запрос пользователей. Runtime backend ux-peak193/frontend ux-peak191 healthy/API/live UI2 passed.192: backend24 профиля passed321237ms; отдельные Monthly SQL/IPv6 UDP-worker-SQL/Ping replay passed, frontend59 test files accepted after corrected stale assertion; полный E2E1286 ещё выполняется.12.5 открыта.
Актуальное продолжение 2026-10-13: завершены191/193/194 — temporal peak, request cache, межпроцессная блокировка опроса трафика. Runtime backend ux-peak194/frontend ux-peak191 healthy/API/live UI2 passed; traffic ownSQL71+cross-process sequence passed53237ms.192: общая backend24 профиля/отдельные SQL controls/frontend59 test files приняты; полный E2E1286 ещё выполняется, итог ещё не принят.12.5 открыта.
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
@@ -3631,3 +3631,17 @@ Backend build passed. Production backend ux-peak193/version1.0.0-test.peak193 р
Протоколы: [cache](ux-ui/acceptance/traffic-users-cache-2026-10-13.json), [SQL](ux-ui/acceptance/traffic-users193-sql193-result.json), [source](ux-ui/acceptance/traffic-users193-source193-result.json), [compiled](ux-ui/acceptance/traffic-users193-compiled193-result.json), [runtime](ux-ui/acceptance/traffic-users193-runtime193-result.json), [API](ux-ui/acceptance/traffic-users193-peak193-api-result.json), [live UI](ux-ui/acceptance/traffic-users-cache-deployed-2026-10-13.json).
Общая регрессия192 начата на backend191; её24 backend профиля passed, текущий cache193 отдельно принят полным traffic-rate профилем. Не повторять общие неизменённые разделы. Полный браузерный прогон1286 ещё выполняется — не считать его завершённым. Исправлена ссылка live UI191: его evidence теперь хранится отдельно от исторического190, исходный протокол190 восстановлен. Следующий шаг — завершить192, разобрать ошибки полного E2E и восстановить только снимки текущего тестового запуска с сохранением8 исходно изменённых пользовательских PNG. Затем multi-instance/scale и внешние группы матрицы.12.5 открыта.
## 12.5.194 — трафик: координация опроса разных backend-процессов (2026-10-13)
На независимых Node-процессах подтверждён baseline: локальный collecting не препятствовал двум одновременным опросам одного источника и двум записанным волнам общей БД. Проверка использует собственную временную PostgreSQL БД и HTTP-эмулятор RouterOS на127.0.0.1, реальные CHR не менялись. [Baseline](ux-ui/acceptance/traffic-concurrency-baseline-2026-10-13.json).
Добавлена nonblocking session advisory lock8723105 на весь collectTrafficOnce в пределах общей PostgreSQL БД. Второй процесс возвращает существующий skipped snapshot до чтения настроек/истории, native GET и записи samples/receipt. Manual API уже преобразует skipped в409 «Сбор трафика уже выполняется», существующая семантика scheduler сохранена. Отложенная очередь опросов не создаётся. Локальный collecting сохранён для быстрых повторов внутри процесса. Неопределённый ответ acquisition не запускает run, соединение уничтожается; unconfirmed/failed unlock тоже уничтожает соединение вместо возврата в pool. Ошибка получения блокировки возвращает fatal snapshot без ложного успешного сбора.
Изменены traffic-collector.ts, добавлен traffic-collection-lock.ts и профильные unit/owned SQL tests. Actual collector body test обновлён под wrapper, не заменён копией алгоритма. Проверены busy/no work, malformed acquisition, run error, unlock false/error и destroy. Два настоящих процесса: первый удерживает HTTP-чтение, второй завершается skipped без второго запроса и samples; после первого следующий процесс выполняет новый опрос — lock released/recovery. SQL profile принял все71 ранее определённых cases и дополнительную cross-process sequence53237ms без unavailable skip; не считать её произвольным количеством новых тестов. DB/role/test container удалены. Production backend build passed;465 source files и1044 compiled artifacts совпали.
Backend ux-peak194/version1.0.0-test.peak194 развёрнут, frontend ux-peak191 сохранён. Backend/PG healthy, frontend HTTP/browser passed. API200, current measurements обоих CHR, live UI1440/320 без page errors/overflow. Native конфигурация/пароли не менялись, messages0.
Протоколы: [SQL](ux-ui/acceptance/traffic-lock194-sql194-result.json), [source](ux-ui/acceptance/traffic-lock194-source194-result.json), [compiled](ux-ui/acceptance/traffic-lock194-compiled194-result.json), [runtime](ux-ui/acceptance/traffic-lock194-runtime194-result.json), [API](ux-ui/acceptance/traffic-lock194-peak194-api-result.json), [live UI](ux-ui/acceptance/traffic-collection-lock-deployed-2026-10-13.json).
Границы: принято отсутствие параллельного traffic run при двух исправных process/session в одной БД, не координация всех writers приложения. Физическая потеря lock connection во время native GET/записи других pool connections не проверена; session lock не является fencing token. Не считать этот блок полной multi-instance/restore-drain/failover приёмкой. Следующий шаг — закончить и разобрать общую E2E192; затем отдельные multi-instance failures и внешние зависимости матрицы.12.5 открыта.
+16 -2
View File
@@ -1,6 +1,6 @@
# Тестовый сервер UX/UI — 2026-10-10
Актуальное продолжение 2026-10-13: завершены191 и193 — общий пик подтверждённых интервалов и одно чтение истории сервера на запрос пользователей. Runtime backend ux-peak193/frontend ux-peak191 healthy/API/live UI2 passed.192: backend24 профиля passed321237ms; отдельные Monthly SQL/IPv6 UDP-worker-SQL/Ping replay passed, frontend59 test files accepted after corrected stale assertion; полный E2E1286 ещё выполняется.12.5 открыта.
Актуальное продолжение 2026-10-13: завершены191/193/194 — temporal peak, request cache, межпроцессная блокировка опроса трафика. Runtime backend ux-peak194/frontend ux-peak191 healthy/API/live UI2 passed; traffic ownSQL71+cross-process sequence passed53237ms.192: общая backend24 профиля/отдельные SQL controls/frontend59 test files приняты; полный E2E1286 ещё выполняется, итог ещё не принят.12.5 открыта.
| Параметр | Проверенное состояние |
| --- | --- |
@@ -708,7 +708,7 @@ flowchart LR
## 12.5.77 — основной блок IPsec (2026-10-09)
Актуальное продолжение 2026-10-13: завершены191 и193 — общий пик подтверждённых интервалов и одно чтение истории сервера на запрос пользователей. Runtime backend ux-peak193/frontend ux-peak191 healthy/API/live UI2 passed.192: backend24 профиля passed321237ms; отдельные Monthly SQL/IPv6 UDP-worker-SQL/Ping replay passed, frontend59 test files accepted after corrected stale assertion; полный E2E1286 ещё выполняется.12.5 открыта.
Актуальное продолжение 2026-10-13: завершены191/193/194 — temporal peak, request cache, межпроцессная блокировка опроса трафика. Runtime backend ux-peak194/frontend ux-peak191 healthy/API/live UI2 passed; traffic ownSQL71+cross-process sequence passed53237ms.192: общая backend24 профиля/отдельные SQL controls/frontend59 test files приняты; полный E2E1286 ещё выполняется, итог ещё не принят.12.5 открыта.
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
@@ -2057,3 +2057,17 @@ Backend build passed. Production backend ux-peak193/version1.0.0-test.peak193 р
Протоколы: [cache](ux-ui/acceptance/traffic-users-cache-2026-10-13.json), [SQL](ux-ui/acceptance/traffic-users193-sql193-result.json), [source](ux-ui/acceptance/traffic-users193-source193-result.json), [compiled](ux-ui/acceptance/traffic-users193-compiled193-result.json), [runtime](ux-ui/acceptance/traffic-users193-runtime193-result.json), [API](ux-ui/acceptance/traffic-users193-peak193-api-result.json), [live UI](ux-ui/acceptance/traffic-users-cache-deployed-2026-10-13.json).
Общая регрессия192 начата на backend191; её24 backend профиля passed, текущий cache193 отдельно принят полным traffic-rate профилем. Не повторять общие неизменённые разделы. Полный браузерный прогон1286 ещё выполняется — не считать его завершённым. Исправлена ссылка live UI191: его evidence теперь хранится отдельно от исторического190, исходный протокол190 восстановлен. Следующий шаг — завершить192, разобрать ошибки полного E2E и восстановить только снимки текущего тестового запуска с сохранением8 исходно изменённых пользовательских PNG. Затем multi-instance/scale и внешние группы матрицы.12.5 открыта.
## 12.5.194 — трафик: координация опроса разных backend-процессов (2026-10-13)
На независимых Node-процессах подтверждён baseline: локальный collecting не препятствовал двум одновременным опросам одного источника и двум записанным волнам общей БД. Проверка использует собственную временную PostgreSQL БД и HTTP-эмулятор RouterOS на127.0.0.1, реальные CHR не менялись. [Baseline](ux-ui/acceptance/traffic-concurrency-baseline-2026-10-13.json).
Добавлена nonblocking session advisory lock8723105 на весь collectTrafficOnce в пределах общей PostgreSQL БД. Второй процесс возвращает существующий skipped snapshot до чтения настроек/истории, native GET и записи samples/receipt. Manual API уже преобразует skipped в409 «Сбор трафика уже выполняется», существующая семантика scheduler сохранена. Отложенная очередь опросов не создаётся. Локальный collecting сохранён для быстрых повторов внутри процесса. Неопределённый ответ acquisition не запускает run, соединение уничтожается; unconfirmed/failed unlock тоже уничтожает соединение вместо возврата в pool. Ошибка получения блокировки возвращает fatal snapshot без ложного успешного сбора.
Изменены traffic-collector.ts, добавлен traffic-collection-lock.ts и профильные unit/owned SQL tests. Actual collector body test обновлён под wrapper, не заменён копией алгоритма. Проверены busy/no work, malformed acquisition, run error, unlock false/error и destroy. Два настоящих процесса: первый удерживает HTTP-чтение, второй завершается skipped без второго запроса и samples; после первого следующий процесс выполняет новый опрос — lock released/recovery. SQL profile принял все71 ранее определённых cases и дополнительную cross-process sequence53237ms без unavailable skip; не считать её произвольным количеством новых тестов. DB/role/test container удалены. Production backend build passed;465 source files и1044 compiled artifacts совпали.
Backend ux-peak194/version1.0.0-test.peak194 развёрнут, frontend ux-peak191 сохранён. Backend/PG healthy, frontend HTTP/browser passed. API200, current measurements обоих CHR, live UI1440/320 без page errors/overflow. Native конфигурация/пароли не менялись, messages0.
Протоколы: [SQL](ux-ui/acceptance/traffic-lock194-sql194-result.json), [source](ux-ui/acceptance/traffic-lock194-source194-result.json), [compiled](ux-ui/acceptance/traffic-lock194-compiled194-result.json), [runtime](ux-ui/acceptance/traffic-lock194-runtime194-result.json), [API](ux-ui/acceptance/traffic-lock194-peak194-api-result.json), [live UI](ux-ui/acceptance/traffic-collection-lock-deployed-2026-10-13.json).
Границы: принято отсутствие параллельного traffic run при двух исправных process/session в одной БД, не координация всех writers приложения. Физическая потеря lock connection во время native GET/записи других pool connections не проверена; session lock не является fencing token. Не считать этот блок полной multi-instance/restore-drain/failover приёмкой. Следующий шаг — закончить и разобрать общую E2E192; затем отдельные multi-instance failures и внешние зависимости матрицы.12.5 открыта.
@@ -0,0 +1,46 @@
{
"date": "2026-10-13",
"block": "12.5.194",
"runtime": {
"backend": "ux-peak194",
"frontend": "ux-peak191"
},
"apis": [
{
"path": "/api/traffic/servers",
"status": 200
},
{
"path": "/api/traffic/users",
"status": 200
},
{
"path": "/api/traffic/bound-interfaces",
"status": 200
}
],
"cases": [
{
"width": 1440,
"trafficServers200": true,
"attemptProvenanceVisible": true,
"actualLiveSample": true,
"backendReceiptLabel": true,
"pageErrors": 0,
"noOverflow": true
},
{
"width": 320,
"trafficServers200": true,
"attemptProvenanceVisible": true,
"actualLiveSample": true,
"backendReceiptLabel": true,
"pageErrors": 0,
"noOverflow": true
}
],
"bounds": [
"Read-only smoke; error/stale/legacy branches proven by owned SQL and UI fixtures",
"No manual production scheduler/config/password/message writes"
]
}
@@ -0,0 +1,17 @@
{
"date": "2026-10-13",
"block": "12.5.194",
"image": "ux-peak193",
"scope": "Two independent processes, owned isolated PostgreSQL and local HTTP RouterOS fixture",
"baselineAccepted": true,
"concurrentInterfaceRequests": 2,
"savedWaves": 2,
"interfaceRequestsIncludingRecovery": 3,
"productionDbTouched": false,
"nativeChrCommands": 0,
"temporaryDatabaseAndRoleRemoved": true,
"bounds": [
"Baseline counts asserted in traffic-concurrency-sql.test.ts; run with MMAPP_TRAFFIC_CONCURRENCY_EXPECT=overlap",
"Not a physical WAN or database connection failure test"
]
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,26 @@
{
"date": "2026-10-13",
"block": "12.5.194",
"version": "1.0.0-test.peak194",
"passed": true,
"method": "Read-only actual application volume API; no new native RouterOS commands; exact edge values covered separately by owned SQL",
"cases": [
{
"sourceId": "1",
"volumeUnit": "GiB",
"rxTotalGiB": 0.012730824761092663,
"txTotalGiB": 0.0009437743574380875,
"currentState": "current"
},
{
"sourceId": "2",
"volumeUnit": "GiB",
"rxTotalGiB": 0.0012853574007749557,
"txTotalGiB": 0.0015285797417163849,
"currentState": "current"
}
],
"newRouterOSCommands": 0,
"passwordChanges": 0,
"messages": 0
}
@@ -0,0 +1,25 @@
{
"date": "2026-10-13",
"block": "12.5.194",
"secretEnvCopied": false,
"rollbackTags": {
"MMAPP_IMAGE_TAG": "ux-peak191",
"BACKEND_IMAGE_TAG": "ux-peak193"
},
"services": {
"backend": {
"image": "mmapp-test/backend:ux-peak194",
"imageId": "sha256:13507a7e8f3c356c4de4f6d909babf53f569e7eb2dd4061b5fa43d6078383e9a",
"state": "running",
"health": "healthy",
"matchedVerifiedImage": true
},
"frontend": {
"image": "mmapp-test/frontend:ux-peak191",
"imageId": "sha256:1d9f85dbb5c791f09dad2863041d0fae5a291b02f5e73a5e96185a7866c673c7",
"state": "running",
"health": null,
"matchedVerifiedImage": true
}
}
}
@@ -0,0 +1,10 @@
{
"date": "2026-10-13",
"block": "12.5.194",
"matched": {
"backend": 465,
"frontend": 0
},
"different": [],
"missing": []
}
@@ -0,0 +1,32 @@
{
"date": "2026-10-13",
"block": "12.5.194",
"passed": true,
"profiles": [
{
"date": "2026-10-13",
"block": "12.5.194",
"database": "mm_accept_166_1791876391027",
"scope": "ephemeral role and precreated owned database; no copied production credentials",
"tests": [
{
"command": "npm --prefix backend run test:traffic-rate",
"passed": true,
"durationMs": 53237,
"ownedTrafficSqlCases": 9,
"ownedHistorySqlCases": 14,
"ownedLiveSqlCases": 10,
"ownedVolumeSqlCases": 17,
"ownedStatusSqlCases": 21,
"ownedConcurrencySqlSequencePassed": true,
"postgresUnavailableSkip": false
}
],
"scopedRoleVerified": true,
"passed": true,
"databaseRemoved": true,
"roleRemoved": true,
"testContainerRemoved": true
}
]
}