@clickup/ent-framework
Version:
A PostgreSQL graph-database-alike library with microsharding and row-level security
64 lines • 2.67 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TimelineManager = void 0;
const misc_1 = require("../internal/misc");
/**
* A side effect based container which holds the current master or replica
* timeline position. For master, the expectation is that the pos will be
* updated after each query only, so no need to use refreshMs. For replica, it's
* also updated after each query PLUS the class will call triggerRefresh() hook
* not more often than every refreshMs interval.
*/
class TimelineManager {
constructor(
/** Time interval after which a replica is declared as "caught up" even if
* it's not caught up. This is to not read from master forever when
* something has happened with the replica. */
maxLagMs,
/** Up to how often we call triggerRefresh(). */
refreshMs,
/** This method is called time to time to refresh the data which is later
* returned by currentPos(). Makes sense for replica connections which
* execute queries rarely: for them, the framework triggers the update when
* the fresh data is needed. */
triggerRefresh) {
this.maxLagMs = maxLagMs;
this.refreshMs = refreshMs;
this.triggerRefresh = triggerRefresh;
this.pos = BigInt(0);
this.changeTime = 0;
this.triggerRefreshPromise = null;
}
/**
* Returns the current Client's replication timeline position (e.g. WAL
* position).
*/
async currentPos() {
const refreshMs = (0, misc_1.maybeCall)(this.refreshMs);
if (performance.now() > this.changeTime + refreshMs) {
// Outdated pos; refresh it, also coalesce concurrent requests if any.
// Notice that we run this logic not only for replicas, but for masters
// too, because we can't distinguish the server role at this place. It's
// not a big deal and results into just one extra query within refreshMs
// time interval (which is typically ~1 second).
try {
this.triggerRefreshPromise ??= this.triggerRefresh();
await this.triggerRefreshPromise;
}
finally {
this.triggerRefreshPromise = null;
}
}
return this.pos;
}
/**
* Sets the actual timeline pos. Must be called by the Client after each
* interaction with the database.
*/
setCurrentPos(pos, force) {
this.pos = pos > this.pos || force ? pos : this.pos;
this.changeTime = performance.now();
}
}
exports.TimelineManager = TimelineManager;
//# sourceMappingURL=TimelineManager.js.map