mongo-oplog2
Version:
Simple monitoring of MongoDB oplog.
217 lines • 15.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Events = exports.MongoOplogStatus = exports.OplogEvents = exports.createInstance = exports.MongoOplog = exports.FilteredMongoOplog = exports.prettify = exports.getTimestamp = exports.getOpName = void 0;
const util_1 = require("util");
const mongodb_1 = require("mongodb");
const eventemitter_1 = require("./eventemitter");
const filter_1 = require("./filter");
const stream_1 = require("./stream");
const util_2 = require("./util");
var util_3 = require("./util");
Object.defineProperty(exports, "getOpName", { enumerable: true, get: function () { return util_3.getOpName; } });
Object.defineProperty(exports, "getTimestamp", { enumerable: true, get: function () { return util_3.getTimestamp; } });
Object.defineProperty(exports, "prettify", { enumerable: true, get: function () { return util_3.prettify; } });
var filter_2 = require("./filter");
Object.defineProperty(exports, "FilteredMongoOplog", { enumerable: true, get: function () { return filter_2.FilteredMongoOplog; } });
const debug = util_1.debuglog("mongo-oplog2");
const reErr = /cursor (killed or )?timed out/;
/**
* Allows tailing the MongoDB oplog.
*/
class MongoOplogImpl extends eventemitter_1.default {
/**
* @param uriOrDb a connection string or existing database connection
* @param opts options for the `MongoOplog` instance. Any options not
* beyond thosed used by `MongoOplog` will be stored and
* passed along to the `mongodb` driver when creating a
* database connection.
*/
constructor(uriOrDb, opts = {}) {
super();
this.ignore = false;
this.pretty = false;
this.uri = "";
if (!uriOrDb || typeof uriOrDb === "string") {
this.uri = uriOrDb || "mongodb://127.0.0.1/local";
const dbOpts = opts.mongo || {};
if (!('useUnifiedTopology' in dbOpts)) {
dbOpts.useUnifiedTopology = true;
}
this.dbOpts = dbOpts;
}
this.tailing = false;
this.pretty = !!opts.pretty;
this.ns = opts.ns || "";
this.collectionName = opts.coll || "";
this._ts = util_2.getTimestamp(opts.since || 0);
this._oplogFilter = opts.filter || void 0;
}
/**
* Returns `true` if database is connected; false otherwise.
*/
get connected() { return !!this._db; }
/**
* The database connection.
*/
get db() { return this._db; }
/**
* The underlying MongoDB cursor stream.
*/
get stream() { return this._stream; }
/**
* Last processed timestamp.
*/
get ts() { return this._ts; }
/**
* Returns an event emitter that will emit database events for the specified
* name space.
* @param ns namespace for the filter.
*/
filter(ns = "") { return new filter_1.FilteredMongoOplog(this, ns); }
/**
* Stop tailing the oplog, disconnect from the database, and emit the
* "destroy" event.
*/
async destroy() {
await this.stop();
await this.disconnect();
this.removeAllListeners();
this.emit("destroy");
return this;
}
/**
* If a timestamp is not provided then returns `true` if either no
* document was found or the `ts` value of the document matches the
* internally tracked ts; `false` otherwise.
* If timestamp is provided returs `true` only if the document returned
* matches the specified timestamp. If `null` is returned an Error is
* raised. If a document is returned but the `ts` property does not match
* the specified `ts` returns `false`.
* @param ts optional timestamp to check, if not supplied use internal
*/
async isCurrent(ts) {
const db = await this.connect();
const doc = await stream_1.getLastDoc(db, this.ns, this.collectionName);
if (!doc) {
if (ts) {
throw new Error("ERR_NO_DOC");
}
return true;
}
if (doc.ts.equals(ts || this._ts)) {
return true;
}
return false;
}
/**
* Stop tailing the oplog and destroy the underlying cursor. Tailing
* can be resumed by calling the `tail` function.
*/
async stop() {
if (this._stream) {
this._stream.destroy();
delete this._stream;
}
this.tailing = false;
debug("streaming stopped");
return this;
}
/**
* Start tailing the oplog.
*/
async tail() {
const onError = async (err) => {
await this.stop();
if (reErr.test(err.message)) {
debug("cursor timedout - retailing: %j", err);
await this.stop();
await this.disconnect();
return this.tail();
}
else {
debug("oplog error: %j", err);
this.emit("error", err);
}
};
try {
if (this.tailing || this._stream) {
return this._stream;
}
this.tailing = true;
if (!this._db) {
await this.connect();
}
this._stream = await stream_1.getStream({
db: this._db,
ns: this.ns,
ts: this.ts,
coll: this.collectionName,
filter: this._oplogFilter,
});
debug("stream started");
this._stream.on("end", () => {
debug("stream ended");
this.emit("end");
this.emit("tail-end");
});
this._stream.on("error", onError);
this._stream.on("data", (doc) => {
if (this.ignore) {
return;
}
this._ts = doc.ts;
const opName = util_2.getOpName(doc.op);
const outDoc = this.pretty ? util_2.prettify(doc) : doc;
debug("incoming data: %j", doc);
debug("outgoing data: %j", outDoc);
this.emit("op", outDoc);
this.emit(opName, outDoc);
});
this.emit("tail-start");
return this._stream;
}
catch (err) {
return onError(err);
}
}
/**
* Connect to the database.
*/
async connect() {
if (this._db) {
return this._db;
}
this._client = await mongodb_1.MongoClient.connect(this.uri, this.dbOpts);
debug("Connected to oplog database.");
this.emit("connect");
this._db = this._client.db('local');
return this._db;
}
/**
* Disconnect from the database by calling `close`.
* If database connection is externally supplied do NOT call `close`.
*/
async disconnect() {
if (!this._db) {
debug("refusing to disconnect unconnected db.");
return;
}
if (this._client) {
await this._client.close(true);
}
this._db = void 0;
this.emit("disconnect");
}
}
exports.MongoOplog = MongoOplogImpl;
function createInstance(uriOrDb, opts = {}) {
return new exports.MongoOplog(uriOrDb, opts);
}
exports.createInstance = createInstance;
exports.OplogEvents = Object.freeze(["delete", "insert", "op", "update", "noop"]);
exports.MongoOplogStatus = Object.freeze([
"connect", "disconnect", "destroy", "end", "error", "tail-start", "tail-end"
]);
exports.Events = Object.freeze([...exports.OplogEvents, ...exports.MongoOplogStatus]);
exports.default = createInstance;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsK0JBQWdDO0FBQ2hDLHFDQUFnRjtBQUVoRixpREFBMEM7QUFDMUMscUNBQThDO0FBQzlDLHFDQUFpRDtBQUNqRCxpQ0FBdUc7QUFDdkcsK0JBQXFGO0FBQTVFLGlHQUFBLFNBQVMsT0FBQTtBQUFFLG9HQUFBLFlBQVksT0FBQTtBQUE0QixnR0FBQSxRQUFRLE9BQUE7QUFDcEUsbUNBQThDO0FBQXJDLDRHQUFBLGtCQUFrQixPQUFBO0FBRTNCLE1BQU0sS0FBSyxHQUFHLGVBQVEsQ0FBQyxjQUFjLENBQUMsQ0FBQztBQUN2QyxNQUFNLEtBQUssR0FBRywrQkFBK0IsQ0FBQztBQW1COUM7O0dBRUc7QUFDSCxNQUFNLGNBQ1UsU0FBUSxzQkFBOEI7SUFlbEQ7Ozs7OztPQU1HO0lBQ0gsWUFBWSxPQUFnQixFQUFFLE9BQThCLEVBQVM7UUFDakUsS0FBSyxFQUFFLENBQUM7UUFyQlosV0FBTSxHQUFZLEtBQUssQ0FBQztRQUN4QixXQUFNLEdBQVksS0FBSyxDQUFDO1FBSWhCLFFBQUcsR0FBVyxFQUFFLENBQUM7UUFpQnJCLElBQUksQ0FBQyxPQUFPLElBQUksT0FBTyxPQUFPLEtBQUssUUFBUSxFQUFFO1lBQ3pDLElBQUksQ0FBQyxHQUFHLEdBQUcsT0FBTyxJQUFJLDJCQUEyQixDQUFDO1lBQ2xELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLElBQUksRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQyxDQUFDLG9CQUFvQixJQUFJLE1BQU0sQ0FBQyxFQUFFO2dCQUNuQyxNQUFNLENBQUMsa0JBQWtCLEdBQUcsSUFBSSxDQUFDO2FBQ3BDO1lBQ0QsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7U0FDeEI7UUFDRCxJQUFJLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztRQUNyQixJQUFJLENBQUMsTUFB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