@clickup/ent-framework
Version:
A PostgreSQL graph-database-alike library with microsharding and row-level security
138 lines • 6.02 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ToolPing = void 0;
const stream_1 = require("stream");
const chalk_1 = __importDefault(require("chalk"));
const delay_1 = __importDefault(require("delay"));
const compact_1 = __importDefault(require("lodash/compact"));
const defaults_1 = __importDefault(require("lodash/defaults"));
const first_1 = __importDefault(require("lodash/first"));
const misc_1 = require("../abstract/internal/misc");
const QueryPing_1 = require("../abstract/QueryPing");
const Timeline_1 = require("../abstract/Timeline");
const misc_2 = require("../internal/misc");
const formatTimeWithMs_1 = require("./internal/formatTimeWithMs");
const highlightIf_1 = require("./internal/highlightIf");
/**
* A tool which plays the role of Linux `ping` command, but for master() or
* replica() Client of a Shard. Allows to verify that there is no downtime
* happening when a PG node goes down or experiences a failover/switchover.
*/
class ToolPing {
/**
* Initializes the instance.
*/
constructor(options) {
this.options = (0, defaults_1.default)({}, options, ToolPing.DEFAULT_OPTIONS);
}
/**
* Runs an endless loop that pings a master() or replica() Client of the
* passed Island. Yields the colored output line by line.
*/
async *[Symbol.asyncIterator]() {
const cluster = this.options.cluster;
const stream = new stream_1.Readable({ objectMode: true, read() { } });
const oldLoggers = { ...cluster.options.loggers };
cluster.options.loggers.clientQueryLogger = ({ annotations, op, elapsed, error, backend, role, connStats, }) => {
const annotation = (0, first_1.default)(annotations);
if (op === misc_1.OP_PING || op === misc_1.OP_SHARD_NOS) {
stream.push(chalk_1.default[error
? op === misc_1.OP_PING
? "yellowBright"
: "yellow"
: op === misc_1.OP_PING
? "whiteBright"
: "white"]((0, compact_1.default)([
`[${(0, formatTimeWithMs_1.formatTimeWithMs)(new Date(), true)}]`,
"clientQueryLogger",
op,
`backend=${backend}`,
(0, highlightIf_1.highlightIf)(`elapsed=${Math.round(elapsed.total).toString().padEnd(2)}`, "bgBlue", () => elapsed.total > 3000),
`role=${role}`,
`conn.id=${connStats.id}`,
`conn.queriesSent=${connStats.queriesSent}`,
...(annotation
? [
(0, highlightIf_1.highlightIf)(`attempt=${annotation.attempt}`, "bgGrey", () => annotation.attempt > 0),
`whyClient=${annotation.whyClient}`,
`|| ${error ?? "ok"}`,
]
: [`|| ${error?.replace(/\s*\[\S+\]$/s, "") ?? "ok"}`]),
]).join(" ")));
}
};
cluster.options.loggers.swallowedErrorLogger = (props) => {
if (props.importance === "low") {
return;
}
stream.push(chalk_1.default.gray([
`[${(0, formatTimeWithMs_1.formatTimeWithMs)(new Date(), true)}]`,
"swallowedErrorLogger",
(0, highlightIf_1.highlightIf)(`elapsed=${Math.round(props.elapsed ?? 0)
.toString()
.padEnd(2)}`, "bgBlue", () => (props.elapsed ?? 0) > 3000),
`where="${props.where}"`,
"||",
props.error?.message?.replace(/\n.*/s, ""),
].join(" ")));
};
let stop = false;
const promise = (async () => {
let shard = undefined;
try {
const shards = [
cluster.globalShard(),
...(await cluster.nonGlobalShards()),
];
shard = (0, misc_2.nullthrows)(shards.find((s) => s.no === this.options.shard), "No such Shard");
}
catch (e) {
stream.push((0, misc_2.indent)(chalk_1.default.redBright(`Shard selection threw ${e}`)));
return;
}
while (!stop) {
try {
const startTime = performance.now();
await shard.run(new QueryPing_1.QueryPing({
execTimeMs: this.options.pingExecTimeMs,
isWrite: this.options.pingIsWrite,
}), {
trace: "ping-trace",
vc: "ping-vc",
debugStack: "",
whyClient: undefined,
attempt: 0,
}, new Timeline_1.Timeline(), null);
const duration = Math.round(performance.now() - startTime);
stream.push((0, misc_2.indent)(chalk_1.default.green(`ping() succeeded in ${duration} ms`)));
}
catch (e) {
stream.push((0, misc_2.indent)(chalk_1.default.redBright(`ping() threw ${e}`)));
}
await (0, delay_1.default)(this.options.pingPollMs);
}
})();
try {
for await (const item of stream) {
yield item;
}
}
finally {
stop = true;
await promise;
Object.assign(cluster.options.loggers, oldLoggers);
}
}
}
exports.ToolPing = ToolPing;
/** Default values for the constructor options. */
ToolPing.DEFAULT_OPTIONS = {
shard: 0,
pingExecTimeMs: 0,
pingPollMs: 500,
pingIsWrite: false,
};
//# sourceMappingURL=ToolPing.js.map
;