telemersive-bus 0.6.14 → 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.
Files changed (2) hide show
  1. package/lib/BusManager.js +78 -54
  2. package/package.json +1 -1
package/lib/BusManager.js CHANGED
@@ -32,6 +32,10 @@ class BusManager {
32
32
  this.roomHousekeep = {};
33
33
  this.houseKeepingInProgress = false;
34
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;
35
39
  this.chatManagers = {};
36
40
  this.switchBoardURI = null;
37
41
  }
@@ -120,6 +124,12 @@ class BusManager {
120
124
  * when peer leaves a room
121
125
  */
122
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
+ }
123
133
  // we dont want to start another housekeeping process until the previous one has finished
124
134
  if(!this.houseKeepingInProgress){
125
135
  this.houseKeepingInProgress = true;
@@ -266,62 +276,76 @@ class BusManager {
266
276
  if (myId !== this.housekeepingId) {
267
277
  return;
268
278
  }
269
- if(this.houseKeepingInProgress) {
270
- console.log(` -> housekeeping -> cleaning out ...`);
271
- // flag the room as being in the process to be removed
272
- const flag4cleanupRooms = (currentRoom) => this.rooms[currentRoom.roomName].flagRoom4cleanup = true;
273
- Object.values(this.roomHousekeep).forEach(flag4cleanupRooms);
274
-
275
- // kick out the non-responding peers
276
- const kickOutPeer = async (currentPeer) => {
277
- console.log(` <- remove peer '${currentPeer.peerName}' from room '${currentPeer.roomName}' `);
278
- // first we send clear retain message to peer joined addresses that have not replied:
279
- await this.communicator.clearRetain(this.busTopics.roomPeerJoin(currentPeer.roomName, currentPeer.peerId).build());
280
-
281
- // create the payload for the leave message
282
- const infoPayload = {
283
- timestamp: Math.round(new Date().getTime() / 1000),
284
- peerId: currentPeer.peerId,
285
- peerName: currentPeer.peerName,
286
- roomName: currentPeer.roomName
287
- };
288
-
289
- // sends the left message to all peers joined in the same room
290
- await this.communicator.publish(
291
- new BusMsgPub(
292
- this.busTopics.roomPeerLeft(currentPeer.roomName, currentPeer.peerId).build(),
293
- PeerInfo, 2, true).encode(infoPayload));
294
- }
295
- const peerCleanup = async () => {
296
- for (const peer of Object.values(this.peerHousekeep)) {
297
- await kickOutPeer(peer);
298
- }
299
- };
300
- await peerCleanup();
301
-
302
- // And now remove the rooms
303
- const cleanupRooms = async (currentRoom) => await this.deleteRoom(currentRoom.roomName);
304
- const housekeeping = async () => {
305
- for (const room of Object.values(this.roomHousekeep)) {
306
- await cleanupRooms(room);
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`);
307
333
  }
308
- };
309
- await housekeeping();
310
-
311
- console.log(` -> housekeeping DONE`);
312
- } else {
313
- console.log(` -> housekeeping was interrupted`);
314
- }
315
334
 
316
- // and we stop by unsubscribing to all peer info messages inside all rooms
317
- await this.communicator.unsubscribe(this.busTopics.roomPeerJoin('+', '+').build());
318
- // unsubscribing to all room ping alive topic
319
- await this.communicator.unsubscribe(this.busTopics.baseRoomAlivePing("+").build());
320
- // cleanup the communicator
321
- this.communicator.clearAutoGeneratedSubscriptions();
322
- // set this flag to false at the end to make sure a new houskeeping cycle can only be started once
323
- // all topics are unsubscribed - otherwise it won't gather the joined peers inside the room.
324
- this.houseKeepingInProgress = false;
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;
325
349
  }
326
350
 
327
351
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telemersive-bus",
3
- "version": "0.6.14",
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": {