UNPKG

@clickup/pg-microsharding

Version:
90 lines 4.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.waitUntilIncrementalCompletes = waitUntilIncrementalCompletes; const createConnectedClient_1 = require("./createConnectedClient"); const inspectError_1 = require("./inspectError"); const logging_1 = require("./logging"); const names_1 = require("./names"); const parseLsn_1 = require("./parseLsn"); const pollDelay_1 = require("./pollDelay"); const quote_1 = require("./quote"); const tookToHuman_1 = require("./tookToHuman"); /** * Locks the tables for write and waits until incremental flow to the * destination tables exhausts. * * If succeeded, the function returns a callback which, when called, unlocks the * tables on the source. This callback must be called after deactivating the * shard on the source. (But even if it's not called, it'll likely be okay * still, because the process will end, PG will disconnect, and the tables will * be auto-unlocked.) */ async function waitUntilIncrementalCompletes({ fromDsn, schema, tables, }, throwIfAborted) { if (tables.length === 0) { return async () => { }; } const waitStartedAt = performance.now(); const fromClient = await (0, createConnectedClient_1.createConnectedClient)(fromDsn); try { const fromServerVersion = await fromClient.serverVersion(); await fromClient.query("BEGIN"); await fromClient.query("SET LOCAL statement_timeout TO 0"); await fromClient.query("SET LOCAL idle_in_transaction_session_timeout TO 0"); fromServerVersion[0] >= 17 && (await fromClient.query("SET LOCAL transaction_timeout TO 0")); await fromClient.query(`SET LOCAL search_path TO ${schema}`); throwIfAborted(); const query = (0, quote_1.sql) `LOCK TABLE ONLY ${(0, quote_1.join)(tables.map(({ name }) => (0, quote_1.sql) `${(0, quote_1.ident)(name)}`), ", ")} IN EXCLUSIVE MODE`.toString(); logging_1.log.shellCmd({ comment: "Locking source tables to pause all WRITES", cmd: `node-postgres ${fromDsn}#${schema}`, input: query, }); await fromClient.query(query); throwIfAborted(); const srcLsn = await fromClient.currentWalInsertLsn(); while (true) { const dstLsn = await fromClient.confirmedFlushLsn((0, names_1.subName)(schema)); // srcLsn is constant and in the future; dstLsn is in the past, grows // monotonically and catches up srcLsn eventually. Once srcLsn-dstLsn // crosses 0, we continue. const gap = (0, parseLsn_1.subtractLsn)(srcLsn, dstLsn); (0, logging_1.progress)(`Waiting for dst=${dstLsn} to be greater src=${srcLsn}, gap=${gap}`); if (gap <= 0) { break; } await (0, pollDelay_1.pollDelay)(); throwIfAborted(); } } catch (e) { try { await fromClient.query("ROLLBACK"); await fromClient.end(); } catch (e) { logging_1.log.error(`Failed to rollback transaction: ${(0, inspectError_1.inspectError)(e)}`); } throw e; } logging_1.progress.clear(); logging_1.log.shellCmd({ comment: "Incremental logical replication completed", cmd: "", input: `Tables were write-locked for ${(0, tookToHuman_1.tookToHuman)(waitStartedAt)}`, }); return async (status) => { // It doesn't really matter for the locks, whether we commit or rollback // here. But in case of a success the "ROLLBACK" message in the logs scare // people. So we use "COMMIT" in case of success and "ROLLBACK" otherwise. const query = status === "error" ? "ROLLBACK" : "COMMIT"; logging_1.log.shellCmd({ comment: `Unlocking source tables; write lock was held for ${(0, tookToHuman_1.tookToHuman)(waitStartedAt)}`, cmd: `node-postgres ${fromDsn}`, input: query, }); await fromClient.query(query); await fromClient.end(); }; } //# sourceMappingURL=waitUntilIncrementalCompletes.js.map