Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion src/PgIpLock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export class PgIpLock implements AnyLock {

private static instances: PgIpLock[] = [];
private acquired = false;
private lockedReported = false;
private notifyHandler?: (message: Notification) => void;
private acquireTimer?: Timeout;

Expand Down Expand Up @@ -192,20 +193,51 @@ export class PgIpLock implements AnyLock {
}

this.acquired = true;

if (this.lockedReported) {
// state change: we were locked out and just took over, which
// means the previous holder's connection is gone
this.lockedReported = false;
this.options.logger.info(
`PgIpLock: acquired '${this.publicChannel}' - the ` +
'previous holder is gone',
);
}
} catch (err) {
// will throw, because insert duplicates existing lock
this.acquired = false;

const pgErr = err as { code?: string; detail?: string };

if (!(pgErr.code === 'P0001' && pgErr.detail === 'LOCKED')) {
if (pgErr.code === 'P0001' && pgErr.detail === 'LOCKED') {
// expected under singleListener, but the transition is worth
// one line: "someone else owns it" must be distinguishable
// from "everything is fine"
if (!this.lockedReported) {
this.lockedReported = true;
this.options.logger.info(
`PgIpLock: '${this.publicChannel}' is held by ` +
'another process, retrying every ' +
`${this.options.acquireInterval}ms`,
);
}
} else {
this.options.logger.error(err);
}
}

return this.acquired;
}

/**
* Channel name without the internal lock prefix, for log messages
*
* @return {string}
*/
private get publicChannel(): string {
return this.channel.replace(RX_LOCK_CHANNEL, '');
}

/**
* Ensures the lock schema exists, bootstrapping it at most once per
* connection: concurrent locks on the same client await a single shared
Expand Down
15 changes: 14 additions & 1 deletion src/PgPubSub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,11 +454,19 @@ export class PgPubSub extends EventEmitter {

const lock = await this.lock(channel);
const acquired = await lock.acquire();
// ignore else

if (acquired) {
await this.pgClient.query(`LISTEN ${ident(channel)}`);
this.emit('listen', channel);

return;
}

// not an error under singleListener - another process owns this
// channel. No log here on purpose: listen() is retried (timer,
// onRelease, reconnect) and would spam. PgIpLock.acquire() reports
// the same fact once per state change, and PgCache summarises the
// resulting coverage as `listening N/M channels`.
}

/**
Expand Down Expand Up @@ -746,6 +754,11 @@ export class PgPubSub extends EventEmitter {

this.reListenChannels = undefined;

this.logger.info(
`PgPubSub: reconnected after ${this.retry} retry(-ies), ` +
`re-subscribing ${channels.length} channel(s)`,
);

await Promise.all(channels.map(channel => this.listen(channel)));

this.emit('reconnect', this.retry);
Expand Down
Loading