import { StatementRunner } from "../../db/statements.js";
import { getOrCreateCounter } from "../../observability/metrics.js";
import { PROTOCOL_VERSION } from "../change-streamer/change-streamer.js";
import { RunningState } from "../running-state.js";
import { ChangeProcessor } from "./change-processor.js";
import { Notifier } from "./notifier.js";
import { ReplicationStatusPublisher } from "./replication-status.js";
import { getSubscriptionState } from "./schema/replication-state.js";
class IncrementalSyncer {
  #taskID;
  #id;
  #changeStreamer;
  #replica;
  #mode;
  #publishReplicationStatus;
  #notifier;
  #state = new RunningState("IncrementalSyncer");
  #replicationEvents = getOrCreateCounter(
    "replication",
    "events",
    "Number of replication events processed"
  );
  constructor(taskID, id, changeStreamer, replica, mode, publishReplicationStatus) {
    this.#taskID = taskID;
    this.#id = id;
    this.#changeStreamer = changeStreamer;
    this.#replica = new StatementRunner(replica);
    this.#mode = mode;
    this.#publishReplicationStatus = publishReplicationStatus;
    this.#notifier = new Notifier();
  }
  async run(lc) {
    lc.info?.(`Starting IncrementalSyncer`);
    const { watermark: initialWatermark } = getSubscriptionState(this.#replica);
    void this.#notifier.notifySubscribers();
    const statusPublisher = this.#publishReplicationStatus ? new ReplicationStatusPublisher(this.#replica.db) : void 0;
    while (this.#state.shouldRun()) {
      const { replicaVersion, watermark } = getSubscriptionState(this.#replica);
      const processor = new ChangeProcessor(
        this.#replica,
        this.#mode,
        (lc2, err2) => this.stop(lc2, err2)
      );
      let downstream;
      let unregister = () => {
      };
      let err;
      try {
        downstream = await this.#changeStreamer.subscribe({
          protocolVersion: PROTOCOL_VERSION,
          taskID: this.#taskID,
          id: this.#id,
          mode: this.#mode,
          watermark,
          replicaVersion,
          initial: watermark === initialWatermark
        });
        this.#state.resetBackoff();
        unregister = this.#state.cancelOnStop(downstream);
        statusPublisher?.publish(
          lc,
          "Replicating",
          `Replicating from ${watermark}`
        );
        for await (const message of downstream) {
          this.#replicationEvents.add(1);
          switch (message[0]) {
            case "status":
              lc.debug?.(`Received initial status`, message[1]);
              break;
            case "error":
              this.stop(lc, message[1]);
              break;
            default: {
              const result = processor.processMessage(lc, message);
              if (result?.schemaUpdated) {
                statusPublisher?.publish(lc, "Replicating", "Schema updated");
              }
              if (result?.watermark && result?.changeLogUpdated) {
                void this.#notifier.notifySubscribers({ state: "version-ready" });
              }
              break;
            }
          }
        }
        processor.abort(lc);
      } catch (e) {
        err = e;
        processor.abort(lc);
      } finally {
        downstream?.cancel();
        unregister();
      }
      await this.#state.backoff(lc, err);
    }
    lc.info?.("IncrementalSyncer stopped");
  }
  subscribe() {
    return this.#notifier.subscribe();
  }
  stop(lc, err) {
    this.#state.stop(lc, err);
  }
}
export {
  IncrementalSyncer
};
//# sourceMappingURL=incremental-sync.js.map
