36 lines
1.7 KiB
TypeScript
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)
|
|
}
|
|
}
|