import { assert } from "../../../shared/src/asserts.js";
import { must } from "../../../shared/src/must.js";
import { DatabaseInitError } from "../../../zqlite/src/db.js";
import { getServerContext } from "../config/server-context.js";
import { getNormalizedZeroConfig } from "../config/zero-config.js";
import { deleteLiteDB } from "../db/delete-lite-db.js";
import { warmupConnections } from "../db/warmup.js";
import { initEventSink, publishCriticalEvent } from "../observability/events.js";
import { initializeCustomChangeSource } from "../services/change-source/custom/change-source.js";
import { initializePostgresChangeSource } from "../services/change-source/pg/change-source.js";
import { BackupMonitor } from "../services/change-streamer/backup-monitor.js";
import { ChangeStreamerHttpServer } from "../services/change-streamer/change-streamer-http.js";
import { initializeStreamer } from "../services/change-streamer/change-streamer-service.js";
import { ReplicaMonitor } from "../services/change-streamer/replica-monitor.js";
import { AutoResetSignal } from "../services/change-streamer/schema/tables.js";
import { exitAfter, runUntilKilled } from "../services/life-cycle.js";
import { replicationStatusError } from "../services/replicator/replication-status.js";
import { pgClient } from "../types/pg.js";
import { singleProcessMode, parentWorker } from "../types/processes.js";
import { getShardConfig } from "../types/shards.js";
import { createLogContext } from "./logging.js";
import { startOtelAuto } from "./otel-start.js";
async function runWorker(parent, env, ...args) {
  assert(args.length > 0, `parent startMs not specified`);
  const parentStartMs = parseInt(args[0]);
  const config = getNormalizedZeroConfig({ env, argv: args.slice(1) });
  const {
    taskID,
    changeStreamer: {
      port,
      address,
      protocol,
      startupDelayMs,
      backPressureLimitHeapProportion
    },
    upstream,
    change,
    replica,
    initialSync,
    litestream
  } = config;
  startOtelAuto(createLogContext(config, { worker: "change-streamer" }, false));
  const lc = createLogContext(config, { worker: "change-streamer" }, true);
  initEventSink(lc, config);
  const changeDB = pgClient(
    lc,
    change.db,
    {
      max: change.maxConns,
      connection: { ["application_name"]: "zero-change-streamer" }
    },
    { sendStringAsJson: true }
  );
  void warmupConnections(lc, changeDB, "change");
  const { autoReset } = config;
  const shard = getShardConfig(config);
  let changeStreamer;
  const context = getServerContext(config);
  for (const first of [true, false]) {
    try {
      const { changeSource, subscriptionState } = upstream.type === "pg" ? await initializePostgresChangeSource(
        lc,
        upstream.db,
        shard,
        replica.file,
        initialSync,
        context
      ) : await initializeCustomChangeSource(
        lc,
        upstream.db,
        shard,
        replica.file,
        context
      );
      changeStreamer = await initializeStreamer(
        lc,
        shard,
        taskID,
        address,
        protocol,
        changeDB,
        changeSource,
        subscriptionState,
        autoReset ?? false,
        backPressureLimitHeapProportion,
        setTimeout
      );
      break;
    } catch (e) {
      if (first && e instanceof AutoResetSignal) {
        lc.warn?.(`resetting replica ${replica.file}`, e);
        deleteLiteDB(replica.file);
        continue;
      }
      await publishCriticalEvent(
        lc,
        replicationStatusError(lc, "Initializing", e)
      );
      if (e instanceof DatabaseInitError) {
        throw new Error(
          `Cannot open ZERO_REPLICA_FILE at "${replica.file}". Please check that the path is valid.`,
          { cause: e }
        );
      }
      throw e;
    }
  }
  assert(changeStreamer, `resetting replica did not advance replicaVersion`);
  const { backupURL, port: metricsPort } = litestream;
  const monitor = backupURL ? new BackupMonitor(
    lc,
    backupURL,
    `http://localhost:${metricsPort}/metrics`,
    changeStreamer,
    // The time between when the zero-cache was started to when the
    // change-streamer is ready to start serves as the initial delay for
    // watermark cleanup (as it either includes a similar replica
    // restoration/preparation step, or an initial-sync, which
    // generally takes longer).
    //
    // Consider: Also account for permanent volumes?
    Date.now() - parentStartMs
  ) : new ReplicaMonitor(lc, replica.file, changeStreamer);
  const changeStreamerWebServer = new ChangeStreamerHttpServer(
    lc,
    config,
    { port, startupDelayMs },
    parent,
    changeStreamer,
    monitor instanceof BackupMonitor ? monitor : null
  );
  parent.send(["ready", { ready: true }]);
  return runUntilKilled(lc, parent, changeStreamerWebServer, monitor);
}
if (!singleProcessMode()) {
  void exitAfter(
    () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2))
  );
}
export {
  runWorker as default
};
//# sourceMappingURL=change-streamer.js.map
