use a fasthttp request for metrics push
This commit is contained in:
parent
e31451bae5
commit
c424476f55
@ -3,6 +3,7 @@ import { PrometheusContentType, Registry, Pushgateway, Summary, Counter, registe
|
|||||||
import { DoormanLambdaContext } from "./DoormanHandlerContext";
|
import { DoormanLambdaContext } from "./DoormanHandlerContext";
|
||||||
import { shouldBlockRequest } from "../utils/blockUserAgent";
|
import { shouldBlockRequest } from "../utils/blockUserAgent";
|
||||||
import { RequestOptions } from "https";
|
import { RequestOptions } from "https";
|
||||||
|
import { FastHttpPushgateway } from "../metrics/FastHttpPromGateway";
|
||||||
|
|
||||||
export type BaseEvent = { request: { cookies: {}; headers: {}; }; }
|
export type BaseEvent = { request: { cookies: {}; headers: {}; }; }
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ export function withMetrics<T extends DoormanLambdaContext, U extends BaseEvent>
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const pushGateway = new Pushgateway(context.PUSHGATEWAY_URL, requestOptions, metricsRegistry);
|
const pushGateway = new FastHttpPushgateway(context.PUSHGATEWAY_URL, requestOptions, metricsRegistry);
|
||||||
|
|
||||||
metricsRegistry.registerMetric(new Summary({
|
metricsRegistry.registerMetric(new Summary({
|
||||||
name: CommonMetrics.RUNTIME,
|
name: CommonMetrics.RUNTIME,
|
||||||
@ -111,7 +112,7 @@ export function withMetrics<T extends DoormanLambdaContext, U extends BaseEvent>
|
|||||||
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(() => {
|
||||||
console.error("[CommonHandler] cutting it too close, abandoning metrics");
|
console.log("[CommonHandler] cutting it too close, abandoning metrics");
|
||||||
callback(...result);
|
callback(...result);
|
||||||
}, remainingTime - 250);
|
}, remainingTime - 250);
|
||||||
|
|
||||||
@ -127,11 +128,9 @@ export function withMetrics<T extends DoormanLambdaContext, U extends BaseEvent>
|
|||||||
});
|
});
|
||||||
console.log("[CommonHandler] pushed metrics successfully");
|
console.log("[CommonHandler] pushed metrics successfully");
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error("[CommonHandler] failed to push metrics, quietly discarding them", e);
|
console.log("[CommonHandler] failed to push metrics, quietly discarding them", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
clearTimeout(metricsTimeout);
|
clearTimeout(metricsTimeout);
|
||||||
callback(...result);
|
callback(...result);
|
||||||
};
|
};
|
||||||
|
|||||||
90
packages/doorman-api/src/metrics/FastHttpPromGateway.ts
Normal file
90
packages/doorman-api/src/metrics/FastHttpPromGateway.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { Pushgateway, Registry, RegistryContentType } from "prom-client";
|
||||||
|
import url from "url";
|
||||||
|
import http from "http";
|
||||||
|
import https from "https";
|
||||||
|
import { gzipSync } from "zlib";
|
||||||
|
|
||||||
|
// copied mostly from pushgateway.js implementation. Just swapping out the http call to not await a response
|
||||||
|
|
||||||
|
export class FastHttpPushgateway<T extends RegistryContentType> extends Pushgateway<T> {
|
||||||
|
constructor(url: string, options?: any, registry?: Registry<T>) {
|
||||||
|
super(url, options, registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
override push(params: Pushgateway.Parameters): Promise<{ resp?: unknown; body?: unknown; }> {
|
||||||
|
let method = "PUT";
|
||||||
|
let job = params.jobName;
|
||||||
|
let groupings = params.groupings;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
let gatewayUrl: string = this.gatewayUrl;
|
||||||
|
// @ts-ignore
|
||||||
|
let requestOptions: any = this.requestOptions;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
let registry: Registry = this.registry;
|
||||||
|
|
||||||
|
// `URL` first added in v6.13.0
|
||||||
|
// eslint-disable-next-line n/no-deprecated-api
|
||||||
|
const gatewayUrlParsed = url.parse(gatewayUrl);
|
||||||
|
const gatewayUrlPath =
|
||||||
|
gatewayUrlParsed.pathname && gatewayUrlParsed.pathname !== '/'
|
||||||
|
? gatewayUrlParsed.pathname
|
||||||
|
: '';
|
||||||
|
const jobPath = job
|
||||||
|
? `/job/${encodeURIComponent(job)}${generateGroupings(groupings)}`
|
||||||
|
: '';
|
||||||
|
const path = `${gatewayUrlPath}/metrics${jobPath}`;
|
||||||
|
|
||||||
|
// eslint-disable-next-line n/no-deprecated-api
|
||||||
|
const target = url.resolve(gatewayUrl, path);
|
||||||
|
// eslint-disable-next-line n/no-deprecated-api
|
||||||
|
const requestParams = url.parse(target);
|
||||||
|
const httpModule = isHttps(requestParams.href) ? https : http;
|
||||||
|
const options = Object.assign(requestParams, requestOptions, {
|
||||||
|
method,
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (method === 'DELETE' && options.headers) {
|
||||||
|
delete options.headers['Content-Encoding'];
|
||||||
|
}
|
||||||
|
const req = httpModule.request(options);
|
||||||
|
|
||||||
|
// write metrics
|
||||||
|
registry
|
||||||
|
.metrics()
|
||||||
|
.then(metrics => {
|
||||||
|
if (
|
||||||
|
options.headers &&
|
||||||
|
options.headers['Content-Encoding'] === 'gzip'
|
||||||
|
) {
|
||||||
|
metrics = gzipSync(metrics) as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write metrics, and once thats done, end req. Don't wait for response
|
||||||
|
req.write(metrics, () => {
|
||||||
|
req.end(() => {
|
||||||
|
resolve({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateGroupings(groupings: any) {
|
||||||
|
if (!groupings) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return Object.keys(groupings)
|
||||||
|
.map(
|
||||||
|
key =>
|
||||||
|
`/${encodeURIComponent(key)}/${encodeURIComponent(groupings[key])}`,
|
||||||
|
)
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isHttps(href: string) {
|
||||||
|
return href.search(/^https/) !== -1;
|
||||||
|
}
|
||||||
@ -135,11 +135,11 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
|
|||||||
.inc({ door: config.door }, 1);
|
.inc({ door: config.door }, 1);
|
||||||
|
|
||||||
const twiml = dialFallbackTwiml(config);
|
const twiml = dialFallbackTwiml(config);
|
||||||
console.error(
|
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);
|
||||||
}, 9500));
|
}, 9000));
|
||||||
});
|
});
|
||||||
|
|
||||||
const twiml = await Promise.race([unlockPromise, gracefulFallbackPromise, ungracefulFallbackPromise]);
|
const twiml = await Promise.race([unlockPromise, gracefulFallbackPromise, ungracefulFallbackPromise]);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user