@dxos/messaging 0.3.0 → 0.3.1-main.0fddbd4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +119 -99
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +117 -97
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/messenger.d.ts +0 -4
- package/dist/types/src/messenger.d.ts.map +1 -1
- package/dist/types/src/signal-manager/websocket-signal-manager.d.ts +3 -0
- package/dist/types/src/signal-manager/websocket-signal-manager.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/messenger.ts +1 -34
- package/src/signal-manager/websocket-signal-manager.ts +45 -7
package/src/messenger.ts
CHANGED
|
@@ -26,9 +26,6 @@ export interface MessengerOptions {
|
|
|
26
26
|
const ReliablePayload = schema.getCodecForType('dxos.mesh.messaging.ReliablePayload');
|
|
27
27
|
const Acknowledgement = schema.getCodecForType('dxos.mesh.messaging.Acknowledgement');
|
|
28
28
|
|
|
29
|
-
const ERROR_LIMIT = 5;
|
|
30
|
-
const NETWORK_REBOOT_DELAY = 3_000;
|
|
31
|
-
|
|
32
29
|
const RECEIVED_MESSAGES_GC_INTERVAL = 120_000;
|
|
33
30
|
|
|
34
31
|
/**
|
|
@@ -56,8 +53,6 @@ export class Messenger {
|
|
|
56
53
|
private _ctx!: Context;
|
|
57
54
|
private _closed = true;
|
|
58
55
|
private readonly _retryDelay: number;
|
|
59
|
-
private _errorCount = 0;
|
|
60
|
-
private _rebooting = false;
|
|
61
56
|
|
|
62
57
|
constructor({ signalManager, retryDelay = 300 }: MessengerOptions) {
|
|
63
58
|
this._signalManager = signalManager;
|
|
@@ -136,10 +131,9 @@ export class Messenger {
|
|
|
136
131
|
|
|
137
132
|
scheduleTask(
|
|
138
133
|
messageContext,
|
|
139
|
-
|
|
134
|
+
() => {
|
|
140
135
|
log('message not delivered', { messageId: reliablePayload.messageId });
|
|
141
136
|
this._onAckCallbacks.delete(reliablePayload.messageId!);
|
|
142
|
-
await this.errorLimitCheck();
|
|
143
137
|
timeoutHit(
|
|
144
138
|
new ProtocolTimeoutError(
|
|
145
139
|
'signaling message not delivered',
|
|
@@ -161,33 +155,6 @@ export class Messenger {
|
|
|
161
155
|
return promise;
|
|
162
156
|
}
|
|
163
157
|
|
|
164
|
-
private async errorLimitCheck() {
|
|
165
|
-
log(`checking error limit ${this._errorCount}`);
|
|
166
|
-
if (this._errorCount++ > ERROR_LIMIT) {
|
|
167
|
-
await this.rebootNetwork();
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
private async rebootNetwork() {
|
|
172
|
-
if (this._rebooting) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
this._rebooting = true;
|
|
176
|
-
|
|
177
|
-
log('rebooting Messenger/SignalManager');
|
|
178
|
-
await this.close();
|
|
179
|
-
await this._signalManager.close();
|
|
180
|
-
log('pausing');
|
|
181
|
-
await new Promise((resolve) => setTimeout(resolve, NETWORK_REBOOT_DELAY));
|
|
182
|
-
log('done pausing');
|
|
183
|
-
await this.open();
|
|
184
|
-
await this._signalManager.open();
|
|
185
|
-
log('done rebooting');
|
|
186
|
-
|
|
187
|
-
this._errorCount = 0;
|
|
188
|
-
this._rebooting = false;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
158
|
/**
|
|
192
159
|
* Subscribes onMessage function to messages that contains payload with payloadType.
|
|
193
160
|
* @param payloadType if not specified, onMessage will be subscribed to all types of messages.
|
|
@@ -2,19 +2,21 @@
|
|
|
2
2
|
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Event, synchronized } from '@dxos/async';
|
|
5
|
+
import { Event, sleep, synchronized } from '@dxos/async';
|
|
6
6
|
import { Any } from '@dxos/codec-protobuf';
|
|
7
7
|
import { Context } from '@dxos/context';
|
|
8
8
|
import { invariant } from '@dxos/invariant';
|
|
9
9
|
import { PublicKey } from '@dxos/keys';
|
|
10
10
|
import { log } from '@dxos/log';
|
|
11
|
-
import { trace } from '@dxos/protocols';
|
|
11
|
+
import { RateLimitExceededError, trace } from '@dxos/protocols';
|
|
12
12
|
import { Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
13
13
|
import { SwarmEvent } from '@dxos/protocols/proto/dxos/mesh/signal';
|
|
14
14
|
|
|
15
15
|
import { SignalManager } from './signal-manager';
|
|
16
16
|
import { CommandTrace, SignalClient, SignalStatus } from '../signal-client';
|
|
17
17
|
|
|
18
|
+
const MAX_SERVER_FAILURES = 5;
|
|
19
|
+
const WSS_SIGNAL_SERVER_REBOOT_DELAY = 3_000;
|
|
18
20
|
/**
|
|
19
21
|
* Manages connection to multiple Signal Servers over WebSocket
|
|
20
22
|
*/
|
|
@@ -24,6 +26,7 @@ export class WebsocketSignalManager implements SignalManager {
|
|
|
24
26
|
private _ctx!: Context;
|
|
25
27
|
private _opened = false;
|
|
26
28
|
|
|
29
|
+
readonly failureCount = new Map<string, number>();
|
|
27
30
|
readonly statusChanged = new Event<SignalStatus[]>();
|
|
28
31
|
readonly commandTrace = new Event<CommandTrace>();
|
|
29
32
|
readonly swarmEvent = new Event<{
|
|
@@ -53,6 +56,7 @@ export class WebsocketSignalManager implements SignalManager {
|
|
|
53
56
|
server.statusChanged.on(() => this.statusChanged.emit(this.getStatus()));
|
|
54
57
|
|
|
55
58
|
this._servers.set(host.server, server);
|
|
59
|
+
this.failureCount.set(host.server, 0);
|
|
56
60
|
server.commandTrace.on((trace) => this.commandTrace.emit(trace));
|
|
57
61
|
}
|
|
58
62
|
}
|
|
@@ -73,6 +77,7 @@ export class WebsocketSignalManager implements SignalManager {
|
|
|
73
77
|
log.trace('dxos.mesh.websocket-signal-manager.open', trace.end({ id: this._instanceId }));
|
|
74
78
|
}
|
|
75
79
|
|
|
80
|
+
@synchronized
|
|
76
81
|
async close() {
|
|
77
82
|
if (!this._opened) {
|
|
78
83
|
return;
|
|
@@ -84,6 +89,18 @@ export class WebsocketSignalManager implements SignalManager {
|
|
|
84
89
|
await Promise.all(Array.from(this._servers.values()).map((server) => server.close()));
|
|
85
90
|
}
|
|
86
91
|
|
|
92
|
+
async restartServer(serverName: string) {
|
|
93
|
+
log('Restarting server', { serverName });
|
|
94
|
+
invariant(this._opened, 'server already closed');
|
|
95
|
+
|
|
96
|
+
const server = this._servers.get(serverName);
|
|
97
|
+
invariant(server, 'server not found');
|
|
98
|
+
|
|
99
|
+
await server.close();
|
|
100
|
+
await sleep(WSS_SIGNAL_SERVER_REBOOT_DELAY);
|
|
101
|
+
await server.open();
|
|
102
|
+
}
|
|
103
|
+
|
|
87
104
|
getStatus(): SignalStatus[] {
|
|
88
105
|
return Array.from(this._servers.values()).map((server) => server.getStatus());
|
|
89
106
|
}
|
|
@@ -112,14 +129,33 @@ export class WebsocketSignalManager implements SignalManager {
|
|
|
112
129
|
recipient: PublicKey;
|
|
113
130
|
payload: Any;
|
|
114
131
|
}): Promise<void> {
|
|
115
|
-
log(`Signal ${recipient}`);
|
|
132
|
+
log(`Signal ${recipient.truncate()}`);
|
|
116
133
|
invariant(this._opened, 'Closed');
|
|
117
134
|
|
|
118
|
-
void this._forEachServer(async (server) => {
|
|
119
|
-
void server.sendMessage({ author, recipient, payload }).catch((err) =>
|
|
135
|
+
void this._forEachServer(async (server, serverName) => {
|
|
136
|
+
void server.sendMessage({ author, recipient, payload }).catch((err) => {
|
|
137
|
+
if (err instanceof RateLimitExceededError) {
|
|
138
|
+
log('WSS rate limit exceeded', { err });
|
|
139
|
+
} else {
|
|
140
|
+
log(`error sending to ${serverName}`, { err });
|
|
141
|
+
void this.checkServerFailure(serverName);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
120
144
|
});
|
|
121
145
|
}
|
|
122
146
|
|
|
147
|
+
@synchronized
|
|
148
|
+
async checkServerFailure(serverName: string) {
|
|
149
|
+
const failureCount = this.failureCount.get(serverName!) ?? 0;
|
|
150
|
+
if (failureCount > MAX_SERVER_FAILURES) {
|
|
151
|
+
log.warn(`Too many failures sending to ${serverName} (${failureCount} > ${MAX_SERVER_FAILURES}), restarting`);
|
|
152
|
+
await this.restartServer(serverName!);
|
|
153
|
+
this.failureCount.set(serverName!, 0);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
this.failureCount.set(serverName!, (this.failureCount.get(serverName!) ?? 0) + 1);
|
|
157
|
+
}
|
|
158
|
+
|
|
123
159
|
async subscribeMessages(peerId: PublicKey) {
|
|
124
160
|
log(`Subscribed for message stream peerId=${peerId}`);
|
|
125
161
|
invariant(this._opened, 'Closed');
|
|
@@ -140,7 +176,9 @@ export class WebsocketSignalManager implements SignalManager {
|
|
|
140
176
|
});
|
|
141
177
|
}
|
|
142
178
|
|
|
143
|
-
private async _forEachServer<ReturnType>(
|
|
144
|
-
|
|
179
|
+
private async _forEachServer<ReturnType>(
|
|
180
|
+
fn: (server: SignalClient, serverName: string) => Promise<ReturnType>,
|
|
181
|
+
): Promise<ReturnType[]> {
|
|
182
|
+
return Promise.all(Array.from(this._servers.entries()).map(([serverName, server]) => fn(server, serverName)));
|
|
145
183
|
}
|
|
146
184
|
}
|