"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.spawnMetricsWorker = void 0;
const worker_threads_1 = require("worker_threads");
const http_1 = require("http");
const prom_client_1 = require("prom-client");
const logging_1 = __importDefault(require("../logging"));
const METRICS_DUMP_TIMEOUT_MS = 20000;
function writeLog(level, msg) {
    return worker_threads_1.parentPort?.postMessage(`log:${level}:${msg}`);
}
function workerThread() {
    let lastDumpTs = Date.now();
    const registry = new prom_client_1.Registry();
    const intervalCounter = new prom_client_1.Gauge({
        name: "metrics_worker_interval",
        help: "Interval time for metrics being reported to the metrics worker process",
        registers: [registry]
    });
    if (!worker_threads_1.parentPort) {
        throw Error("Missing parentPort");
    }
    const writeAndEnd = (res, data) => {
        if (res.writable) {
            res.write(data);
        }
        if (!res.finished) {
            res.end();
        }
    };
    (0, http_1.createServer)((req, res) => {
        res.setHeader("Content-Type", "text/plain");
        if (!req.url || req.url !== "/metrics" || req.method !== "GET") {
            res.statusCode = 404;
            res.write('Path or method not known');
            res.end();
            return;
        }
        writeLog("debug", "Request for /metrics");
        const timeout = setTimeout(async () => {
            intervalCounter.inc(METRICS_DUMP_TIMEOUT_MS);
            res.statusCode = 200;
            res.writeHead(200);
            writeAndEnd(res, await registry.metrics());
        }, METRICS_DUMP_TIMEOUT_MS);
        // We've checked for the existence of parentPort above.
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        worker_threads_1.parentPort.once("message", async (msg) => {
            clearTimeout(timeout);
            const time = Date.now();
            intervalCounter.set(time - lastDumpTs);
            lastDumpTs = time;
            const dump = msg.substring('metricsdump:'.length);
            if (res.finished) {
                // Sometimes a message will come in far too late because we've already
                // sent an empty response. Drop it here.
                return;
            }
            res.writeHead(200);
            writeAndEnd(res, `${dump}\n${await registry.metrics()}`);
        });
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        worker_threads_1.parentPort.postMessage("metricsdump");
    }).listen(worker_threads_1.workerData.port, worker_threads_1.workerData.hostname, 1);
}
function spawnMetricsWorker(port, hostname = "127.0.0.1", onMetricsRequested) {
    const worker = new worker_threads_1.Worker(__filename, { workerData: { port, hostname } });
    const workerLogger = (0, logging_1.default)("MetricsWorker");
    worker.on("message", async (msg) => {
        if (msg === "metricsdump") {
            worker.postMessage("metricsdump:" + await onMetricsRequested());
        }
        else if (msg.startsWith("log")) {
            const [, logLevel, logMsg] = msg.split(":");
            workerLogger.log(logLevel, logMsg, { loggerName: "MetricsWorker" });
        }
    });
    return worker;
}
exports.spawnMetricsWorker = spawnMetricsWorker;
if (!worker_threads_1.isMainThread) {
    workerThread();
}
//# sourceMappingURL=MetricsWorker.js.map