'use strict';
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.restart = restart;
exports.restartService = restartService;
const minimist_1 = __importDefault(require("minimist"));
const worker_threads_1 = require("worker_threads");
const hdbTerms = __importStar(require("../utility/hdbTerms.js"));
const harper_logger_ts_1 = __importDefault(require("../utility/logging/harper_logger.js"));
const processMan = __importStar(require("../utility/processManagement/processManagement.js"));
const copyDb_ts_1 = require("./copyDb.js");
const manageThreads_js_1 = require("../server/threads/manageThreads.js");
const hdbError_ts_1 = require("../utility/errors/hdbError.js");
const { HTTP_STATUS_CODES } = hdbError_ts_1.hdbErrors;
const envMgr = __importStar(require("../utility/environment/environmentManager.js"));
const path = __importStar(require("node:path"));
const node_fs_1 = require("node:fs");
const nodeName_ts_1 = require("../server/nodeName.js");
envMgr.initSync();
const RESTART_RESPONSE = `Restarting Harper. This may take up to ${hdbTerms.RESTART_TIMEOUT_MS / 1000} seconds.`;
const INVALID_SERVICE_ERR = 'Invalid service';
let calledFromCli;
// Add ITC event listener to main thread which will be called from child that receives restart request.
if (worker_threads_1.isMainThread) {
    (0, manageThreads_js_1.onMessageByType)(hdbTerms.ITC_EVENT_TYPES.RESTART, async (message, port) => {
        if (message.workerType)
            await restartService({ service: message.workerType });
        else
            restart({ operation: 'restart' });
        port.postMessage({ type: 'restart-complete' });
    });
}
/**
 * Restart Harper.
 * It will restart all the child threads and the hub and leaf server processes.
 * @param req
 * @returns {Promise<string>}
 */
async function restart(req) {
    calledFromCli = Object.keys(req).length === 0;
    const cliArgs = (0, minimist_1.default)(process.argv);
    if (cliArgs.service) {
        await restartService(cliArgs);
        return;
    }
    if (calledFromCli) {
        const hdbPid = processMan.getHdbPid();
        console.error(hdbPid ? 'Restarting Harper...' : 'Starting Harper...');
        require('./run').launch(true);
        return RESTART_RESPONSE;
    }
    if (worker_threads_1.isMainThread) {
        harper_logger_ts_1.default.notify(RESTART_RESPONSE);
        if (envMgr.get(hdbTerms.CONFIG_PARAMS.STORAGE_COMPACTONSTART))
            await (0, copyDb_ts_1.compactOnStart)();
        setTimeout(async () => {
            // It seems like you should just be able to start the other process and kill this process and everything should
            // be cleaned up, however that doesn't work for some reason; the socket listening fds somehow get transferred to the
            // child process if they are not explicitly closed. And when transferred they are orphaned listening, accepting
            // connections and hanging. So we need to explicitly close down all the workers and then start the new process
            // and shut down.
            harper_logger_ts_1.default.debug('Shutdown workers');
            await (0, manageThreads_js_1.shutdownWorkersNow)();
            const { closeServers } = require('../server/threads/threadServer.js');
            await closeServers();
            await processMan.cleanupChildrenProcesses(false);
            // remove pid file so it doesn't trip up the launch
            (0, node_fs_1.unlinkSync)(path.join(envMgr.get(hdbTerms.CONFIG_PARAMS.ROOTPATH), hdbTerms.HDB_PID_FILE));
            harper_logger_ts_1.default.debug('Starting new process...');
            if (process.env.HARPER_EXIT_ON_RESTART) {
                // use this to exit the process so that it will be restarted by the
                // PM/container/orchestrator.
                harper_logger_ts_1.default.warn('Exiting Harper process to trigger a container restart');
                process.exit(0);
            }
            // now launch the new process and exit this process
            require('./run').launch(true);
        }, 50); // can't await this because it is going to do an exit(), but wait for 50ms so we give the HTTP thread a
        // chance to return a response
    }
    else {
        // Post msg to main parent thread requesting it restart (so the main thread can process.exit())
        worker_threads_1.parentPort.postMessage({
            type: hdbTerms.ITC_EVENT_TYPES.RESTART,
        });
    }
    return RESTART_RESPONSE;
}
/**
 * Used to restart a particular service, services includes - httpWorkers
 * @param req
 * @returns {Promise<string>}
 */
async function restartService(req) {
    let { service } = req;
    if (hdbTerms.HDB_PROCESS_SERVICES[service] === undefined) {
        throw (0, hdbError_ts_1.handleHDBError)(new Error(), INVALID_SERVICE_ERR, HTTP_STATUS_CODES.BAD_REQUEST, undefined, undefined, true);
    }
    processMan.expectedRestartOfChildren();
    if (!worker_threads_1.isMainThread) {
        if (req.replicated) {
            global.server.replication.monitorNodeCAs(); // get all the CAs from the nodes we know about
        }
        worker_threads_1.parentPort.postMessage({
            type: hdbTerms.ITC_EVENT_TYPES.RESTART,
            workerType: service,
        });
        worker_threads_1.parentPort.ref(); // don't let the parent thread exit until we're done
        await new Promise((resolve) => {
            worker_threads_1.parentPort.on('message', (msg) => {
                if (msg.type === 'restart-complete') {
                    resolve();
                    worker_threads_1.parentPort.unref();
                }
            });
        });
        let replicatedResponses;
        if (req.replicated) {
            req.replicated = false; // don't send a replicated flag to the nodes we are sending to
            replicatedResponses = [];
            for (let node of global.server.nodes) {
                if (node.name === (0, nodeName_ts_1.getThisNodeName)())
                    continue;
                // for now, only one at a time
                let job_id;
                try {
                    ({ job_id } = await global.server.replication.sendOperationToNode(node, req));
                }
                catch (err) {
                    // If request to node fails, add the error to the response and continue to the next node
                    replicatedResponses.push({ node: node.name, message: err.message });
                    continue;
                }
                // wait for the job to finish by polling for the completion of the job
                replicatedResponses.push(await new Promise((resolve, reject) => {
                    const RETRY_INTERVAL = 250;
                    let retriesLeft = 2400; // 10 minutes
                    let interval = setInterval(async () => {
                        if (retriesLeft-- <= 0) {
                            clearInterval(interval);
                            let error = new Error('Timed out waiting for restart job to complete');
                            error.replicated = replicatedResponses; // report the finished restarts
                            reject(error);
                        }
                        let response = await global.server.replication.sendOperationToNode(node, {
                            operation: 'get_job',
                            id: job_id,
                        });
                        const jobResult = response.results[0];
                        if (jobResult.status === 'COMPLETE') {
                            clearInterval(interval);
                            resolve({ node: node.name, message: jobResult.message });
                        }
                        if (jobResult.status === 'ERROR') {
                            clearInterval(interval);
                            let error = new Error(jobResult.message);
                            error.replicated = replicatedResponses; // report the finished restarts
                            reject(error);
                        }
                    }, RETRY_INTERVAL);
                }));
            }
            return { replicated: replicatedResponses };
        }
        return;
    }
    let errMsg;
    switch (service) {
        case 'custom_functions':
        case 'custom functions':
        case hdbTerms.HDB_PROCESS_SERVICES.harperdb:
        case hdbTerms.HDB_PROCESS_SERVICES.http_workers:
        case hdbTerms.HDB_PROCESS_SERVICES.http:
            if (calledFromCli)
                console.log(`Restarting httpWorkers`);
            harper_logger_ts_1.default.notify('Restarting http_workers');
            if (calledFromCli) {
                await processMan.restart(hdbTerms.PROCESS_DESCRIPTORS.HDB);
            }
            else {
                await (0, manageThreads_js_1.restartWorkers)('http');
            }
            break;
        default:
            errMsg = `Unrecognized service: ${service}`;
            break;
    }
    if (errMsg) {
        harper_logger_ts_1.default.error(errMsg);
        if (calledFromCli)
            console.error(errMsg);
        return errMsg;
    }
    if (service === 'custom_functions')
        service = 'Custom Functions';
    return `Restarting ${service}`;
}
//# sourceMappingURL=restart.js.map