Skip to content

Commit cefb2e6

Browse files
committed
v3.0.3: cap socket.io reconnects + monitor cleanup on terminal failure
Defense-in-depth on top of v3.0.2's websocket-only fix. Three changes: @pryv/socket.io - io() now passes reconnectionAttempts: 10, reconnectionDelayMax: 60000, randomizationFactor: 0.5. socket.io-client defaults are infinite attempts at <=5s — one stuck client could drive ~17k req/day per tab. With these settings the client gives up after ~10 attempts spread over ~1 minute. - New 'reconnect_failed' listener tears down the underlying _io and emits 'error' so consumers can clean up instead of leaving a zombie socket reference. @pryv/monitor - UpdateMethod/Socket.js error handler now drops this.socket if the underlying _io has been torn down. A future Changes.READY will rebuild from scratch instead of short-circuiting on the dead handle. Plus npm audit fix (non-breaking) — 15 → 5 vulnerabilities (the remaining 5 need --force / major version bumps in dev deps; out of scope for this PR).
1 parent c931c13 commit cefb2e6

8 files changed

Lines changed: 140 additions & 107 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
<!-- Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) -->
44

5+
## [3.0.3](https://github.com/pryv/lib-js/compare/3.0.2...3.0.3)
6+
- `@pryv/socket.io`: cap reconnection (`reconnectionAttempts: 10`, `reconnectionDelayMax: 60000`, `randomizationFactor: 0.5`) so a server-side outage no longer drives an unbounded reconnect loop. Listens for `reconnect_failed` and surfaces a terminal `error` event so consumers can clean up instead of leaving a zombie socket reference.
7+
- `@pryv/monitor`: when the underlying socket.io transport dies (terminal `reconnect_failed`), drop the cached socket reference so a future `Changes.READY` cycle rebuilds from scratch instead of short-circuiting on the dead handle.
8+
59
## [3.0.2](https://github.com/pryv/lib-js/compare/3.0.0...3.0.2)
610
- Socket.IO: use WebSocket transport directly instead of polling-first. Fixes connection failures against service-core v2 cluster mode (which disables HTTP long-polling).
711

components/pryv-monitor/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pryv/monitor",
3-
"version": "3.0.2",
3+
"version": "3.0.3",
44
"description": "Extends `pryv` with event-driven notifications for changes on a Pryv.io account",
55
"keywords": [
66
"Pryv",

components/pryv-monitor/src/UpdateMethod/Socket.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,16 @@ class Socket extends UpdateMethod {
2323
this.socket = await this.monitor.connection.socket.open();
2424
this.socket.on('eventsChanged', () => { this.monitor.updateEvents(); });
2525
this.socket.on('streamsChanged', () => { this.monitor.updateStreams(); });
26-
this.socket.on('error', (error) => { this.monitor.emit(Changes.ERROR, error); });
26+
this.socket.on('error', (error) => {
27+
this.monitor.emit(Changes.ERROR, error);
28+
// If the underlying socket.io-client transport has been torn down
29+
// (i.e. SocketIO emitted 'error' after reconnect_failed), drop our
30+
// reference so a future Changes.READY can rebuild instead of
31+
// short-circuiting on the cached, dead handle.
32+
if (this.socket && !this.socket._io) {
33+
this.socket = null;
34+
}
35+
});
2736
}
2837

2938
async stop () {

components/pryv-socket.io/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pryv/socket.io",
3-
"version": "3.0.2",
3+
"version": "3.0.3",
44
"description": "Extends `pryv` with Socket.IO transport",
55
"keywords": [
66
"Pryv",

components/pryv-socket.io/src/SocketIO.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,29 @@ class SocketIO extends EventEmitter {
4040
this.connection.username()
4141
.then(username => {
4242
const socketEndpoint = this.connection.endpoint + username + '?auth=' + this.connection.token;
43+
// Cap reconnects so a server-side outage can't drive a runaway loop.
44+
// socket.io-client default is reconnectionAttempts: Infinity, max delay 5s.
45+
// With these settings, a stuck client gives up after ~10 attempts spread
46+
// over ~1 minute (with randomization), then surfaces 'error' to consumers.
4347
// @ts-ignore - io is callable in socket.io-client
44-
this._io = io(socketEndpoint, { forceNew: true, transports: ['websocket'] });
48+
this._io = io(socketEndpoint, {
49+
forceNew: true,
50+
transports: ['websocket'],
51+
reconnectionAttempts: 10,
52+
reconnectionDelayMax: 60000,
53+
randomizationFactor: 0.5
54+
});
55+
56+
// Terminal failure: socket.io-client gave up reconnecting.
57+
// Tear down our handle and surface 'error' so consumers (e.g. Monitor)
58+
// can clean up instead of leaving a zombie socket reference.
59+
this._io.on('reconnect_failed', () => {
60+
const dead = this._io;
61+
this._io = null;
62+
this.connecting = false;
63+
try { if (dead) dead.close(); } catch (ex) { }
64+
this.emit('error', new Error('socket.io: reconnect_failed (gave up after configured attempts)'));
65+
});
4566

4667
// handle failure
4768
for (const errcode of ['connect_error', 'connection_failed', 'error', 'connection_timeout']) {

components/pryv/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pryv",
3-
"version": "3.0.2",
3+
"version": "3.0.3",
44
"description": "Pryv JavaScript library",
55
"keywords": [
66
"Pryv",

0 commit comments

Comments
 (0)