UNPKG

pg-mutex-lock

Version:
119 lines (118 loc) 4.27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const pg_connection_string_1 = require("pg-connection-string"); const crypto_1 = require("crypto"); const events_1 = require("events"); const driver_1 = require("./driver"); var EVENTS; (function (EVENTS) { EVENTS["CONNECTED"] = "connected"; EVENTS["CONNECT_FAILED"] = "connect_failed"; })(EVENTS || (EVENTS = {})); class PGMutexLock { constructor({ database = {}, timeout = 10 * 1000, retryCount = 3 } = {}) { this.emitter = new events_1.EventEmitter(); this.isConnected = false; this.timeout = timeout; this.retryCount = retryCount; const dbName = (() => { if (typeof database === 'string') { return pg_connection_string_1.parse(database).database; } else { const { database: dbName, connectionString } = database || {}; return dbName || (connectionString && pg_connection_string_1.parse(connectionString).database) || process.env.PGDATABASE || process.env.PGUSER || process.env.USER; } })(); this.client = driver_1.createClient(database); this.client.connect() .then(() => initTable(this.client, dbName)) .then((oid) => { this.databaseId = oid; this.isConnected = true; this.emitter.emit(EVENTS.CONNECTED); }) .catch((err) => { console.error(err); this.emitter.emit(EVENTS.CONNECT_FAILED, err); }); this.end = this.end.bind(this); this.acquireLock = this.acquireLock.bind(this); this.releaseLock = this.releaseLock.bind(this); } waitConnection() { return new Promise((resolve, reject) => { if (this.isConnected) { resolve(); return; } this.emitter.once(EVENTS.CONNECTED, () => { resolve(); }); this.emitter.once(EVENTS.CONNECT_FAILED, (err) => { reject(err); }); }); } async acquireLock(key) { let [classid, objid] = strToKey(key); await this.waitConnection(); // Check in session lock for (let i = 0; i < this.retryCount; i++) { let time = +new Date(); while (+new Date() - time < this.timeout) { let res = await this.client.query(` SELECT CASE count(*) WHEN 0 THEN (SELECT pg_try_advisory_lock($1, $2)) ELSE FALSE END as pg_try_advisory_lock FROM pg_locks WHERE pid = ( SELECT pg_backend_pid() ) AND locktype = 'advisory' AND classid = $1 AND objid = $2 AND "database" = $3; `, [classid, objid, this.databaseId]); if (res.rows[0].pg_try_advisory_lock == true) return true; await sleep(100); } } throw Error("Cannot acquire lock"); } async releaseLock(key) { let [classid, objid] = strToKey(key); await this.waitConnection(); let res = await this.client.query(` SELECT pg_advisory_unlock($1, $2); `, [classid, objid]); return res.rows[0].pg_advisory_unlock; } end() { return this.client.end(); } } exports.default = PGMutexLock; async function initTable(client, databasename) { let res = await client.query(` SELECT oid from pg_database WHERE datname=$1; `, [databasename]); if (res.rowCount == 0) throw Error("Table does not exists!!"); return res.rows[0].oid; } function strToKey(str) { let buf = crypto_1.createHash("sha256").update(str).digest(); return [buf.readInt32LE(0), buf.readInt32LE(4)]; } function sleep(time) { return new Promise((resolve) => setTimeout(resolve, time)); }