@eleven-am/pondsocket 0.1.182 → 0.1.184

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.
@@ -33,7 +33,7 @@ var __rest = (this && this.__rest) || function (s, e) {
33
33
  var __importDefault = (this && this.__importDefault) || function (mod) {
34
34
  return (mod && mod.__esModule) ? mod : { "default": mod };
35
35
  };
36
- var _RedisClient_instances, _RedisClient_heartbeatInterval, _RedisClient_cleanupInterval, _RedisClient_instanceTtl, _RedisClient_redisClient, _RedisClient_pubClient, _RedisClient_subClient, _RedisClient_instanceId, _RedisClient_assignsPublisher, _RedisClient_presencePublisher, _RedisClient_userLeavesPublisher, _RedisClient_channelMessagePublisher, _RedisClient_stateSyncPublisher, _RedisClient_heartbeatTimer, _RedisClient_cleanupTimer, _RedisClient_presence_changes_channel_get, _RedisClient_assigns_changes_channel_get, _RedisClient_channel_messages_channel_get, _RedisClient_user_leaves_channel_get, _RedisClient_cleanup, _RedisClient_handleErrors, _RedisClient_getPresenceCacheChannel, _RedisClient_getAssignsCacheChannel, _RedisClient_publishPresenceChange, _RedisClient_publishAssignsChange, _RedisClient_publishChannelMessage, _RedisClient_publishUserLeave, _RedisClient_registerInstance, _RedisClient_unregisterInstance, _RedisClient_unsubscribeFromChannels, _RedisClient_subscribeToChannels, _RedisClient_startHeartbeat, _RedisClient_startPeriodicCleanup, _RedisClient_performConsistencyCheck, _RedisClient_handleRedisMessage, _RedisClient_publishCacheMessage, _RedisClient_emitStateSyncEvent, _RedisClient_subscribeToCacheChanges, _RedisClient_subscribeToChannelMessages, _RedisClient_subscribeToUserLeaves, _RedisClient_subscribeToStateSync, _RedisClient_handleExit;
36
+ var _RedisClient_instances, _RedisClient_heartbeatInterval, _RedisClient_cleanupInterval, _RedisClient_instanceTtl, _RedisClient_redisClient, _RedisClient_pubClient, _RedisClient_subClient, _RedisClient_instanceId, _RedisClient_assignsPublisher, _RedisClient_presencePublisher, _RedisClient_userLeavesPublisher, _RedisClient_channelMessagePublisher, _RedisClient_stateSyncPublisher, _RedisClient_heartbeatTimer, _RedisClient_cleanupTimer, _RedisClient_presence_changes_channel_get, _RedisClient_assigns_changes_channel_get, _RedisClient_channel_messages_channel_get, _RedisClient_user_leaves_channel_get, _RedisClient_handleErrors, _RedisClient_getPresenceCacheChannel, _RedisClient_getAssignsCacheChannel, _RedisClient_publishPresenceChange, _RedisClient_publishAssignsChange, _RedisClient_publishChannelMessage, _RedisClient_publishUserLeave, _RedisClient_registerInstance, _RedisClient_unregisterInstance, _RedisClient_unsubscribeFromChannels, _RedisClient_subscribeToChannels, _RedisClient_startHeartbeat, _RedisClient_startPeriodicCleanup, _RedisClient_performConsistencyCheck, _RedisClient_handleRedisMessage, _RedisClient_publishCacheMessage, _RedisClient_emitStateSyncEvent, _RedisClient_subscribeToCacheChanges, _RedisClient_subscribeToChannelMessages, _RedisClient_subscribeToUserLeaves, _RedisClient_subscribeToStateSync, _RedisClient_handleExit, _RedisClient_deleteKeysByPattern;
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
38
  exports.RedisClient = void 0;
39
39
  const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
@@ -97,11 +97,12 @@ class RedisClient {
97
97
  }
98
98
  shutdown() {
99
99
  return __awaiter(this, void 0, void 0, function* () {
100
+ const batchSize = 1000;
100
101
  clearInterval(__classPrivateFieldGet(this, _RedisClient_heartbeatTimer, "f"));
101
102
  clearInterval(__classPrivateFieldGet(this, _RedisClient_cleanupTimer, "f"));
102
103
  yield __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_unsubscribeFromChannels).call(this);
103
104
  yield __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_unregisterInstance).call(this);
104
- yield __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_cleanup).call(this);
105
+ yield __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_deleteKeysByPattern).call(this, `*_cache:${__classPrivateFieldGet(this, _RedisClient_instanceId, "f")}:*`, batchSize);
105
106
  });
106
107
  }
107
108
  }
@@ -114,15 +115,6 @@ _RedisClient_heartbeatInterval = new WeakMap(), _RedisClient_cleanupInterval = n
114
115
  return 'channel_messages';
115
116
  }, _RedisClient_user_leaves_channel_get = function _RedisClient_user_leaves_channel_get() {
116
117
  return 'user_leaves';
117
- }, _RedisClient_cleanup = function _RedisClient_cleanup() {
118
- return __awaiter(this, void 0, void 0, function* () {
119
- const pipeline = __classPrivateFieldGet(this, _RedisClient_redisClient, "f").pipeline();
120
- const presenceKeys = yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").keys('presence_cache:*');
121
- presenceKeys.forEach((key) => pipeline.del(key));
122
- const assignsKeys = yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").keys('assigns_cache:*');
123
- assignsKeys.forEach((key) => pipeline.del(key));
124
- yield pipeline.exec();
125
- });
126
118
  }, _RedisClient_handleErrors = function _RedisClient_handleErrors() {
127
119
  __classPrivateFieldGet(this, _RedisClient_redisClient, "f").on('error', (err) => console.error('Redis client error:', err));
128
120
  __classPrivateFieldGet(this, _RedisClient_pubClient, "f").on('error', (err) => console.error('Redis pub client error:', err));
@@ -312,8 +304,8 @@ _RedisClient_heartbeatInterval = new WeakMap(), _RedisClient_cleanupInterval = n
312
304
  const state = message.state ? JSON.stringify(message.state) : '';
313
305
  yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").eval(script, 2, cacheKey, key, messageData, message.userId, state);
314
306
  });
315
- }, _RedisClient_emitStateSyncEvent = function _RedisClient_emitStateSyncEvent(endpointId, channelId) {
316
- return __awaiter(this, void 0, void 0, function* () {
307
+ }, _RedisClient_emitStateSyncEvent = function _RedisClient_emitStateSyncEvent(endpointId_1, channelId_1) {
308
+ return __awaiter(this, arguments, void 0, function* (endpointId, channelId, initialFetch = false) {
317
309
  const script = `
318
310
  local active_instances = redis.call('SMEMBERS', 'distributed_instances')
319
311
  local presence_data = {}
@@ -341,6 +333,7 @@ _RedisClient_heartbeatInterval = new WeakMap(), _RedisClient_cleanupInterval = n
341
333
  const event = {
342
334
  endpointId,
343
335
  channelId,
336
+ initialFetch,
344
337
  presence: new Map(Object.entries(JSON.parse(presenceData))),
345
338
  assigns: new Map(Object.entries(JSON.parse(assignsData))),
346
339
  };
@@ -374,7 +367,7 @@ _RedisClient_heartbeatInterval = new WeakMap(), _RedisClient_cleanupInterval = n
374
367
  return callback(data);
375
368
  }
376
369
  });
377
- void __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_emitStateSyncEvent).call(this, endpoint, channel);
370
+ void __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_emitStateSyncEvent).call(this, endpoint, channel, true);
378
371
  return () => {
379
372
  clearInterval(interval);
380
373
  subscription();
@@ -389,4 +382,15 @@ _RedisClient_heartbeatInterval = new WeakMap(), _RedisClient_cleanupInterval = n
389
382
  process.exit(1);
390
383
  }
391
384
  });
385
+ }, _RedisClient_deleteKeysByPattern = function _RedisClient_deleteKeysByPattern(pattern, batchSize) {
386
+ return __awaiter(this, void 0, void 0, function* () {
387
+ let cursor = '0';
388
+ do {
389
+ const [newCursor, keys] = yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").scan(cursor, 'MATCH', pattern, 'COUNT', batchSize);
390
+ cursor = newCursor;
391
+ if (keys.length > 0) {
392
+ yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").del(...keys);
393
+ }
394
+ } while (cursor !== '0');
395
+ });
392
396
  };
@@ -10,13 +10,15 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _DistributedManager_client, _DistributedManager_subscriptions;
13
+ var _DistributedManager_instances, _DistributedManager_client, _DistributedManager_subscriptions, _DistributedManager_parseAndUpdatePresence;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.DistributedManager = void 0;
16
+ const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
16
17
  const manager_1 = require("./manager");
17
18
  class DistributedManager extends manager_1.Manager {
18
19
  constructor(client) {
19
20
  super(client.channelId);
21
+ _DistributedManager_instances.add(this);
20
22
  _DistributedManager_client.set(this, void 0);
21
23
  _DistributedManager_subscriptions.set(this, null);
22
24
  __classPrivateFieldSet(this, _DistributedManager_client, client, "f");
@@ -84,7 +86,7 @@ class DistributedManager extends manager_1.Manager {
84
86
  });
85
87
  const stateSyncSubscription = __classPrivateFieldGet(this, _DistributedManager_client, "f").subscribeToStateSync((state) => {
86
88
  this.assignsCache = new Map(state.assigns);
87
- this.presenceCache = new Map(state.presence);
89
+ __classPrivateFieldGet(this, _DistributedManager_instances, "m", _DistributedManager_parseAndUpdatePresence).call(this, new Map(state.presence), state.initialFetch);
88
90
  });
89
91
  __classPrivateFieldSet(this, _DistributedManager_subscriptions, () => {
90
92
  leaveSubscription();
@@ -101,4 +103,63 @@ class DistributedManager extends manager_1.Manager {
101
103
  }
102
104
  }
103
105
  exports.DistributedManager = DistributedManager;
104
- _DistributedManager_client = new WeakMap(), _DistributedManager_subscriptions = new WeakMap();
106
+ _DistributedManager_client = new WeakMap(), _DistributedManager_subscriptions = new WeakMap(), _DistributedManager_instances = new WeakSet(), _DistributedManager_parseAndUpdatePresence = function _DistributedManager_parseAndUpdatePresence(newCache, initialFetch) {
107
+ if (initialFetch) {
108
+ this.presenceCache = newCache;
109
+ }
110
+ const newUsers = new Set(newCache.keys());
111
+ const oldUsers = new Set(this.presenceCache.keys());
112
+ const usersToAdd = new Set([...newUsers].filter((user) => !oldUsers.has(user)));
113
+ const usersToRemove = new Set([...oldUsers].filter((user) => !newUsers.has(user)));
114
+ const noChange = new Set([...newUsers].filter((user) => oldUsers.has(user)));
115
+ const usersToUpdate = new Set([...noChange]
116
+ .filter((user) => {
117
+ const oldData = this.presenceCache.get(user);
118
+ const newData = newCache.get(user);
119
+ return JSON.stringify(oldData) !== JSON.stringify(newData);
120
+ }));
121
+ const upSertInfo = [
122
+ ...[...usersToAdd]
123
+ .map((user) => ({
124
+ userId: user,
125
+ state: newCache.get(user),
126
+ event: pondsocket_common_1.PresenceEventTypes.JOIN,
127
+ })),
128
+ ...[...usersToUpdate]
129
+ .map((user) => ({
130
+ userId: user,
131
+ state: newCache.get(user),
132
+ event: pondsocket_common_1.PresenceEventTypes.UPDATE,
133
+ })),
134
+ ];
135
+ const upsertMessages = upSertInfo.map(({ userId, state, event }) => {
136
+ this.presenceCache.set(userId, state);
137
+ return {
138
+ event,
139
+ requestId: (0, pondsocket_common_1.uuid)(),
140
+ recipients: Array.from(this.presenceCache.keys()),
141
+ channelName: this.channelId,
142
+ action: pondsocket_common_1.ServerActions.PRESENCE,
143
+ payload: {
144
+ changed: state,
145
+ presence: Array.from(this.presenceCache.values()),
146
+ },
147
+ };
148
+ });
149
+ const removeMessages = [...usersToRemove].map((user) => {
150
+ const current = this.presenceCache.get(user);
151
+ this.presenceCache.delete(user);
152
+ return {
153
+ event: pondsocket_common_1.PresenceEventTypes.LEAVE,
154
+ requestId: (0, pondsocket_common_1.uuid)(),
155
+ recipients: Array.from(this.presenceCache.keys()),
156
+ channelName: this.channelId,
157
+ action: pondsocket_common_1.ServerActions.PRESENCE,
158
+ payload: {
159
+ changed: current,
160
+ presence: Array.from(this.presenceCache.values()),
161
+ },
162
+ };
163
+ });
164
+ [...upsertMessages, ...removeMessages].forEach((message) => this.broadcast(message));
165
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eleven-am/pondsocket",
3
- "version": "0.1.182",
3
+ "version": "0.1.184",
4
4
  "description": "PondSocket is a fast simple socket server",
5
5
  "keywords": [
6
6
  "socket",