Files
MikrotikManager/backend/src/services/traffic-collection-lock.ts
T

36 lines
1.7 KiB
TypeScript

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:(assertActive:()=>void)=>Promise<T>):Promise<{acquired:false}|{acquired:true;value:T}> {
const client=await connect()
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
const value=await run(assertActive)
assertActive()
return {acquired:true,value}
}finally{
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)
}
}