telemersive-bus 0.6.13 → 0.6.15
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/lib/BusManager.js +97 -60
- package/package.json +2 -2
package/lib/BusManager.js
CHANGED
|
@@ -31,6 +31,11 @@ class BusManager {
|
|
|
31
31
|
this.peerHousekeep = {};
|
|
32
32
|
this.roomHousekeep = {};
|
|
33
33
|
this.houseKeepingInProgress = false;
|
|
34
|
+
this.housekeepingId = 0;
|
|
35
|
+
// promise set while cleanOut is actively mutating this.rooms, null otherwise.
|
|
36
|
+
// startHousekeeping awaits it before snapshotting this.rooms, so a new cycle
|
|
37
|
+
// cannot start before the previous one's room deletions have fully settled.
|
|
38
|
+
this.cleanOutInProgress = null;
|
|
34
39
|
this.chatManagers = {};
|
|
35
40
|
this.switchBoardURI = null;
|
|
36
41
|
}
|
|
@@ -119,9 +124,19 @@ class BusManager {
|
|
|
119
124
|
* when peer leaves a room
|
|
120
125
|
*/
|
|
121
126
|
startHousekeeping = async () => {
|
|
127
|
+
// wait for any in-flight cleanOut to fully settle before snapshotting this.rooms.
|
|
128
|
+
// this prevents overlap where a still-running cleanOut's slow stopServerSidePortScripts
|
|
129
|
+
// finishes (and deletes from this.rooms) after a new cycle already snapshotted them.
|
|
130
|
+
if (this.cleanOutInProgress) {
|
|
131
|
+
await this.cleanOutInProgress;
|
|
132
|
+
}
|
|
122
133
|
// we dont want to start another housekeeping process until the previous one has finished
|
|
123
134
|
if(!this.houseKeepingInProgress){
|
|
124
135
|
this.houseKeepingInProgress = true;
|
|
136
|
+
// each cycle gets a unique id so stale timers from an interrupted cycle can detect
|
|
137
|
+
// they no longer own the housekeeping state and bail out.
|
|
138
|
+
this.housekeepingId += 1;
|
|
139
|
+
const myId = this.housekeepingId;
|
|
125
140
|
console.log(` -> starting housekeeping -> gather all peers ...`);
|
|
126
141
|
this.peerHousekeep = {};
|
|
127
142
|
this.roomHousekeep = Object.assign({}, this.rooms); // clone the current room list
|
|
@@ -130,7 +145,7 @@ class BusManager {
|
|
|
130
145
|
await this.communicator.subscribe(new BusMsgSub(this.busTopics.roomPeerJoin('+', '+').build(), this.onAllPeers, PeerInfo, 0));
|
|
131
146
|
|
|
132
147
|
// now we have to wait a moment
|
|
133
|
-
setTimeout(this.pingAllPeersAlive, houseKeepingTimeOut);
|
|
148
|
+
setTimeout(() => this.pingAllPeersAlive(myId), houseKeepingTimeOut);
|
|
134
149
|
} else {
|
|
135
150
|
// if there is already a housekeeping going on,
|
|
136
151
|
// we wait for some time to finish it and try again.
|
|
@@ -154,7 +169,11 @@ class BusManager {
|
|
|
154
169
|
/**
|
|
155
170
|
* ping all peers that are subscribed to each individual rooms
|
|
156
171
|
*/
|
|
157
|
-
pingAllPeersAlive = async () => {
|
|
172
|
+
pingAllPeersAlive = async (myId) => {
|
|
173
|
+
// if a newer cycle has started, this callback belongs to an interrupted cycle — drop it.
|
|
174
|
+
if (myId !== this.housekeepingId) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
158
177
|
if(this.houseKeepingInProgress) {
|
|
159
178
|
console.log(` -> housekeeping -> pinging all rooms ...`);
|
|
160
179
|
const keys = Object.keys(this.rooms);
|
|
@@ -174,11 +193,11 @@ class BusManager {
|
|
|
174
193
|
));
|
|
175
194
|
}
|
|
176
195
|
// now we have to wait a moment
|
|
177
|
-
setTimeout(this.cleanOut, houseKeepingTimeOut);
|
|
196
|
+
setTimeout(() => this.cleanOut(myId), houseKeepingTimeOut);
|
|
178
197
|
} else {
|
|
179
198
|
// this means the housekeeping got interrupted and
|
|
180
199
|
// needs to finish as fast as possible to cleaning up all subscriptions.
|
|
181
|
-
setTimeout(this.cleanOut, 0);
|
|
200
|
+
setTimeout(() => this.cleanOut(myId), 0);
|
|
182
201
|
}
|
|
183
202
|
}
|
|
184
203
|
|
|
@@ -252,63 +271,81 @@ class BusManager {
|
|
|
252
271
|
/**
|
|
253
272
|
* cleanout dead peers and rooms
|
|
254
273
|
*/
|
|
255
|
-
cleanOut = async () => {
|
|
256
|
-
if
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const flag4cleanupRooms = (currentRoom) => this.rooms[currentRoom.roomName].flagRoom4cleanup = true;
|
|
260
|
-
Object.values(this.roomHousekeep).forEach(flag4cleanupRooms);
|
|
261
|
-
|
|
262
|
-
// kick out the non-responding peers
|
|
263
|
-
const kickOutPeer = async (currentPeer) => {
|
|
264
|
-
console.log(` <- remove peer '${currentPeer.peerName}' from room '${currentPeer.roomName}' `);
|
|
265
|
-
// first we send clear retain message to peer joined addresses that have not replied:
|
|
266
|
-
await this.communicator.clearRetain(this.busTopics.roomPeerJoin(currentPeer.roomName, currentPeer.peerId).build());
|
|
267
|
-
|
|
268
|
-
// create the payload for the leave message
|
|
269
|
-
const infoPayload = {
|
|
270
|
-
timestamp: Math.round(new Date().getTime() / 1000),
|
|
271
|
-
peerId: currentPeer.peerId,
|
|
272
|
-
peerName: currentPeer.peerName,
|
|
273
|
-
roomName: currentPeer.roomName
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
// sends the left message to all peers joined in the same room
|
|
277
|
-
await this.communicator.publish(
|
|
278
|
-
new BusMsgPub(
|
|
279
|
-
this.busTopics.roomPeerLeft(currentPeer.roomName, currentPeer.peerId).build(),
|
|
280
|
-
PeerInfo, 2, true).encode(infoPayload));
|
|
281
|
-
}
|
|
282
|
-
const peerCleanup = async () => {
|
|
283
|
-
for (const peer of Object.values(this.peerHousekeep)) {
|
|
284
|
-
await kickOutPeer(peer);
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
await peerCleanup();
|
|
288
|
-
|
|
289
|
-
// And now remove the rooms
|
|
290
|
-
const cleanupRooms = async (currentRoom) => await this.deleteRoom(currentRoom.roomName);
|
|
291
|
-
const housekeeping = async () => {
|
|
292
|
-
for (const room of Object.values(this.roomHousekeep)) {
|
|
293
|
-
await cleanupRooms(room);
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
await housekeeping();
|
|
297
|
-
|
|
298
|
-
console.log(` -> housekeeping DONE`);
|
|
299
|
-
} else {
|
|
300
|
-
console.log(` -> housekeeping was interrupted`);
|
|
274
|
+
cleanOut = async (myId) => {
|
|
275
|
+
// if a newer cycle has started, this callback belongs to an interrupted cycle — drop it.
|
|
276
|
+
if (myId !== this.housekeepingId) {
|
|
277
|
+
return;
|
|
301
278
|
}
|
|
279
|
+
// expose the in-flight cleanOut as a promise so the next startHousekeeping can await it
|
|
280
|
+
// and avoid snapshotting this.rooms while deleteRoom is still mutating it.
|
|
281
|
+
this.cleanOutInProgress = (async () => {
|
|
282
|
+
try {
|
|
283
|
+
if(this.houseKeepingInProgress) {
|
|
284
|
+
console.log(` -> housekeeping -> cleaning out ...`);
|
|
285
|
+
// flag the room as being in the process to be removed. the entry may already have
|
|
286
|
+
// been deleted from this.rooms by an earlier overlapping cleanOut whose slow
|
|
287
|
+
// stopServerSidePortScripts only just finished — in that case, nothing to flag.
|
|
288
|
+
const flag4cleanupRooms = (currentRoom) => {
|
|
289
|
+
const room = this.rooms[currentRoom.roomName];
|
|
290
|
+
if (room) room.flagRoom4cleanup = true;
|
|
291
|
+
};
|
|
292
|
+
Object.values(this.roomHousekeep).forEach(flag4cleanupRooms);
|
|
293
|
+
|
|
294
|
+
// kick out the non-responding peers
|
|
295
|
+
const kickOutPeer = async (currentPeer) => {
|
|
296
|
+
console.log(` <- remove peer '${currentPeer.peerName}' from room '${currentPeer.roomName}' `);
|
|
297
|
+
// first we send clear retain message to peer joined addresses that have not replied:
|
|
298
|
+
await this.communicator.clearRetain(this.busTopics.roomPeerJoin(currentPeer.roomName, currentPeer.peerId).build());
|
|
299
|
+
|
|
300
|
+
// create the payload for the leave message
|
|
301
|
+
const infoPayload = {
|
|
302
|
+
timestamp: Math.round(new Date().getTime() / 1000),
|
|
303
|
+
peerId: currentPeer.peerId,
|
|
304
|
+
peerName: currentPeer.peerName,
|
|
305
|
+
roomName: currentPeer.roomName
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// sends the left message to all peers joined in the same room
|
|
309
|
+
await this.communicator.publish(
|
|
310
|
+
new BusMsgPub(
|
|
311
|
+
this.busTopics.roomPeerLeft(currentPeer.roomName, currentPeer.peerId).build(),
|
|
312
|
+
PeerInfo, 2, true).encode(infoPayload));
|
|
313
|
+
}
|
|
314
|
+
const peerCleanup = async () => {
|
|
315
|
+
for (const peer of Object.values(this.peerHousekeep)) {
|
|
316
|
+
await kickOutPeer(peer);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
await peerCleanup();
|
|
320
|
+
|
|
321
|
+
// And now remove the rooms
|
|
322
|
+
const cleanupRooms = async (currentRoom) => await this.deleteRoom(currentRoom.roomName);
|
|
323
|
+
const housekeeping = async () => {
|
|
324
|
+
for (const room of Object.values(this.roomHousekeep)) {
|
|
325
|
+
await cleanupRooms(room);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
await housekeeping();
|
|
329
|
+
|
|
330
|
+
console.log(` -> housekeeping DONE`);
|
|
331
|
+
} else {
|
|
332
|
+
console.log(` -> housekeeping was interrupted`);
|
|
333
|
+
}
|
|
302
334
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
335
|
+
// and we stop by unsubscribing to all peer info messages inside all rooms
|
|
336
|
+
await this.communicator.unsubscribe(this.busTopics.roomPeerJoin('+', '+').build());
|
|
337
|
+
// unsubscribing to all room ping alive topic
|
|
338
|
+
await this.communicator.unsubscribe(this.busTopics.baseRoomAlivePing("+").build());
|
|
339
|
+
// cleanup the communicator
|
|
340
|
+
this.communicator.clearAutoGeneratedSubscriptions();
|
|
341
|
+
// set this flag to false at the end to make sure a new houskeeping cycle can only be started once
|
|
342
|
+
// all topics are unsubscribed - otherwise it won't gather the joined peers inside the room.
|
|
343
|
+
this.houseKeepingInProgress = false;
|
|
344
|
+
} finally {
|
|
345
|
+
this.cleanOutInProgress = null;
|
|
346
|
+
}
|
|
347
|
+
})();
|
|
348
|
+
await this.cleanOutInProgress;
|
|
312
349
|
}
|
|
313
350
|
|
|
314
351
|
/**
|
|
@@ -511,7 +548,7 @@ class BusManager {
|
|
|
511
548
|
// Generate short UUID as peerId
|
|
512
549
|
let roomUUID = this.translator.new(); // mhvXdrZT4jP5T8vBxuvm75
|
|
513
550
|
|
|
514
|
-
if(roomID === null)
|
|
551
|
+
if(roomID === null || roomID === undefined)
|
|
515
552
|
roomID = this.findUnusedRoomId();
|
|
516
553
|
this.rooms[_roomName] = {
|
|
517
554
|
roomName: _roomName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "telemersive-bus",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.15",
|
|
4
4
|
"description": "MQTT based data protocol to manage unlimited peers, connected by rooms, where all peers joined to a room can exchange private data. It provides a simple chat mechanism, latency pinging, publish-subscribe mechanism (based on mqtt) and a OSC-like data stream. ",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"crypto-js": "^4.0.0",
|
|
12
12
|
"internal-ip": "^6.2.0",
|
|
13
13
|
"mqtt": "^5.3.5",
|
|
14
|
-
"protobufjs": "^
|
|
14
|
+
"protobufjs": "^7.5.5",
|
|
15
15
|
"public-ip": "^4.0.2",
|
|
16
16
|
"sha1": "^1.1.1",
|
|
17
17
|
"short-uuid": "^4.1.0",
|