This commit is contained in:
@@ -5,6 +5,19 @@
|
||||
|
||||
const locks = new Map()
|
||||
|
||||
/**
|
||||
* Get or create a queue for a lock key.
|
||||
* @param {string} lockKey - Lock key
|
||||
* @returns {Promise<any>} Existing or new queue promise
|
||||
*/
|
||||
const getOrCreateQueue = (lockKey) => {
|
||||
const existingQueue = locks.get(lockKey)
|
||||
if (existingQueue) return existingQueue
|
||||
const newQueue = Promise.resolve()
|
||||
locks.set(lockKey, newQueue)
|
||||
return newQueue
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire a lock for a key and execute callback.
|
||||
* Only one callback per key executes at a time.
|
||||
@@ -14,12 +27,7 @@ const locks = new Map()
|
||||
*/
|
||||
export async function acquire(key, callback) {
|
||||
const lockKey = String(key)
|
||||
let queue = locks.get(lockKey)
|
||||
|
||||
if (!queue) {
|
||||
queue = Promise.resolve()
|
||||
locks.set(lockKey, queue)
|
||||
}
|
||||
const queue = getOrCreateQueue(lockKey)
|
||||
|
||||
const next = queue.then(() => callback()).finally(() => {
|
||||
if (locks.get(lockKey) === next) {
|
||||
|
||||
@@ -15,21 +15,20 @@ const TRADITIONAL_DELIMITER = Buffer.from('</event>', 'utf8')
|
||||
/**
|
||||
* @param {Buffer} buf
|
||||
* @param {number} offset
|
||||
* @param {number} value - Accumulated value
|
||||
* @param {number} shift - Current bit shift
|
||||
* @param {number} bytesRead - Bytes consumed so far
|
||||
* @returns {{ value: number, bytesRead: number }} Decoded varint and bytes consumed.
|
||||
*/
|
||||
function readVarint(buf, offset) {
|
||||
let value = 0
|
||||
let shift = 0
|
||||
let bytesRead = 0
|
||||
while (offset + bytesRead < buf.length) {
|
||||
const b = buf[offset + bytesRead]
|
||||
bytesRead += 1
|
||||
value += (b & 0x7F) << shift
|
||||
if ((b & 0x80) === 0) return { value, bytesRead }
|
||||
shift += 7
|
||||
if (shift > 28) return { value: 0, bytesRead: 0 }
|
||||
}
|
||||
return { value, bytesRead }
|
||||
function readVarint(buf, offset, value = 0, shift = 0, bytesRead = 0) {
|
||||
if (offset + bytesRead >= buf.length) return { value, bytesRead }
|
||||
const b = buf[offset + bytesRead]
|
||||
const newValue = value + ((b & 0x7F) << shift)
|
||||
const newBytesRead = bytesRead + 1
|
||||
if ((b & 0x80) === 0) return { value: newValue, bytesRead: newBytesRead }
|
||||
const newShift = shift + 7
|
||||
if (newShift > 28) return { value: 0, bytesRead: 0 }
|
||||
return readVarint(buf, offset, newValue, newShift, newBytesRead)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,12 +126,14 @@ export function parseCotPayload(payload) {
|
||||
const uid = String(event['@_uid'] ?? event.uid ?? '')
|
||||
const eventType = String(event['@_type'] ?? event.type ?? '')
|
||||
const point = findInObject(parsed, 'point') ?? findInObject(event, 'point')
|
||||
let lat = Number.NaN
|
||||
let lng = Number.NaN
|
||||
if (point && typeof point === 'object') {
|
||||
lat = Number(point['@_lat'] ?? point.lat)
|
||||
lng = Number(point['@_lon'] ?? point.lon ?? point['@_lng'] ?? point.lng)
|
||||
const extractCoords = (pt) => {
|
||||
if (!pt || typeof pt !== 'object') return { lat: Number.NaN, lng: Number.NaN }
|
||||
return {
|
||||
lat: Number(pt['@_lat'] ?? pt.lat),
|
||||
lng: Number(pt['@_lon'] ?? pt.lon ?? pt['@_lng'] ?? pt.lng),
|
||||
}
|
||||
}
|
||||
const { lat, lng } = extractCoords(point)
|
||||
if (!Number.isFinite(lat) || !Number.isFinite(lng)) return null
|
||||
|
||||
const detail = findInObject(parsed, 'detail')
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
import { SHUTDOWN_TIMEOUT_MS } from './constants.js'
|
||||
|
||||
const cleanupFunctions = []
|
||||
let isShuttingDown = false
|
||||
const shutdownState = {
|
||||
isShuttingDown: false,
|
||||
}
|
||||
|
||||
export function clearCleanup() {
|
||||
cleanupFunctions.length = 0
|
||||
isShuttingDown = false
|
||||
shutdownState.isShuttingDown = false
|
||||
}
|
||||
|
||||
export function registerCleanup(fn) {
|
||||
@@ -17,17 +19,25 @@ export function registerCleanup(fn) {
|
||||
cleanupFunctions.push(fn)
|
||||
}
|
||||
|
||||
async function executeCleanup() {
|
||||
if (isShuttingDown) return
|
||||
isShuttingDown = true
|
||||
for (let i = cleanupFunctions.length - 1; i >= 0; i--) {
|
||||
try {
|
||||
await cleanupFunctions[i]()
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`[shutdown] Cleanup function ${i} failed:`, error?.message || String(error))
|
||||
}
|
||||
const executeCleanupFunction = async (fn, index) => {
|
||||
try {
|
||||
await fn()
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`[shutdown] Cleanup function ${index} failed:`, error?.message || String(error))
|
||||
}
|
||||
}
|
||||
|
||||
const executeCleanupReverse = async (functions, index = functions.length - 1) => {
|
||||
if (index < 0) return
|
||||
await executeCleanupFunction(functions[index], index)
|
||||
return executeCleanupReverse(functions, index - 1)
|
||||
}
|
||||
|
||||
async function executeCleanup() {
|
||||
if (shutdownState.isShuttingDown) return
|
||||
shutdownState.isShuttingDown = true
|
||||
await executeCleanupReverse(cleanupFunctions)
|
||||
}
|
||||
|
||||
export async function graceful(error) {
|
||||
|
||||
Reference in New Issue
Block a user