UNPKG

@clickup/pg-microsharding

Version:
129 lines 6.41 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.waitUntilEverythingIsNotLaggingMuch = waitUntilEverythingIsNotLaggingMuch; const readline_1 = __importDefault(require("readline")); const getPhysicalReplicas_1 = require("./getPhysicalReplicas"); const getSlotInfo_1 = require("./getSlotInfo"); const logging_1 = require("./logging"); const names_1 = require("./names"); const pollDelay_1 = require("./pollDelay"); const secToHuman_1 = require("./secToHuman"); const tookToHuman_1 = require("./tookToHuman"); const FORCE_CONTINUE_KEY = { sequence: "S", prompt: "press Shift+S to force-continue: not recommended!", }; /** * Waits until all physical replicas on a DSN are not lagging for too much, so * we can e.g. call waitUntilIncrementalCompletes() and then resultCommit(). * Also, if fromDsn is passed, waits for the logical slot to lag not more than * maxReplicationLagSec time. * * For the logical slot replication lag estimation, in the beginning, we * "latch", i.e. remember the master's LSN, and then wait until the slot replay * is past that LSN. Once it happens, the amount of time passed is roughly the * logical replication slot lag in seconds. This is the only way to estimate, * how many seconds behind is a logical slot. * * For physical replicas, we query their replication lag in seconds directly. * * If waitForUserToContinue=true, then the function exits ONLY when the user * told it to do so explicitly by pressing the hotkey. */ async function waitUntilEverythingIsNotLaggingMuch({ when, fromDsn, tables, toDsn, schema, maxReplicationLagSec: maxLagSec, waitForUserToContinue, }, throwIfAborted) { var _a, _b, _c, _d; if (tables.length === 0) { return; } const waitStartedAt = performance.now(); const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout, }); try { let physicalReplicas = undefined; let slotInfo = undefined; let stats = []; let delayPromise = null; let userForcedContinue = false; readline_1.default.emitKeypressEvents(process.stdin, rl); rl.on("SIGINT", () => { throwIfAborted = () => { throw "Aborted due to SIGINT"; }; }); (_b = (_a = process.stdin).setRawMode) === null || _b === void 0 ? void 0 : _b.call(_a, true); process.stdin.on("keypress", (_str, key) => { if (key.sequence === FORCE_CONTINUE_KEY.sequence) { userForcedContinue = true; delayPromise === null || delayPromise === void 0 ? void 0 : delayPromise.clear(); } }); for (let i = 0, needWait = true; needWait && !userForcedContinue; i++) { throwIfAborted === null || throwIfAborted === void 0 ? void 0 : throwIfAborted(); physicalReplicas = await (0, getPhysicalReplicas_1.getPhysicalReplicas)({ dsn: toDsn, comment: i === 0 ? `Waiting till ${fromDsn ? "the slot and " : ""}replicas are not lagging much (${when})` : undefined, }); slotInfo = fromDsn ? await (0, getSlotInfo_1.getSlotInfo)({ dsn: fromDsn, slotName: (0, names_1.subName)(schema) }, slotInfo) : undefined; needWait = waitForUserToContinue; stats = []; if (slotInfo) { const { srcLsn, dstLsn, gap, gapTillLatch, lagSec } = slotInfo; const prefix = "Logical slot"; const info = `src=${srcLsn} dst=${dstLsn} gap=${gap} gapTillLatch=${gapTillLatch}`; if (lagSec === null) { needWait = true; stats.push(`? ${prefix}: lag is unknown yet (likely LARGE; be patient, estimating); ${info}`); } else if (lagSec > maxLagSec) { needWait = true; stats.push(`- ${prefix}: lag is ${(0, secToHuman_1.secToHuman)(lagSec)} > ${(0, secToHuman_1.secToHuman)(maxLagSec)}; ${info}`); } else { stats.push(`+ ${prefix}: lag is ${(0, secToHuman_1.secToHuman)(lagSec)} <= ${(0, secToHuman_1.secToHuman)(maxLagSec)}; ${info}`); } } for (const { clientAddr, userName, applicationName, lagSec, feedbackAgoSec, masterLsn, replicaLsn, gap, } of physicalReplicas) { const prefix = `Physical replica of the dst ${userName}@${clientAddr} (${applicationName})`; const info = `master=${masterLsn} replica=${replicaLsn} gap=${gap} feedbackAgoSec=${feedbackAgoSec}`; if (lagSec === null) { needWait = true; stats.push(`? ${prefix}: lag is unknown yet; ${info}`); } else if (lagSec > maxLagSec) { needWait = true; stats.push(`- ${prefix}: lag is ${(0, secToHuman_1.secToHuman)(lagSec)} > ${(0, secToHuman_1.secToHuman)(maxLagSec)}; ${info}`); } else { stats.push(`+ ${prefix}: lag is ${(0, secToHuman_1.secToHuman)(lagSec)} <= ${(0, secToHuman_1.secToHuman)(maxLagSec)}; ${info}`); } } (0, logging_1.progress)(waitForUserToContinue ? `Waiting for the user action: ${FORCE_CONTINUE_KEY.prompt}` : `Waiting for the lag to stabilize (or ${FORCE_CONTINUE_KEY.prompt}):`, ...stats); await (delayPromise = (0, pollDelay_1.pollDelay)()); } logging_1.progress.clear(); logging_1.log.shellCmd({ comment: userForcedContinue ? `Force-continued in ${(0, tookToHuman_1.tookToHuman)(waitStartedAt)}` : `Everything is caught up in ${(0, tookToHuman_1.tookToHuman)(waitStartedAt)}`, cmd: "", input: stats.join("\n"), }); } finally { rl.close(); (_d = (_c = process.stdin).setRawMode) === null || _d === void 0 ? void 0 : _d.call(_c, false); process.stdin.pause(); } } //# sourceMappingURL=waitUntilEverythingIsNotLaggingMuch.js.map