Skip to content

Commit

Permalink
Check lost connection timeouts during ping phase
Browse files Browse the repository at this point in the history
  • Loading branch information
goto-bus-stop committed Dec 7, 2024
1 parent 57f73b5 commit 1197ccd
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 22 deletions.
6 changes: 2 additions & 4 deletions src/SocketServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ class SocketServer {
/**
* Get the connection instance for a specific user.
*
* @param {User|string} user The user.
* @param {User|import('./schema.js').UserID} user The user.
* @returns {Connection|undefined}
*/
connection(user) {
Expand All @@ -730,9 +730,7 @@ class SocketServer {

ping() {
this.#connections.forEach((connection) => {
if ('socket' in connection) {
connection.ping();
}
connection.ping();
});
}

Expand Down
41 changes: 23 additions & 18 deletions src/sockets/LostConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import EventEmitter from 'node:events';
class LostConnection extends EventEmitter {
#logger;

#removeTimer;
#expiresAt;

#uw;

/**
* @param {import('../Uwave.js').default} uw
Expand All @@ -12,20 +14,15 @@ class LostConnection extends EventEmitter {
*/
constructor(uw, user, sessionID, timeout = 30) {
super();
this.uw = uw;
this.#uw = uw;
this.user = user;
this.sessionID = sessionID;
this.timeout = timeout;
this.#expiresAt = Date.now() + timeout * 1_000;
this.#logger = uw.logger.child({
ns: 'uwave:sockets', connectionType: 'LostConnection', userID: this.user.id, sessionID,
});

this.#initQueued();

this.#removeTimer = setTimeout(() => {
this.close();
this.uw.redis.del(this.#key, this.#messagesKey);
}, timeout * 1000);
this.#initQueued(timeout);
}

get #key() {
Expand All @@ -36,16 +33,17 @@ class LostConnection extends EventEmitter {
return `http-api:disconnected:${this.sessionID}:messages`;
}

#initQueued() {
/** @param {number} seconds */
#initQueued(seconds) {
// We expire the keys after timeout*10, because a server restart near the
// end of the timeout might mean that someone fails to reconnect. This way
// we can ensure that everyone still gets the full `timeout` duration to
// reconnect after a server restart, while also not filling up Redis with
// messages to users who left and will never return.
this.uw.redis.multi()
.set(this.#key, 'true', 'EX', this.timeout * 10)
this.#uw.redis.multi()
.set(this.#key, 'true', 'EX', seconds * 10)
.ltrim(this.#messagesKey, 0, 0)
.expire(this.#messagesKey, this.timeout * 10)
.expire(this.#messagesKey, seconds * 10)
.exec();
}

Expand All @@ -56,21 +54,28 @@ class LostConnection extends EventEmitter {
send(command, data) {
this.#logger.info({ command, data }, 'queue command');

this.uw.redis.rpush(
this.#uw.redis.rpush(
this.#messagesKey,
JSON.stringify({ command, data }),
);
}

ping() {
if (Date.now() > this.#expiresAt) {
this.close();
this.#uw.redis.del(this.#key, this.#messagesKey).catch(() => {
// No big deal
});
}
}

close() {
this.#logger.info('close');
queueMicrotask(() => {
this.emit('close');
});
this.emit('close');
}

removed() {
clearTimeout(this.#removeTimer);
// Nothing to do
}

toString() {
Expand Down

0 comments on commit 1197ccd

Please sign in to comment.