import { must } from "../../../shared/src/must.js";
import { childWorker, parentWorker, singleProcessMode } from "../types/processes.js";
import { ProcessManager, exitAfter, runUntilKilled } from "../services/life-cycle.js";
import { getNormalizedZeroConfig } from "../config/zero-config.js";
import { startOtelAuto } from "./otel-start.js";
import { createLogContext } from "./logging.js";
import { CHANGE_STREAMER_URL, REAPER_URL, REPLICATOR_URL, SYNCER_URL } from "./worker-urls.js";
import { initEventSink } from "../observability/events.js";
import { restoreReplica, startReplicaBackupProcess } from "../services/litestream/commands.js";
import { createNotifierFrom, handleSubscriptionsFrom, subscribeTo } from "../workers/replicator.js";
import { WorkerDispatcher } from "./worker-dispatcher.js";
import { resolver } from "@rocicorp/resolver";
import path from "node:path";
//#region ../zero-cache/src/server/main.ts
async function runWorker(parent, env) {
	const startMs = Date.now();
	const config = getNormalizedZeroConfig({ env });
	startOtelAuto(createLogContext(config, { worker: "dispatcher" }, false));
	const lc = createLogContext(config, { worker: "dispatcher" }, true);
	initEventSink(lc, config);
	const processes = new ProcessManager(lc, parent);
	const { numSyncWorkers: numSyncers } = config;
	if (config.enableCrudMutations && config.upstream.maxConns < numSyncers) throw new Error(`Insufficient upstream connections (${config.upstream.maxConns}) for ${numSyncers} syncers.Increase ZERO_UPSTREAM_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`);
	if (config.cvr.maxConns < numSyncers) throw new Error(`Insufficient cvr connections (${config.cvr.maxConns}) for ${numSyncers} syncers.Increase ZERO_CVR_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`);
	const internalFlags = numSyncers === 0 ? [] : [
		"--upstream-max-conns-per-worker",
		String(Math.floor(config.upstream.maxConns / numSyncers)),
		"--cvr-max-conns-per-worker",
		String(Math.floor(config.cvr.maxConns / numSyncers))
	];
	function loadWorker(moduleUrl, type, id, ...args) {
		const worker = childWorker(moduleUrl, env, ...args, ...internalFlags);
		const name = path.basename(moduleUrl.pathname) + (id ? ` (${id})` : "");
		return processes.addWorker(worker, type, name);
	}
	const { taskID, changeStreamer: { mode: changeStreamerMode, uri: changeStreamerURI }, litestream } = config;
	const runChangeStreamer = changeStreamerMode === "dedicated" && changeStreamerURI === void 0;
	let restoreStart = /* @__PURE__ */ new Date();
	if (litestream.backupURL || litestream.executable && !runChangeStreamer) try {
		restoreStart = await restoreReplica(lc, config);
	} catch (e) {
		if (runChangeStreamer) lc.error?.("error restoring backup. resyncing the replica.", e);
		else throw e;
	}
	const { promise: changeStreamerReady, resolve: changeStreamerStarted } = resolver();
	const changeStreamer = runChangeStreamer ? loadWorker(CHANGE_STREAMER_URL, "supporting", void 0, String(restoreStart.getTime())).once("message", changeStreamerStarted) : changeStreamerStarted() ?? void 0;
	const { promise: reaperReady, resolve: reaperStarted } = resolver();
	if (numSyncers > 0) loadWorker(REAPER_URL, "supporting").once("message", reaperStarted);
	else reaperStarted();
	await changeStreamerReady;
	if (runChangeStreamer && litestream.backupURL) {
		const { promise: backupReady, resolve } = resolver();
		const mode = "backup";
		loadWorker(REPLICATOR_URL, "supporting", mode, mode).once("message", () => {
			processes.addSubprocess(startReplicaBackupProcess(lc, config), "supporting", "litestream");
			resolve();
		});
		await backupReady;
	}
	await reaperReady;
	const syncers = [];
	if (numSyncers) {
		const mode = runChangeStreamer && litestream.backupURL ? "serving-copy" : "serving";
		const { promise: replicaReady, resolve } = resolver();
		const replicator = loadWorker(REPLICATOR_URL, "supporting", mode, mode).once("message", () => {
			subscribeTo(lc, replicator);
			resolve();
		});
		await replicaReady;
		const notifier = createNotifierFrom(lc, replicator);
		for (let i = 0; i < numSyncers; i++) syncers.push(loadWorker(SYNCER_URL, "user-facing", i + 1, mode));
		syncers.forEach((syncer) => handleSubscriptionsFrom(lc, syncer, notifier));
	}
	let mutator;
	lc.info?.("waiting for workers to be ready ...");
	const logWaiting = setInterval(() => lc.info?.(`still waiting for ${processes.initializing().join(", ")}`), 1e4);
	await processes.allWorkersReady();
	clearInterval(logWaiting);
	lc.info?.(`all workers ready (${Date.now() - startMs} ms)`);
	parent.send(["ready", { ready: true }]);
	try {
		await runUntilKilled(lc, parent, new WorkerDispatcher(lc, taskID, parent, syncers, mutator, changeStreamer));
	} catch (err) {
		processes.logErrorAndExit(err, "dispatcher");
	}
	await processes.done();
}
if (!singleProcessMode()) exitAfter(() => runWorker(must(parentWorker), process.env));
//#endregion
export { runWorker as default };

//# sourceMappingURL=main.js.map