fix(traffic): останавливать сбор при потере соединения блокировки
This commit is contained in:
1 parent
46de87e213
commit
cbe44e8072
16 files changed
+1340
-18
No files matched your search
@@ -0,0 +1,33 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import {createServer} from 'node:http'
|
||||
import {spawn} from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
const database=process.env.MMAPP_OWN_SQL_DB
|
||||
assert(/^mm_accept_166_\d+$/.test(database??''))
|
||||
const url=new URL(process.env.DATABASE_URL);assert.equal(url.pathname,'/'+database);assert.equal(url.username,database)
|
||||
const {pool,closePool}=await import('/app/backend/dist/db/index.js')
|
||||
const {initDatabase}=await import('/app/backend/dist/db/bootstrap.js')
|
||||
assert.equal((await pool.query('SELECT current_database() AS name')).rows[0].name,database)
|
||||
assert(Object.values((await pool.query('SELECT rolsuper,rolcreatedb,rolcreaterole,rolbypassrls FROM pg_roles WHERE rolname=current_user')).rows[0]).every(v=>v===false))
|
||||
const baseline=process.env.MMAPP_LOCK_LOSS_BASELINE==='yes',children=[],cases=[];let holdNext=false,heldSeen=()=>{},releaseHeld=()=>{},interfaceRequests=0,serverId
|
||||
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(holdNext){holdNext=false;releaseHeld=respond;heldSeen();return}}respond()})
|
||||
await new Promise(r=>http.listen(0,'127.0.0.1',r));const address=http.address()
|
||||
function start(){const code=`import {pool,closePool} from '/app/backend/dist/db/index.js';import {collectTrafficOnce} from '/app/backend/dist/services/traffic-collector.js';try{console.log(JSON.stringify(await collectTrafficOnce()))}finally{await closePool()}`;const child=spawn(process.execPath,['--input-type=module','-e',code],{env:process.env,stdio:['ignore','pipe','pipe']});children.push(child);let out='',err='';child.stdout.on('data',b=>out+=b);child.stderr.on('data',b=>err+=b);const done=new Promise((resolve,reject)=>{child.on('error',reject);child.on('exit',(exit,signal)=>resolve({exit,signal,out,err}))});return {child,done}}
|
||||
async function held(){holdNext=true;const seen=new Promise(r=>heldSeen=r);const job=start();await Promise.race([seen,job.done.then(()=>{throw Error('Collector ended before held native request')}),new Promise((_,reject)=>{const timer=setTimeout(()=>reject(Error('Held request deadline')),8000);timer.unref()})]);return job}
|
||||
async function count(){return Number((await pool.query('SELECT count(*) AS n FROM traffic_samples WHERE server_id=$1',[serverId])).rows[0].n)}
|
||||
const report={date:'2026-10-13',block:'12.5.200',database,baseline,cases,passed:false,scope:'Production compiled collector, independent Linux child processes, owned restricted PostgreSQL and local RouterOS HTTP fixture; no production PID/native CHR touched'}
|
||||
try{
|
||||
await initDatabase();serverId=Number((await pool.query("INSERT INTO servers(name,host,port,use_ssl,enabled,username,password) VALUES('own-lifecycle200','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 crashed=await held();crashed.child.kill('SIGKILL');const crash=await crashed.done;assert.equal(crash.signal,'SIGKILL');releaseHeld();assert.equal(await count(),0)
|
||||
let recovered=await start().done;assert.equal(recovered.exit,0);assert.equal(JSON.parse(recovered.out).fatalError,undefined);assert.equal(await count(),1);cases.push({name:'process crash releases advisory lock',signal:'SIGKILL',crashedWaveRows:0,recoveryWaveRows:1})
|
||||
const beforeSettings=(await pool.query('SELECT last_collected_at FROM traffic_settings WHERE id=1')).rows[0].last_collected_at
|
||||
const lost=await held();const locks=(await pool.query("SELECT a.pid,a.datname,a.usename FROM pg_locks l JOIN pg_stat_activity a ON a.pid=l.pid WHERE l.locktype='advisory' AND l.objid=8723105 AND l.classid=0 AND l.objsubid=1 AND l.granted AND a.datname=$1 AND a.usename=$1",[database])).rows;assert.equal(locks.length,1);assert.equal(locks[0].datname,database);assert.equal(locks[0].usename,database)
|
||||
assert.equal((await pool.query('SELECT pg_terminate_backend($1) AS terminated',[locks[0].pid])).rows[0].terminated,true)
|
||||
await new Promise(r=>setTimeout(r,150));releaseHeld();const ended=await lost.done
|
||||
if(baseline){assert.notEqual(ended.exit,0);assert(ended.err.includes("Unhandled 'error' event"));cases.push({name:'lock connection loss baseline',unhandledClientError:true,nonzeroChildExit:true})}
|
||||
else{assert.equal(ended.exit,0);assert.match(JSON.parse(ended.out).fatalError,/Соединение блокировки/);assert.equal(await count(),1);assert.equal(String((await pool.query('SELECT last_collected_at FROM traffic_settings WHERE id=1')).rows[0].last_collected_at),String(beforeSettings));cases.push({name:'lock connection loss',collectorProcessSurvived:true,fatalSnapshot:true,staleSampleRows:0,staleSettingsWrite:false})}
|
||||
recovered=await start().done;assert.equal(recovered.exit,0);assert.equal(JSON.parse(recovered.out).fatalError,undefined);assert.equal(await count(),2);cases.push({name:'next process recovers after connection loss',waveRows:2});report.interfaceRequests=interfaceRequests;report.passed=true
|
||||
}catch(e){report.errorName=e.name;fs.writeFileSync('/app/backend/acceptance/lifecycle200-private.log',String(e.stack??e),{mode:0o600})}
|
||||
finally{releaseHeld();for(const c of children)if(c.exitCode===null&&c.signalCode===null)c.kill('SIGKILL');http.closeAllConnections();await new Promise(r=>http.close(r));await closePool();fs.writeFileSync('/app/backend/acceptance/lifecycle200-'+(baseline?'baseline':'fixed')+'-result.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report))}
|
||||
if(!report.passed)process.exitCode=1
|
||||
@@ -15,3 +15,11 @@ for(const state of ['busy','missing','ok','run-error','unlock-false','unlock-err
|
||||
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')
|
||||
import {EventEmitter} from 'node:events'
|
||||
for(const event of ['error','end']){
|
||||
const events=new EventEmitter();let destroy=false,writes=0
|
||||
const client={query:async()=>({rows:[{acquired:true,released:true}]}),release:(value?:boolean)=>{destroy=!!value},on:events.on.bind(events),removeListener:events.removeListener.bind(events)}
|
||||
await assert.rejects(withTrafficCollectionLock(async()=>client,async guard=>{guard();events.emit(event,Error('own loss'));guard();writes++;return 42}),/Соединение блокировки/)
|
||||
assert.equal(writes,0);assert.equal(destroy,true);assert.equal(events.listenerCount(event),0)
|
||||
}
|
||||
console.log('traffic lock: error/end handled, guarded work aborted, connection destroyed, listeners removed')
|
||||
@@ -1,24 +1,35 @@
|
||||
type Client = {
|
||||
query: (sql:string,params:number[])=>Promise<{rows:Record<string,unknown>[]}>
|
||||
release: (destroy?:boolean)=>void
|
||||
on?: (event:"error"|"end",listener:()=>void)=>unknown
|
||||
removeListener?: (event:"error"|"end",listener:()=>void)=>unknown
|
||||
}
|
||||
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}> {
|
||||
export async function withTrafficCollectionLock<T>(connect:()=>Promise<Client>,run:(assertActive:()=>void)=>Promise<T>):Promise<{acquired:false}|{acquired:true;value:T}> {
|
||||
const client=await connect()
|
||||
let owned=false,destroy=true
|
||||
let owned=false,destroy=true,lost=false
|
||||
const lose=()=>{lost=true;destroy=true}
|
||||
const assertActive=()=>{if(lost)throw Error('Соединение блокировки опроса трафика потеряно; опрос остановлен')}
|
||||
client.on?.('error',lose)
|
||||
client.on?.('end',lose)
|
||||
try {
|
||||
const result=await client.query('SELECT pg_try_advisory_lock($1) AS acquired',[TRAFFIC_COLLECTION_LOCK_KEY])
|
||||
assertActive()
|
||||
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()}
|
||||
const value=await run(assertActive)
|
||||
assertActive()
|
||||
return {acquired:true,value}
|
||||
}finally{
|
||||
if(owned){
|
||||
if(owned&&!lost){
|
||||
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)
|
||||
client.removeListener?.('error',lose)
|
||||
client.removeListener?.('end',lose)
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ export async function collectTrafficOnce(): Promise<TrafficRunSnapshot> {
|
||||
}
|
||||
collecting = true
|
||||
try {
|
||||
const result = await withTrafficCollectionLock(() => pool.connect(), () => collectTrafficRun(sampledAt))
|
||||
const result = await withTrafficCollectionLock(() => pool.connect(), assertActive => collectTrafficRun(sampledAt,assertActive))
|
||||
return result.acquired ? result.value : {
|
||||
v:SCHEDULER_RUN_SNAPSHOT_VERSION,job:"traffic",sampledAt,skipped:true,servers:[],
|
||||
}
|
||||
@@ -129,7 +129,7 @@ export async function collectTrafficOnce(): Promise<TrafficRunSnapshot> {
|
||||
}
|
||||
}
|
||||
|
||||
async function collectTrafficRun(sampledAt:string):Promise<TrafficRunSnapshot> {
|
||||
async function collectTrafficRun(sampledAt:string,assertActive:()=>void=()=>{}):Promise<TrafficRunSnapshot> {
|
||||
const startedAt = Date.now()
|
||||
const now = sampledAt
|
||||
const snapshot: TrafficRunSnapshot = {
|
||||
@@ -140,14 +140,18 @@ async function collectTrafficRun(sampledAt:string):Promise<TrafficRunSnapshot> {
|
||||
}
|
||||
|
||||
try {
|
||||
assertActive()
|
||||
const settings = await getSettingsRow()
|
||||
const maxIntervalMs = Math.max(5, settings?.intervalSec ?? 30) * 3000
|
||||
const enabledServers = await db.select().from(servers).where(eq(servers.enabled, true))
|
||||
|
||||
for (const srv of enabledServers) {
|
||||
assertActive()
|
||||
const name = srv.name || srv.host
|
||||
const ensureSource = async () => {
|
||||
assertActive()
|
||||
const current = (await db.select({enabled:servers.enabled,updatedAt:servers.updatedAt}).from(servers).where(eq(servers.id,srv.id)).limit(1))[0]
|
||||
assertActive()
|
||||
if (!current?.enabled || Date.parse(current.updatedAt) !== Date.parse(srv.updatedAt))
|
||||
throw new Error("Сервер выключен, удалён или его конфигурация изменилась во время опроса")
|
||||
}
|
||||
@@ -247,6 +251,7 @@ async function collectTrafficRun(sampledAt:string):Promise<TrafficRunSnapshot> {
|
||||
} : {}),
|
||||
})
|
||||
} catch (err) {
|
||||
assertActive()
|
||||
await recordTrafficCollectionAttempt(srv, "error", now, err instanceof Error ? err.message : String(err))
|
||||
snapshot.servers.push({
|
||||
serverId: srv.id,
|
||||
@@ -258,6 +263,7 @@ async function collectTrafficRun(sampledAt:string):Promise<TrafficRunSnapshot> {
|
||||
}
|
||||
}
|
||||
|
||||
assertActive()
|
||||
await db.update(trafficSettings).set({
|
||||
lastCollectedAt: now,
|
||||
lastDurationMs: Date.now() - startedAt,
|
||||
@@ -265,6 +271,7 @@ async function collectTrafficRun(sampledAt:string):Promise<TrafficRunSnapshot> {
|
||||
updatedAt: now,
|
||||
}).where(eq(trafficSettings.id, 1))
|
||||
} catch (e) {
|
||||
assertActive()
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
snapshot.fatalError = msg
|
||||
await db.update(trafficSettings).set({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# UX/UI: протокол внешней приёмки 12.5
|
||||
|
||||
Актуальное продолжение 2026-10-13: завершён199 — SSO сохраняет исходный разрешённый раздел/query; unit/typecheck/ESLint,12 E2E и2 live expired recovery passed. Frontend ux-portal199/backend ux-peak196, Portal1.3.1. Оставшиеся return_to199 приняты после коррекции тестового URL. Следующий200 — crash/lock-loss traffic collector; диагностическая граница цельного Telegram197 сохраняется.12.5 открыта.
|
||||
Актуальное продолжение 2026-10-13: завершён200 — baseline lock-loss crash воспроизведён, исправленный collector survives/aborts stale run/recovery passed.71 owned SQL + concurrency/57603ms,1044 compiled matched, live traffic API/UI2 passed. Runtime backend ux-lock200/frontend ux-portal199. Следующий201 — цельный Telegram live с расширенной диагностикой и ограниченным бюджетом3 сообщений.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: завершён199 — SSO сохраняет исходный разрешённый раздел/query; unit/typecheck/ESLint,12 E2E и2 live expired recovery passed. Frontend ux-portal199/backend ux-peak196, Portal1.3.1. Оставшиеся return_to199 приняты после коррекции тестового URL. Следующий200 — crash/lock-loss traffic collector; диагностическая граница цельного Telegram197 сохраняется.12.5 открыта.
|
||||
Актуальное продолжение 2026-10-13: завершён200 — baseline lock-loss crash воспроизведён, исправленный collector survives/aborts stale run/recovery passed.71 owned SQL + concurrency/57603ms,1044 compiled matched, live traffic API/UI2 passed. Runtime backend ux-lock200/frontend ux-portal199. Следующий201 — цельный Telegram live с расширенной диагностикой и ограниченным бюджетом3 сообщений.12.5 открыта.
|
||||
|
||||
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
|
||||
|
||||
@@ -2432,3 +2432,17 @@ Unit unsafe/normalization/query/permissions passed, frontend TypeScript и scope
|
||||
| [ReUI App Shell12](https://reui.io/preview/base/app-shell-12?ref=mcp) | Раздел + фильтры → expired session → Portal → callback с проверкой прав → тот же раздел + фильтры |
|
||||
|
||||
Следующий200: owned PostgreSQL/child-process crash и advisory-lock loss traffic collector. Цельный непрерывный Telegram live197 остаётся отдельно с исходным failed evidence; успешные chunk tests не отменяют его. Logout не отозвал offlineJWT доexp, publicIPv6/clientOS/independentWAN/scale остаются внешними ограничениями.12.5 открыта.
|
||||
|
||||
## 12.5.200 — восстановление коллектора после падения и потери блокировки (2026-10-13)
|
||||
|
||||
В отдельной own DB/ограниченной роли на Linux два сценария действительного сбоя: SIGKILL дочернего коллектора при удержанном ответе локального RouterOS fixture и pg_terminate_backend только собственного lock PID, выбранного по точным datname/usename/key8723105. Production PID/CHR не затрагивались. Старый production compiled ux-peak196 воспроизвёл Unhandled error event и nonzero exit при потере session-lock connection. После SIGKILL PostgreSQL освободил advisory lock, новый процесс собрал первую волну. Baseline не выдаётся за passed исправление.
|
||||
|
||||
withTrafficCollectionLock теперь слушает error/end dedicated client, фиксирует потерю соединения, уничтожает его и снимает listeners. Callback получает assertActive, helper проверяет его также после callback. collectTrafficRun проверяет active guard перед проходом/источником, после чтения source-state, перед итоговыми writes и в catch ветках; известная потеря lock больше не приводит к записи error/settings старого прохода. collectTrafficOnce возвращает fatal snapshot, collecting сбрасывается finally. Unit подтвердил error/end без unhandled event, guard abort, destroy/listener cleanup; прежние busy/acquire/unlock cases сохранились.
|
||||
|
||||
Production compiled ux-lock200 на own DB: SIGKILL recovery passed; lock connection terminated во время held native response — процесс survived, fatal snapshot, ноль новых traffic_samples, last_collected_at не переписан. Следующий независимый процесс успешно собрал новую волну.3 own DB/role/container (baseline/fixed/profile) удалены и отсутствие проверено.71 профильная SQL case + cross-process skip/release/recovery passed57603ms, TypeScript build и scoped ESLint passed. Перед build468 source files совпали,1044 compiled artifacts matched. Backend ux-lock200 healthy развёрнут, frontend ux-portal199/Portal/AUTH_REQUIRED сохранены.3 real API200 и live UI1440/320 — provenance/received live labels, noerrors/overflow. Native CHR config/passwords не менялись.
|
||||
|
||||
Граница: active guard реагирует на наблюдённые error/end; это не fencing token и не атомарная защита всех writes на других pool connections. Окно между проверкой и write, задержка TCP detection и физическая network partition остаются ограничениями. Уже отправленный native HTTP GET не отменяется; tested response fixture лишь удерживал ответ. Не объявлять гарантию exactly-once при произвольном разрыве.
|
||||
|
||||
Протоколы: [baseline](ux-ui/acceptance/lifecycle200-baseline-result.json), [fixed lifecycle](ux-ui/acceptance/lifecycle200-fixed-result.json), [SQL profile](ux-ui/acceptance/owned200-result.json), [compiled](ux-ui/acceptance/compiled200-result.json), [deployment/bounds](ux-ui/acceptance/lock200-deployment-2026-10-13.json), [live traffic](ux-ui/acceptance/traffic-lock200-deployed-2026-10-13.json).
|
||||
|
||||
Следующий201: цельный Telegram live197 с диагностикой начала requests/response codes/dispatch outcome/status и ограничением3 сообщений в собственном чате. Controlled lost receipt остаётся имитацией; первоначальный failed evidence не удалять.12.5 открыта.
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
# План реализации UX/UI по пятичасовым окнам
|
||||
|
||||
Актуальное продолжение 2026-10-13: завершён199 — SSO сохраняет исходный разрешённый раздел/query; unit/typecheck/ESLint,12 E2E и2 live expired recovery passed. Frontend ux-portal199/backend ux-peak196, Portal1.3.1. Оставшиеся return_to199 приняты после коррекции тестового URL. Следующий200 — crash/lock-loss traffic collector; диагностическая граница цельного Telegram197 сохраняется.12.5 открыта.
|
||||
Актуальное продолжение 2026-10-13: завершён200 — baseline lock-loss crash воспроизведён, исправленный collector survives/aborts stale run/recovery passed.71 owned SQL + concurrency/57603ms,1044 compiled matched, live traffic API/UI2 passed. Runtime backend ux-lock200/frontend ux-portal199. Следующий201 — цельный Telegram live с расширенной диагностикой и ограниченным бюджетом3 сообщений.12.5 открыта.
|
||||
|
||||
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-13. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
||||
|
||||
## Актуальная точка продолжения — 2026-10-13
|
||||
|
||||
Актуальное продолжение 2026-10-13: завершён199 — SSO сохраняет исходный разрешённый раздел/query; unit/typecheck/ESLint,12 E2E и2 live expired recovery passed. Frontend ux-portal199/backend ux-peak196, Portal1.3.1. Оставшиеся return_to199 приняты после коррекции тестового URL. Следующий200 — crash/lock-loss traffic collector; диагностическая граница цельного Telegram197 сохраняется.12.5 открыта.
|
||||
Актуальное продолжение 2026-10-13: завершён200 — baseline lock-loss crash воспроизведён, исправленный collector survives/aborts stale run/recovery passed.71 owned SQL + concurrency/57603ms,1044 compiled matched, live traffic API/UI2 passed. Runtime backend ux-lock200/frontend ux-portal199. Следующий201 — цельный Telegram live с расширенной диагностикой и ограниченным бюджетом3 сообщений.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: завершён199 — SSO сохраняет исходный разрешённый раздел/query; unit/typecheck/ESLint,12 E2E и2 live expired recovery passed. Frontend ux-portal199/backend ux-peak196, Portal1.3.1. Оставшиеся return_to199 приняты после коррекции тестового URL. Следующий200 — crash/lock-loss traffic collector; диагностическая граница цельного Telegram197 сохраняется.12.5 открыта.
|
||||
Актуальное продолжение 2026-10-13: завершён200 — baseline lock-loss crash воспроизведён, исправленный collector survives/aborts stale run/recovery passed.71 owned SQL + concurrency/57603ms,1044 compiled matched, live traffic API/UI2 passed. Runtime backend ux-lock200/frontend ux-portal199. Следующий201 — цельный Telegram live с расширенной диагностикой и ограниченным бюджетом3 сообщений.12.5 открыта.
|
||||
|
||||
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
|
||||
+15
-1
@@ -1,6 +1,6 @@
|
||||
# UX/UI: журнал реализации
|
||||
|
||||
Актуальное продолжение 2026-10-13: завершён199 — SSO сохраняет исходный разрешённый раздел/query; unit/typecheck/ESLint,12 E2E и2 live expired recovery passed. Frontend ux-portal199/backend ux-peak196, Portal1.3.1. Оставшиеся return_to199 приняты после коррекции тестового URL. Следующий200 — crash/lock-loss traffic collector; диагностическая граница цельного Telegram197 сохраняется.12.5 открыта.
|
||||
Актуальное продолжение 2026-10-13: завершён200 — baseline lock-loss crash воспроизведён, исправленный collector survives/aborts stale run/recovery passed.71 owned SQL + concurrency/57603ms,1044 compiled matched, live traffic API/UI2 passed. Runtime backend ux-lock200/frontend ux-portal199. Следующий201 — цельный Telegram live с расширенной диагностикой и ограниченным бюджетом3 сообщений.12.5 открыта.
|
||||
|
||||
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
|
||||
|
||||
@@ -38,6 +38,20 @@
|
||||
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
|
||||
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–196 и ограничения ниже. Runtime backend/frontend ux-peak196. |
|
||||
|
||||
## 12.5.200 — восстановление коллектора после падения и потери блокировки (2026-10-13)
|
||||
|
||||
В отдельной own DB/ограниченной роли на Linux два сценария действительного сбоя: SIGKILL дочернего коллектора при удержанном ответе локального RouterOS fixture и pg_terminate_backend только собственного lock PID, выбранного по точным datname/usename/key8723105. Production PID/CHR не затрагивались. Старый production compiled ux-peak196 воспроизвёл Unhandled error event и nonzero exit при потере session-lock connection. После SIGKILL PostgreSQL освободил advisory lock, новый процесс собрал первую волну. Baseline не выдаётся за passed исправление.
|
||||
|
||||
withTrafficCollectionLock теперь слушает error/end dedicated client, фиксирует потерю соединения, уничтожает его и снимает listeners. Callback получает assertActive, helper проверяет его также после callback. collectTrafficRun проверяет active guard перед проходом/источником, после чтения source-state, перед итоговыми writes и в catch ветках; известная потеря lock больше не приводит к записи error/settings старого прохода. collectTrafficOnce возвращает fatal snapshot, collecting сбрасывается finally. Unit подтвердил error/end без unhandled event, guard abort, destroy/listener cleanup; прежние busy/acquire/unlock cases сохранились.
|
||||
|
||||
Production compiled ux-lock200 на own DB: SIGKILL recovery passed; lock connection terminated во время held native response — процесс survived, fatal snapshot, ноль новых traffic_samples, last_collected_at не переписан. Следующий независимый процесс успешно собрал новую волну.3 own DB/role/container (baseline/fixed/profile) удалены и отсутствие проверено.71 профильная SQL case + cross-process skip/release/recovery passed57603ms, TypeScript build и scoped ESLint passed. Перед build468 source files совпали,1044 compiled artifacts matched. Backend ux-lock200 healthy развёрнут, frontend ux-portal199/Portal/AUTH_REQUIRED сохранены.3 real API200 и live UI1440/320 — provenance/received live labels, noerrors/overflow. Native CHR config/passwords не менялись.
|
||||
|
||||
Граница: active guard реагирует на наблюдённые error/end; это не fencing token и не атомарная защита всех writes на других pool connections. Окно между проверкой и write, задержка TCP detection и физическая network partition остаются ограничениями. Уже отправленный native HTTP GET не отменяется; tested response fixture лишь удерживал ответ. Не объявлять гарантию exactly-once при произвольном разрыве.
|
||||
|
||||
Протоколы: [baseline](ux-ui/acceptance/lifecycle200-baseline-result.json), [fixed lifecycle](ux-ui/acceptance/lifecycle200-fixed-result.json), [SQL profile](ux-ui/acceptance/owned200-result.json), [compiled](ux-ui/acceptance/compiled200-result.json), [deployment/bounds](ux-ui/acceptance/lock200-deployment-2026-10-13.json), [live traffic](ux-ui/acceptance/traffic-lock200-deployed-2026-10-13.json).
|
||||
|
||||
Следующий201: цельный Telegram live197 с диагностикой начала requests/response codes/dispatch outcome/status и ограничением3 сообщений в собственном чате. Controlled lost receipt остаётся имитацией; первоначальный failed evidence не удалять.12.5 открыта.
|
||||
|
||||
## 12.5.199 — восстановление раздела и параметров после SSO (2026-10-13)
|
||||
|
||||
Из198 подтверждено: expired session на /servers попадала после login в /dashboard. redirectToPortalLogin теперь добавляет исходный pathname+query в next callback URL. AuthCallbackPage после valid claims выбирает permittedAuthReturnPath и проверяет существующее permissionForPath/can; недопустимый путь или недостаточные права используют firstAllowedPath. При повторном handoff next сохраняется. JWT fragment не включается в сохранённый путь.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Оставшаяся внешняя приёмка — 2026-10-13
|
||||
|
||||
Актуальное продолжение 2026-10-13: завершён199 — SSO сохраняет исходный разрешённый раздел/query; unit/typecheck/ESLint,12 E2E и2 live expired recovery passed. Frontend ux-portal199/backend ux-peak196, Portal1.3.1. Оставшиеся return_to199 приняты после коррекции тестового URL. Следующий200 — crash/lock-loss traffic collector; диагностическая граница цельного Telegram197 сохраняется.12.5 открыта.
|
||||
Актуальное продолжение 2026-10-13: завершён200 — baseline lock-loss crash воспроизведён, исправленный collector survives/aborts stale run/recovery passed.71 owned SQL + concurrency/57603ms,1044 compiled matched, live traffic API/UI2 passed. Runtime backend ux-lock200/frontend ux-portal199. Следующий201 — цельный Telegram live с расширенной диагностикой и ограниченным бюджетом3 сообщений.12.5 открыта.
|
||||
|
||||
Это актуальная матрица после 12.5.199. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
|
||||
Это актуальная матрица после 12.5.200. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
|
||||
|
||||
Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend/frontend `ux-peak196`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
## Следующая последовательность — 2026-10-13
|
||||
|
||||
1. **200 — crash/lock-loss collector:** собственная ограниченная БД и процессы, без остановки production PG или native CHR. Telegram197 whole-live failure остаётся отдельной диагностической границей.
|
||||
1. **201 — цельный Telegram live:**3 сообщения, pacing/trace, own DB; первичный failure197 не скрывать.200 crash/observed lock-loss принят с границами fencing/network partition.
|
||||
2. **Профильный контроль:** для новых изменений выполнять затронутые SQL/API/UI проверки. Общая регрессия192 выполнена; не повторять её без изменений общей основы или нового основания.
|
||||
3. **Внешние зависимости:** Portal issuer/login/revoke, Telegram bot/chat/разрешённое сообщение, клиентские ОС, независимые WAN/public IPv6 и scale/multi-instance — отдельные группы по таблице выше. Ограничения текущего стенда не превращать в обещание приёмки всех конфигураций.
|
||||
|
||||
@@ -1348,3 +1348,17 @@ Unit unsafe/normalization/query/permissions passed, frontend TypeScript и scope
|
||||
| [ReUI App Shell12](https://reui.io/preview/base/app-shell-12?ref=mcp) | Раздел + фильтры → expired session → Portal → callback с проверкой прав → тот же раздел + фильтры |
|
||||
|
||||
Следующий200: owned PostgreSQL/child-process crash и advisory-lock loss traffic collector. Цельный непрерывный Telegram live197 остаётся отдельно с исходным failed evidence; успешные chunk tests не отменяют его. Logout не отозвал offlineJWT доexp, publicIPv6/clientOS/independentWAN/scale остаются внешними ограничениями.12.5 открыта.
|
||||
|
||||
## 12.5.200 — восстановление коллектора после падения и потери блокировки (2026-10-13)
|
||||
|
||||
В отдельной own DB/ограниченной роли на Linux два сценария действительного сбоя: SIGKILL дочернего коллектора при удержанном ответе локального RouterOS fixture и pg_terminate_backend только собственного lock PID, выбранного по точным datname/usename/key8723105. Production PID/CHR не затрагивались. Старый production compiled ux-peak196 воспроизвёл Unhandled error event и nonzero exit при потере session-lock connection. После SIGKILL PostgreSQL освободил advisory lock, новый процесс собрал первую волну. Baseline не выдаётся за passed исправление.
|
||||
|
||||
withTrafficCollectionLock теперь слушает error/end dedicated client, фиксирует потерю соединения, уничтожает его и снимает listeners. Callback получает assertActive, helper проверяет его также после callback. collectTrafficRun проверяет active guard перед проходом/источником, после чтения source-state, перед итоговыми writes и в catch ветках; известная потеря lock больше не приводит к записи error/settings старого прохода. collectTrafficOnce возвращает fatal snapshot, collecting сбрасывается finally. Unit подтвердил error/end без unhandled event, guard abort, destroy/listener cleanup; прежние busy/acquire/unlock cases сохранились.
|
||||
|
||||
Production compiled ux-lock200 на own DB: SIGKILL recovery passed; lock connection terminated во время held native response — процесс survived, fatal snapshot, ноль новых traffic_samples, last_collected_at не переписан. Следующий независимый процесс успешно собрал новую волну.3 own DB/role/container (baseline/fixed/profile) удалены и отсутствие проверено.71 профильная SQL case + cross-process skip/release/recovery passed57603ms, TypeScript build и scoped ESLint passed. Перед build468 source files совпали,1044 compiled artifacts matched. Backend ux-lock200 healthy развёрнут, frontend ux-portal199/Portal/AUTH_REQUIRED сохранены.3 real API200 и live UI1440/320 — provenance/received live labels, noerrors/overflow. Native CHR config/passwords не менялись.
|
||||
|
||||
Граница: active guard реагирует на наблюдённые error/end; это не fencing token и не атомарная защита всех writes на других pool connections. Окно между проверкой и write, задержка TCP detection и физическая network partition остаются ограничениями. Уже отправленный native HTTP GET не отменяется; tested response fixture лишь удерживал ответ. Не объявлять гарантию exactly-once при произвольном разрыве.
|
||||
|
||||
Протоколы: [baseline](ux-ui/acceptance/lifecycle200-baseline-result.json), [fixed lifecycle](ux-ui/acceptance/lifecycle200-fixed-result.json), [SQL profile](ux-ui/acceptance/owned200-result.json), [compiled](ux-ui/acceptance/compiled200-result.json), [deployment/bounds](ux-ui/acceptance/lock200-deployment-2026-10-13.json), [live traffic](ux-ui/acceptance/traffic-lock200-deployed-2026-10-13.json).
|
||||
|
||||
Следующий201: цельный Telegram live197 с диагностикой начала requests/response codes/dispatch outcome/status и ограничением3 сообщений в собственном чате. Controlled lost receipt остаётся имитацией; первоначальный failed evidence не удалять.12.5 открыта.
|
||||
+15
-1
@@ -1,6 +1,6 @@
|
||||
# Этап 12: итоговая приёмка UX/UI
|
||||
|
||||
Актуальное продолжение 2026-10-13: завершён199 — SSO сохраняет исходный разрешённый раздел/query; unit/typecheck/ESLint,12 E2E и2 live expired recovery passed. Frontend ux-portal199/backend ux-peak196, Portal1.3.1. Оставшиеся return_to199 приняты после коррекции тестового URL. Следующий200 — crash/lock-loss traffic collector; диагностическая граница цельного Telegram197 сохраняется.12.5 открыта.
|
||||
Актуальное продолжение 2026-10-13: завершён200 — baseline lock-loss crash воспроизведён, исправленный collector survives/aborts stale run/recovery passed.71 owned SQL + concurrency/57603ms,1044 compiled matched, live traffic API/UI2 passed. Runtime backend ux-lock200/frontend ux-portal199. Следующий201 — цельный Telegram live с расширенной диагностикой и ограниченным бюджетом3 сообщений.12.5 открыта.
|
||||
|
||||
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
|
||||
|
||||
@@ -3737,3 +3737,17 @@ Unit unsafe/normalization/query/permissions passed, frontend TypeScript и scope
|
||||
| [ReUI App Shell12](https://reui.io/preview/base/app-shell-12?ref=mcp) | Раздел + фильтры → expired session → Portal → callback с проверкой прав → тот же раздел + фильтры |
|
||||
|
||||
Следующий200: owned PostgreSQL/child-process crash и advisory-lock loss traffic collector. Цельный непрерывный Telegram live197 остаётся отдельно с исходным failed evidence; успешные chunk tests не отменяют его. Logout не отозвал offlineJWT доexp, publicIPv6/clientOS/independentWAN/scale остаются внешними ограничениями.12.5 открыта.
|
||||
|
||||
## 12.5.200 — восстановление коллектора после падения и потери блокировки (2026-10-13)
|
||||
|
||||
В отдельной own DB/ограниченной роли на Linux два сценария действительного сбоя: SIGKILL дочернего коллектора при удержанном ответе локального RouterOS fixture и pg_terminate_backend только собственного lock PID, выбранного по точным datname/usename/key8723105. Production PID/CHR не затрагивались. Старый production compiled ux-peak196 воспроизвёл Unhandled error event и nonzero exit при потере session-lock connection. После SIGKILL PostgreSQL освободил advisory lock, новый процесс собрал первую волну. Baseline не выдаётся за passed исправление.
|
||||
|
||||
withTrafficCollectionLock теперь слушает error/end dedicated client, фиксирует потерю соединения, уничтожает его и снимает listeners. Callback получает assertActive, helper проверяет его также после callback. collectTrafficRun проверяет active guard перед проходом/источником, после чтения source-state, перед итоговыми writes и в catch ветках; известная потеря lock больше не приводит к записи error/settings старого прохода. collectTrafficOnce возвращает fatal snapshot, collecting сбрасывается finally. Unit подтвердил error/end без unhandled event, guard abort, destroy/listener cleanup; прежние busy/acquire/unlock cases сохранились.
|
||||
|
||||
Production compiled ux-lock200 на own DB: SIGKILL recovery passed; lock connection terminated во время held native response — процесс survived, fatal snapshot, ноль новых traffic_samples, last_collected_at не переписан. Следующий независимый процесс успешно собрал новую волну.3 own DB/role/container (baseline/fixed/profile) удалены и отсутствие проверено.71 профильная SQL case + cross-process skip/release/recovery passed57603ms, TypeScript build и scoped ESLint passed. Перед build468 source files совпали,1044 compiled artifacts matched. Backend ux-lock200 healthy развёрнут, frontend ux-portal199/Portal/AUTH_REQUIRED сохранены.3 real API200 и live UI1440/320 — provenance/received live labels, noerrors/overflow. Native CHR config/passwords не менялись.
|
||||
|
||||
Граница: active guard реагирует на наблюдённые error/end; это не fencing token и не атомарная защита всех writes на других pool connections. Окно между проверкой и write, задержка TCP detection и физическая network partition остаются ограничениями. Уже отправленный native HTTP GET не отменяется; tested response fixture лишь удерживал ответ. Не объявлять гарантию exactly-once при произвольном разрыве.
|
||||
|
||||
Протоколы: [baseline](ux-ui/acceptance/lifecycle200-baseline-result.json), [fixed lifecycle](ux-ui/acceptance/lifecycle200-fixed-result.json), [SQL profile](ux-ui/acceptance/owned200-result.json), [compiled](ux-ui/acceptance/compiled200-result.json), [deployment/bounds](ux-ui/acceptance/lock200-deployment-2026-10-13.json), [live traffic](ux-ui/acceptance/traffic-lock200-deployed-2026-10-13.json).
|
||||
|
||||
Следующий201: цельный Telegram live197 с диагностикой начала requests/response codes/dispatch outcome/status и ограничением3 сообщений в собственном чате. Controlled lost receipt остаётся имитацией; первоначальный failed evidence не удалять.12.5 открыта.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Тестовый сервер UX/UI — 2026-10-10
|
||||
|
||||
Актуальное продолжение 2026-10-13: завершён199 — SSO сохраняет исходный разрешённый раздел/query; unit/typecheck/ESLint,12 E2E и2 live expired recovery passed. Frontend ux-portal199/backend ux-peak196, Portal1.3.1. Оставшиеся return_to199 приняты после коррекции тестового URL. Следующий200 — crash/lock-loss traffic collector; диагностическая граница цельного Telegram197 сохраняется.12.5 открыта.
|
||||
Актуальное продолжение 2026-10-13: завершён200 — baseline lock-loss crash воспроизведён, исправленный collector survives/aborts stale run/recovery passed.71 owned SQL + concurrency/57603ms,1044 compiled matched, live traffic API/UI2 passed. Runtime backend ux-lock200/frontend ux-portal199. Следующий201 — цельный Telegram live с расширенной диагностикой и ограниченным бюджетом3 сообщений.12.5 открыта.
|
||||
|
||||
| Параметр | Проверенное состояние |
|
||||
| --- | --- |
|
||||
@@ -708,7 +708,7 @@ flowchart LR
|
||||
|
||||
## 12.5.77 — основной блок IPsec (2026-10-09)
|
||||
|
||||
Актуальное продолжение 2026-10-13: завершён199 — SSO сохраняет исходный разрешённый раздел/query; unit/typecheck/ESLint,12 E2E и2 live expired recovery passed. Frontend ux-portal199/backend ux-peak196, Portal1.3.1. Оставшиеся return_to199 приняты после коррекции тестового URL. Следующий200 — crash/lock-loss traffic collector; диагностическая граница цельного Telegram197 сохраняется.12.5 открыта.
|
||||
Актуальное продолжение 2026-10-13: завершён200 — baseline lock-loss crash воспроизведён, исправленный collector survives/aborts stale run/recovery passed.71 owned SQL + concurrency/57603ms,1044 compiled matched, live traffic API/UI2 passed. Runtime backend ux-lock200/frontend ux-portal199. Следующий201 — цельный Telegram live с расширенной диагностикой и ограниченным бюджетом3 сообщений.12.5 открыта.
|
||||
|
||||
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
|
||||
|
||||
@@ -2163,3 +2163,17 @@ Unit unsafe/normalization/query/permissions passed, frontend TypeScript и scope
|
||||
| [ReUI App Shell12](https://reui.io/preview/base/app-shell-12?ref=mcp) | Раздел + фильтры → expired session → Portal → callback с проверкой прав → тот же раздел + фильтры |
|
||||
|
||||
Следующий200: owned PostgreSQL/child-process crash и advisory-lock loss traffic collector. Цельный непрерывный Telegram live197 остаётся отдельно с исходным failed evidence; успешные chunk tests не отменяют его. Logout не отозвал offlineJWT доexp, publicIPv6/clientOS/independentWAN/scale остаются внешними ограничениями.12.5 открыта.
|
||||
|
||||
## 12.5.200 — восстановление коллектора после падения и потери блокировки (2026-10-13)
|
||||
|
||||
В отдельной own DB/ограниченной роли на Linux два сценария действительного сбоя: SIGKILL дочернего коллектора при удержанном ответе локального RouterOS fixture и pg_terminate_backend только собственного lock PID, выбранного по точным datname/usename/key8723105. Production PID/CHR не затрагивались. Старый production compiled ux-peak196 воспроизвёл Unhandled error event и nonzero exit при потере session-lock connection. После SIGKILL PostgreSQL освободил advisory lock, новый процесс собрал первую волну. Baseline не выдаётся за passed исправление.
|
||||
|
||||
withTrafficCollectionLock теперь слушает error/end dedicated client, фиксирует потерю соединения, уничтожает его и снимает listeners. Callback получает assertActive, helper проверяет его также после callback. collectTrafficRun проверяет active guard перед проходом/источником, после чтения source-state, перед итоговыми writes и в catch ветках; известная потеря lock больше не приводит к записи error/settings старого прохода. collectTrafficOnce возвращает fatal snapshot, collecting сбрасывается finally. Unit подтвердил error/end без unhandled event, guard abort, destroy/listener cleanup; прежние busy/acquire/unlock cases сохранились.
|
||||
|
||||
Production compiled ux-lock200 на own DB: SIGKILL recovery passed; lock connection terminated во время held native response — процесс survived, fatal snapshot, ноль новых traffic_samples, last_collected_at не переписан. Следующий независимый процесс успешно собрал новую волну.3 own DB/role/container (baseline/fixed/profile) удалены и отсутствие проверено.71 профильная SQL case + cross-process skip/release/recovery passed57603ms, TypeScript build и scoped ESLint passed. Перед build468 source files совпали,1044 compiled artifacts matched. Backend ux-lock200 healthy развёрнут, frontend ux-portal199/Portal/AUTH_REQUIRED сохранены.3 real API200 и live UI1440/320 — provenance/received live labels, noerrors/overflow. Native CHR config/passwords не менялись.
|
||||
|
||||
Граница: active guard реагирует на наблюдённые error/end; это не fencing token и не атомарная защита всех writes на других pool connections. Окно между проверкой и write, задержка TCP detection и физическая network partition остаются ограничениями. Уже отправленный native HTTP GET не отменяется; tested response fixture лишь удерживал ответ. Не объявлять гарантию exactly-once при произвольном разрыве.
|
||||
|
||||
Протоколы: [baseline](ux-ui/acceptance/lifecycle200-baseline-result.json), [fixed lifecycle](ux-ui/acceptance/lifecycle200-fixed-result.json), [SQL profile](ux-ui/acceptance/owned200-result.json), [compiled](ux-ui/acceptance/compiled200-result.json), [deployment/bounds](ux-ui/acceptance/lock200-deployment-2026-10-13.json), [live traffic](ux-ui/acceptance/traffic-lock200-deployed-2026-10-13.json).
|
||||
|
||||
Следующий201: цельный Telegram live197 с диагностикой начала requests/response codes/dispatch outcome/status и ограничением3 сообщений в собственном чате. Controlled lost receipt остаётся имитацией; первоначальный failed evidence не удалять.12.5 открыта.
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"date": "2026-10-13",
|
||||
"block": "12.5.200",
|
||||
"database": "mm_accept_166_1791882888802",
|
||||
"baseline": true,
|
||||
"cases": [
|
||||
{
|
||||
"name": "process crash releases advisory lock",
|
||||
"signal": "SIGKILL",
|
||||
"crashedWaveRows": 0,
|
||||
"recoveryWaveRows": 1
|
||||
},
|
||||
{
|
||||
"name": "lock connection loss baseline",
|
||||
"unhandledClientError": true,
|
||||
"nonzeroChildExit": true
|
||||
},
|
||||
{
|
||||
"name": "next process recovers after connection loss",
|
||||
"waveRows": 2
|
||||
}
|
||||
],
|
||||
"passed": true,
|
||||
"scope": "Production compiled collector, independent Linux child processes, owned restricted PostgreSQL and local RouterOS HTTP fixture; no production PID/native CHR touched",
|
||||
"interfaceRequests": 4
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"date": "2026-10-13",
|
||||
"block": "12.5.200",
|
||||
"database": "mm_accept_166_1791882999162",
|
||||
"baseline": false,
|
||||
"cases": [
|
||||
{
|
||||
"name": "process crash releases advisory lock",
|
||||
"signal": "SIGKILL",
|
||||
"crashedWaveRows": 0,
|
||||
"recoveryWaveRows": 1
|
||||
},
|
||||
{
|
||||
"name": "lock connection loss",
|
||||
"collectorProcessSurvived": true,
|
||||
"fatalSnapshot": true,
|
||||
"staleSampleRows": 0,
|
||||
"staleSettingsWrite": false
|
||||
},
|
||||
{
|
||||
"name": "next process recovers after connection loss",
|
||||
"waveRows": 2
|
||||
}
|
||||
],
|
||||
"passed": true,
|
||||
"scope": "Production compiled collector, independent Linux child processes, owned restricted PostgreSQL and local RouterOS HTTP fixture; no production PID/native CHR touched",
|
||||
"interfaceRequests": 4
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"date": "2026-10-13",
|
||||
"block": "12.5.200",
|
||||
"sourceFilesMatched": 468,
|
||||
"compiledFilesMatched": 1044,
|
||||
"backendImage": "mmapp-test/backend:ux-lock200",
|
||||
"imageId": "sha256:5bebf46bb5bb6844565529b9c7e125e4a41b1bd5529de089465e8df47d4fcd9a6",
|
||||
"frontendImage": "mmapp-test/frontend:ux-portal199",
|
||||
"healthy": true,
|
||||
"ownDatabasesRolesContainersRemoved": 3,
|
||||
"productionProcessesKilled": 0,
|
||||
"chrChanges": 0,
|
||||
"limits": [
|
||||
"Guards react after pg error/end is observed; no atomic fencing of writes across separate pooled connections",
|
||||
"Already issued native HTTP request cannot be unsent; delayed TCP failure detection and true network partition not tested"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"date": "2026-10-13",
|
||||
"block": "12.5.200",
|
||||
"database": "mm_accept_166_1791883011630",
|
||||
"scope": "ephemeral role and precreated owned database; no copied production credentials",
|
||||
"tests": [
|
||||
{
|
||||
"command": "npm --prefix backend run test:traffic-rate",
|
||||
"passed": true,
|
||||
"durationMs": 57603,
|
||||
"ownedTrafficSqlCases": 9,
|
||||
"ownedHistorySqlCases": 14,
|
||||
"ownedLiveSqlCases": 10,
|
||||
"ownedVolumeSqlCases": 17,
|
||||
"ownedStatusSqlCases": 21,
|
||||
"ownedConcurrencySqlSequencePassed": true,
|
||||
"postgresUnavailableSkip": false
|
||||
}
|
||||
],
|
||||
"scopedRoleVerified": true,
|
||||
"passed": true
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"date": "2026-10-13",
|
||||
"block": "12.5.200",
|
||||
"runtime": {
|
||||
"backend": "ux-lock200",
|
||||
"frontend": "ux-portal199"
|
||||
},
|
||||
"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"
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user