give more time to push metrics before exiting
Some checks failed
Build and push image for doorman-homeassistant / docker (push) Failing after 10m55s
Build and push Doorman UI / API / docker (push) Successful in 1m35s
Build and push image for doorman-homeassistant / deploy-gitainer (push) Has been cancelled

This commit is contained in:
Martin Dimitrov 2025-03-03 15:22:03 -08:00
parent 3b763ae5e7
commit 4817ed93d2
2 changed files with 33 additions and 13 deletions

View File

@ -24,6 +24,7 @@ export enum CommonMetrics {
HTTP_CLIENT_ERROR = "HttpClientError", HTTP_CLIENT_ERROR = "HttpClientError",
LOKI_ERROR = "LokiError", LOKI_ERROR = "LokiError",
LOKI_LOG_AFTER_CLOSE = "LokiLogAfterClose", LOKI_LOG_AFTER_CLOSE = "LokiLogAfterClose",
TWILIO_ISOLATION_BUSTED = "TwilioIsolationBusted",
}; };
export function getMetricFromRegistry<T>(metricsRegistry: Registry, metric: string): T { export function getMetricFromRegistry<T>(metricsRegistry: Registry, metric: string): T {
@ -33,7 +34,6 @@ export function getMetricFromRegistry<T>(metricsRegistry: Registry, metric: stri
export function gracefullyEndLogger(logger: Logger): Promise<void> { export function gracefullyEndLogger(logger: Logger): Promise<void> {
return new Promise((fulfill, reject) => { return new Promise((fulfill, reject) => {
// Add logic for other transports here ... // Add logic for other transports here ...
logger.on('finish', () => { logger.on('finish', () => {
fulfill(); fulfill();
}); });
@ -42,6 +42,9 @@ export function gracefullyEndLogger(logger: Logger): Promise<void> {
}); });
} }
const MINIMUM_MS_TO_SEND_RESPONSE: number = 250;
const FUNCTION_MAXIMUM_DURATION_MS: number = 10_000;
/** /**
* A decorator for twilio handlers. It provides a metrics registry and * A decorator for twilio handlers. It provides a metrics registry and
* should implement timeout and cleanup jobs based on lambda timeout * should implement timeout and cleanup jobs based on lambda timeout
@ -92,6 +95,11 @@ export function withMetrics<T extends DoormanLambdaContext, U extends BaseEvent>
help: "Tried to write logs after log stream closed", help: "Tried to write logs after log stream closed",
})); }));
metricsRegistry.registerMetric(new Counter({
name: CommonMetrics.TWILIO_ISOLATION_BUSTED,
help: "Twilio invocation lasted longer than 10s",
}));
// Create a Winston logger // Create a Winston logger
const logger = createLogger({ const logger = createLogger({
level: 'info', level: 'info',
@ -199,13 +207,25 @@ export function withMetrics<T extends DoormanLambdaContext, U extends BaseEvent>
console.timeEnd("[CommonHandler] nested handler time"); console.timeEnd("[CommonHandler] nested handler time");
const endTime = Date.now(); const endTime = Date.now();
const remainingTime = 10000 - (endTime - startTime); const remainingTime = FUNCTION_MAXIMUM_DURATION_MS - (endTime - startTime);
if (remainingTime < 0) {
console.error("[CommonHandler] remaining time is negative for some reason...");
getMetricFromRegistry<Counter>(metricsRegistry, CommonMetrics.TWILIO_ISOLATION_BUSTED).inc(1);
}
// abandoning metrics, since there isn't enough time
if (remainingTime <= MINIMUM_MS_TO_SEND_RESPONSE) {
console.error("[CommonHandler] there is no time to send metrics / logs abandoning them");
callback(...result);
return;
}
console.log(`[CommonHandler] there is ${remainingTime} ms left to send metrics`); console.log(`[CommonHandler] there is ${remainingTime} ms left to send metrics`);
let metricsTimeout = setTimeout(() => { let metricsTimeout = setTimeout(() => {
callback(...result); callback(...result);
}, remainingTime - 250); }, remainingTime - MINIMUM_MS_TO_SEND_RESPONSE);
summaryTimer(); summaryTimer();
console.log("[CommonHandler] attempting to push metrics..."); console.log("[CommonHandler] attempting to push metrics...");

View File

@ -26,7 +26,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
let invokeId = `[${randomUUID()}]`; let invokeId = `[${randomUUID()}]`;
let configString = event.config; let configString = event.config;
let config: InfoResponseClient | undefined; let config: InfoResponseClient | undefined;
console.log(invokeId, "starting execution"); console.log(invokeId + " starting execution");
// get by api or parse it out from query // get by api or parse it out from query
if (!configString) { if (!configString) {
@ -82,7 +82,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
if (!discordLock) { if (!discordLock) {
discordLock = true; discordLock = true;
console.log( console.log(
invokeId, "UnlockPromise: I was the fastest, so I will attempt to notify discord users before resolving with unlock" invokeId + " UnlockPromise: I was the fastest, so I will attempt to notify discord users before resolving with unlock"
); );
getMetricFromRegistry<Counter>(metricsRegistry, BuzzerActivatedMetrics.API_UNLOCK) getMetricFromRegistry<Counter>(metricsRegistry, BuzzerActivatedMetrics.API_UNLOCK)
.inc({ door: config.door }, 1); .inc({ door: config.door }, 1);
@ -91,11 +91,11 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
resolve(twiml); resolve(twiml);
} else { } else {
console.log( console.log(
invokeId, "UnlockPromise: dropping out of the race, graceful fallback is already notifiying discord users" invokeId + " UnlockPromise: dropping out of the race, graceful fallback is already notifiying discord users"
); );
} }
} }
}).catch(err => console.log(invokeId, err)); }).catch(err => console.log(invokeId + err));
}, 1250)); }, 1250));
}); });
@ -109,7 +109,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
.inc({ door: config.door }, 1); .inc({ door: config.door }, 1);
console.log( console.log(
invokeId, "GracefulFallbackPromise: I was the fastest, so I will attempt to notify discord users before resolving with a call" invokeId + " GracefulFallbackPromise: I was the fastest, so I will attempt to notify discord users before resolving with a call"
); );
await notifyAllDiscord( await notifyAllDiscord(
context, context,
@ -120,10 +120,10 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
resolve(twiml); resolve(twiml);
} else { } else {
console.log( console.log(
invokeId, "GracefulFallbackPromise: dropping out of the race, unlock is already notifying discord users" invokeId + " GracefulFallbackPromise: dropping out of the race, unlock is already notifying discord users"
); );
} }
}, 8000)); }, 7500));
}); });
const ungracefulFallbackPromise = new Promise<VoiceResponse>((resolve, reject) => { const ungracefulFallbackPromise = new Promise<VoiceResponse>((resolve, reject) => {
@ -136,14 +136,14 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
const twiml = dialFallbackTwiml(config); const twiml = dialFallbackTwiml(config);
console.log( console.log(
invokeId, "UngracefulFallbackPromise: Cutting it too close to timeout! Skipping notifying users and calling fallback" invokeId + " UngracefulFallbackPromise: Cutting it too close to timeout! Skipping notifying users and calling fallback"
); );
resolve(twiml); resolve(twiml);
}, 9000)); }, 8500));
}); });
const twiml = await Promise.race([unlockPromise, gracefulFallbackPromise, ungracefulFallbackPromise]); const twiml = await Promise.race([unlockPromise, gracefulFallbackPromise, ungracefulFallbackPromise]);
console.log(invokeId, "Race ended, clearing residual timers"); console.log(invokeId + " Race ended, clearing residual timers");
timeouts.forEach(timeout => clearTimeout(timeout)); timeouts.forEach(timeout => clearTimeout(timeout));
intervals.forEach(interval => clearInterval(interval)); intervals.forEach(interval => clearInterval(interval));
callback(null, twiml); callback(null, twiml);