@eleven-am/pondsocket 0.1.197 → 0.1.198
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/engines/channelEngine.js +192 -39
- package/engines/endpointEngine.js +27 -15
- package/engines/lobbyEngine.js +94 -5
- package/engines/presenceEngine.js +140 -0
- package/index.js +0 -3
- package/package.json +3 -3
- package/requests/eventRequest.js +19 -1
- package/responses/eventResponse.js +30 -0
- package/responses/joinResponse.js +29 -3
- package/server/server.js +21 -32
- package/types.d.ts +32 -24
- package/wrappers/channel.js +37 -1
- package/wrappers/pondChannel.js +4 -0
- package/abstracts/redisClient.js +0 -499
- package/errors/redisError.js +0 -10
- package/managers/distributedManager.js +0 -165
- package/managers/localManager.js +0 -41
- package/managers/manager.js +0 -172
- package/managers/managerFactory.js +0 -20
package/wrappers/channel.js
CHANGED
|
@@ -19,48 +19,84 @@ class Channel {
|
|
|
19
19
|
_Channel_engine.set(this, void 0);
|
|
20
20
|
__classPrivateFieldSet(this, _Channel_engine, engine, "f");
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Gets a user's data
|
|
24
|
+
*/
|
|
22
25
|
getUserData(userId) {
|
|
23
|
-
return __classPrivateFieldGet(this, _Channel_engine, "f").
|
|
26
|
+
return __classPrivateFieldGet(this, _Channel_engine, "f").getUserData(userId);
|
|
24
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Gets all presence data
|
|
30
|
+
*/
|
|
25
31
|
getPresences() {
|
|
26
32
|
return __classPrivateFieldGet(this, _Channel_engine, "f").getPresence();
|
|
27
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Gets all assigns data
|
|
36
|
+
*/
|
|
28
37
|
getAssigns() {
|
|
29
38
|
return __classPrivateFieldGet(this, _Channel_engine, "f").getAssigns();
|
|
30
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Broadcasts a message to all users
|
|
42
|
+
*/
|
|
31
43
|
broadcast(event, payload) {
|
|
32
44
|
__classPrivateFieldGet(this, _Channel_engine, "f").sendMessage(pondsocket_common_1.SystemSender.CHANNEL, pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.BROADCAST, event, payload);
|
|
33
45
|
return this;
|
|
34
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Broadcasts a message from a specific user to all others
|
|
49
|
+
*/
|
|
35
50
|
broadcastFrom(userId, event, payload) {
|
|
36
51
|
__classPrivateFieldGet(this, _Channel_engine, "f").sendMessage(userId, pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER, pondsocket_common_1.ServerActions.BROADCAST, event, payload);
|
|
37
52
|
return this;
|
|
38
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Broadcasts a message to specific users
|
|
56
|
+
*/
|
|
39
57
|
broadcastTo(userIds, event, payload) {
|
|
40
58
|
const users = Array.isArray(userIds) ? userIds : [userIds];
|
|
41
59
|
__classPrivateFieldGet(this, _Channel_engine, "f").sendMessage(pondsocket_common_1.SystemSender.CHANNEL, users, pondsocket_common_1.ServerActions.BROADCAST, event, payload);
|
|
42
60
|
return this;
|
|
43
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Kicks a user from the channel
|
|
64
|
+
*/
|
|
44
65
|
evictUser(userId, reason) {
|
|
45
66
|
__classPrivateFieldGet(this, _Channel_engine, "f").kickUser(userId, reason !== null && reason !== void 0 ? reason : 'You have been banned from the channel');
|
|
46
67
|
return this;
|
|
47
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Tracks a user's presence
|
|
71
|
+
*/
|
|
48
72
|
trackPresence(userId, presence) {
|
|
49
73
|
__classPrivateFieldGet(this, _Channel_engine, "f").trackPresence(userId, presence);
|
|
50
74
|
return this;
|
|
51
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Updates a user's presence
|
|
78
|
+
*/
|
|
52
79
|
updatePresence(userId, presence) {
|
|
53
80
|
__classPrivateFieldGet(this, _Channel_engine, "f").updatePresence(userId, presence);
|
|
54
81
|
return this;
|
|
55
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Removes a user's presence
|
|
85
|
+
*/
|
|
56
86
|
removePresence(userId) {
|
|
57
87
|
__classPrivateFieldGet(this, _Channel_engine, "f").removePresence(userId);
|
|
58
88
|
return this;
|
|
59
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Adds or updates a user's presence
|
|
92
|
+
*/
|
|
60
93
|
upsertPresence(userId, presence) {
|
|
61
94
|
__classPrivateFieldGet(this, _Channel_engine, "f").upsertPresence(userId, presence);
|
|
62
95
|
return this;
|
|
63
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Updates a user's assigns
|
|
99
|
+
*/
|
|
64
100
|
updateAssigns(userId, assigns) {
|
|
65
101
|
__classPrivateFieldGet(this, _Channel_engine, "f").updateAssigns(userId, assigns);
|
|
66
102
|
return this;
|
package/wrappers/pondChannel.js
CHANGED
|
@@ -28,6 +28,10 @@ class PondChannel {
|
|
|
28
28
|
__classPrivateFieldGet(this, _PondChannel_lobby, "f").onLeave(callback);
|
|
29
29
|
return this;
|
|
30
30
|
}
|
|
31
|
+
onOutgoing(event, handler) {
|
|
32
|
+
__classPrivateFieldGet(this, _PondChannel_lobby, "f").handleOutgoingEvent(event, handler);
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
31
35
|
getChannel(channelName) {
|
|
32
36
|
try {
|
|
33
37
|
return __classPrivateFieldGet(this, _PondChannel_instances, "m", _PondChannel_getChannel).call(this, channelName);
|
package/abstracts/redisClient.js
DELETED
|
@@ -1,499 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
12
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
13
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
14
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
15
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
16
|
-
};
|
|
17
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
18
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
19
|
-
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");
|
|
20
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
21
|
-
};
|
|
22
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
23
|
-
var t = {};
|
|
24
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
25
|
-
t[p] = s[p];
|
|
26
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
27
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
28
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
29
|
-
t[p[i]] = s[p[i]];
|
|
30
|
-
}
|
|
31
|
-
return t;
|
|
32
|
-
};
|
|
33
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
34
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
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_stateSyncInterval, _RedisClient_ttlBuffer, _RedisClient_presence_changes_channel_get, _RedisClient_assigns_changes_channel_get, _RedisClient_channel_messages_channel_get, _RedisClient_user_leaves_channel_get, _RedisClient_handleErrors, _RedisClient_registerInstance, _RedisClient_unregisterInstance, _RedisClient_cleanupDisconnectedClients, _RedisClient_startPeriodicCleanup, _RedisClient_startHeartbeat, _RedisClient_unsubscribeFromChannels, _RedisClient_subscribeToChannels, _RedisClient_handleRedisMessage, _RedisClient_getPresenceCacheChannel, _RedisClient_getAssignsCacheChannel, _RedisClient_subscribeToCacheChanges, _RedisClient_subscribeToChannelMessages, _RedisClient_subscribeToUserLeaves, _RedisClient_subscribeToStateSync, _RedisClient_publishPresenceChange, _RedisClient_publishAssignsChange, _RedisClient_publishChannelMessage, _RedisClient_publishUserLeave, _RedisClient_publishCacheMessage, _RedisClient_emitStateSyncEvent, _RedisClient_getActiveInstances, _RedisClient_getCachedInstances, _RedisClient_generateCache, _RedisClient_handleExit;
|
|
37
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
-
exports.RedisClient = void 0;
|
|
39
|
-
const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
|
|
40
|
-
const ioredis_1 = __importDefault(require("ioredis"));
|
|
41
|
-
const redisError_1 = require("../errors/redisError");
|
|
42
|
-
class RedisClient {
|
|
43
|
-
constructor(config) {
|
|
44
|
-
var _a;
|
|
45
|
-
_RedisClient_instances.add(this);
|
|
46
|
-
_RedisClient_heartbeatInterval.set(this, void 0);
|
|
47
|
-
_RedisClient_cleanupInterval.set(this, void 0);
|
|
48
|
-
_RedisClient_instanceTtl.set(this, void 0);
|
|
49
|
-
_RedisClient_redisClient.set(this, void 0);
|
|
50
|
-
_RedisClient_pubClient.set(this, void 0);
|
|
51
|
-
_RedisClient_subClient.set(this, void 0);
|
|
52
|
-
_RedisClient_instanceId.set(this, void 0);
|
|
53
|
-
_RedisClient_assignsPublisher.set(this, void 0);
|
|
54
|
-
_RedisClient_presencePublisher.set(this, void 0);
|
|
55
|
-
_RedisClient_userLeavesPublisher.set(this, void 0);
|
|
56
|
-
_RedisClient_channelMessagePublisher.set(this, void 0);
|
|
57
|
-
_RedisClient_stateSyncPublisher.set(this, void 0);
|
|
58
|
-
_RedisClient_heartbeatTimer.set(this, void 0);
|
|
59
|
-
_RedisClient_cleanupTimer.set(this, void 0);
|
|
60
|
-
_RedisClient_stateSyncInterval.set(this, void 0);
|
|
61
|
-
_RedisClient_ttlBuffer.set(this, 5);
|
|
62
|
-
const baseTtl = (_a = config.instanceTtl) !== null && _a !== void 0 ? _a : 90;
|
|
63
|
-
const milliseconds = baseTtl * 1000;
|
|
64
|
-
__classPrivateFieldSet(this, _RedisClient_stateSyncInterval, milliseconds, "f");
|
|
65
|
-
__classPrivateFieldSet(this, _RedisClient_cleanupInterval, milliseconds * 2, "f");
|
|
66
|
-
__classPrivateFieldSet(this, _RedisClient_heartbeatInterval, milliseconds / 3, "f");
|
|
67
|
-
__classPrivateFieldSet(this, _RedisClient_instanceTtl, baseTtl + __classPrivateFieldGet(this, _RedisClient_ttlBuffer, "f"), "f");
|
|
68
|
-
__classPrivateFieldSet(this, _RedisClient_redisClient, new ioredis_1.default(config), "f");
|
|
69
|
-
__classPrivateFieldSet(this, _RedisClient_pubClient, new ioredis_1.default(config), "f");
|
|
70
|
-
__classPrivateFieldSet(this, _RedisClient_subClient, new ioredis_1.default(config), "f");
|
|
71
|
-
__classPrivateFieldSet(this, _RedisClient_assignsPublisher, new pondsocket_common_1.Subject(), "f");
|
|
72
|
-
__classPrivateFieldSet(this, _RedisClient_presencePublisher, new pondsocket_common_1.Subject(), "f");
|
|
73
|
-
__classPrivateFieldSet(this, _RedisClient_userLeavesPublisher, new pondsocket_common_1.Subject(), "f");
|
|
74
|
-
__classPrivateFieldSet(this, _RedisClient_channelMessagePublisher, new pondsocket_common_1.Subject(), "f");
|
|
75
|
-
__classPrivateFieldSet(this, _RedisClient_stateSyncPublisher, new pondsocket_common_1.Subject(), "f");
|
|
76
|
-
__classPrivateFieldSet(this, _RedisClient_instanceId, (0, pondsocket_common_1.uuid)(), "f");
|
|
77
|
-
}
|
|
78
|
-
buildClient(endpointId) {
|
|
79
|
-
return (channelId) => ({
|
|
80
|
-
channelId,
|
|
81
|
-
publishUserLeave: __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_publishUserLeave).bind(this, endpointId, channelId),
|
|
82
|
-
subscribeToStateSync: __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_subscribeToStateSync).bind(this, endpointId, channelId),
|
|
83
|
-
publishAssignsChange: __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_publishAssignsChange).bind(this, endpointId, channelId),
|
|
84
|
-
publishPresenceChange: __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_publishPresenceChange).bind(this, endpointId, channelId),
|
|
85
|
-
publishChannelMessage: __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_publishChannelMessage).bind(this, endpointId, channelId),
|
|
86
|
-
subscribeToUserLeaves: __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_subscribeToUserLeaves).bind(this, endpointId, channelId),
|
|
87
|
-
subscribeToChannelMessages: __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_subscribeToChannelMessages).bind(this, endpointId, channelId),
|
|
88
|
-
subscribeToPresenceChanges: __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_subscribeToCacheChanges).bind(this, endpointId, channelId, true),
|
|
89
|
-
subscribeToAssignsChanges: __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_subscribeToCacheChanges).bind(this, endpointId, channelId, false),
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
initialize() {
|
|
93
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
94
|
-
__classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_handleErrors).call(this);
|
|
95
|
-
yield __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_registerInstance).call(this);
|
|
96
|
-
__classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_startHeartbeat).call(this);
|
|
97
|
-
__classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_startPeriodicCleanup).call(this);
|
|
98
|
-
yield __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_subscribeToChannels).call(this);
|
|
99
|
-
process.on('SIGINT', __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_handleExit).bind(this));
|
|
100
|
-
process.on('SIGTERM', __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_handleExit).bind(this));
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
shutdown() {
|
|
104
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
105
|
-
clearInterval(__classPrivateFieldGet(this, _RedisClient_heartbeatTimer, "f"));
|
|
106
|
-
clearInterval(__classPrivateFieldGet(this, _RedisClient_cleanupTimer, "f"));
|
|
107
|
-
yield __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_unsubscribeFromChannels).call(this);
|
|
108
|
-
yield __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_unregisterInstance).call(this);
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
exports.RedisClient = RedisClient;
|
|
113
|
-
_RedisClient_heartbeatInterval = new WeakMap(), _RedisClient_cleanupInterval = new WeakMap(), _RedisClient_instanceTtl = new WeakMap(), _RedisClient_redisClient = new WeakMap(), _RedisClient_pubClient = new WeakMap(), _RedisClient_subClient = new WeakMap(), _RedisClient_instanceId = new WeakMap(), _RedisClient_assignsPublisher = new WeakMap(), _RedisClient_presencePublisher = new WeakMap(), _RedisClient_userLeavesPublisher = new WeakMap(), _RedisClient_channelMessagePublisher = new WeakMap(), _RedisClient_stateSyncPublisher = new WeakMap(), _RedisClient_heartbeatTimer = new WeakMap(), _RedisClient_cleanupTimer = new WeakMap(), _RedisClient_stateSyncInterval = new WeakMap(), _RedisClient_ttlBuffer = new WeakMap(), _RedisClient_instances = new WeakSet(), _RedisClient_presence_changes_channel_get = function _RedisClient_presence_changes_channel_get() {
|
|
114
|
-
return 'presence_changes';
|
|
115
|
-
}, _RedisClient_assigns_changes_channel_get = function _RedisClient_assigns_changes_channel_get() {
|
|
116
|
-
return 'assigns_changes';
|
|
117
|
-
}, _RedisClient_channel_messages_channel_get = function _RedisClient_channel_messages_channel_get() {
|
|
118
|
-
return 'channel_messages';
|
|
119
|
-
}, _RedisClient_user_leaves_channel_get = function _RedisClient_user_leaves_channel_get() {
|
|
120
|
-
return 'user_leaves';
|
|
121
|
-
}, _RedisClient_handleErrors = function _RedisClient_handleErrors() {
|
|
122
|
-
__classPrivateFieldGet(this, _RedisClient_redisClient, "f").on('error', (err) => console.error('Redis client error:', err));
|
|
123
|
-
__classPrivateFieldGet(this, _RedisClient_pubClient, "f").on('error', (err) => console.error('Redis pub client error:', err));
|
|
124
|
-
__classPrivateFieldGet(this, _RedisClient_subClient, "f").on('error', (err) => console.error('Redis sub client error:', err));
|
|
125
|
-
}, _RedisClient_registerInstance = function _RedisClient_registerInstance() {
|
|
126
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
127
|
-
try {
|
|
128
|
-
const now = Date.now().toString();
|
|
129
|
-
yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").set(`heartbeat:${__classPrivateFieldGet(this, _RedisClient_instanceId, "f")}`, now, 'EX', __classPrivateFieldGet(this, _RedisClient_instanceTtl, "f"));
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
throw new redisError_1.RedisError(`Error registering instance: ${error}`);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
}, _RedisClient_unregisterInstance = function _RedisClient_unregisterInstance() {
|
|
136
|
-
return __awaiter(this, arguments, void 0, function* (instanceId = __classPrivateFieldGet(this, _RedisClient_instanceId, "f")) {
|
|
137
|
-
const script = `
|
|
138
|
-
-- Delete heartbeat
|
|
139
|
-
redis.call('DEL', 'heartbeat:' .. ARGV[1])
|
|
140
|
-
|
|
141
|
-
-- Delete all cache keys in one SCAN operation
|
|
142
|
-
local cursor = '0'
|
|
143
|
-
repeat
|
|
144
|
-
local result = redis.call('SCAN', cursor, 'MATCH', '*_cache:' .. ARGV[1] .. ':*', 'COUNT', 100)
|
|
145
|
-
cursor = result[1]
|
|
146
|
-
local keys = result[2]
|
|
147
|
-
if #keys > 0 then
|
|
148
|
-
redis.call('DEL', unpack(keys))
|
|
149
|
-
end
|
|
150
|
-
until cursor == '0'
|
|
151
|
-
|
|
152
|
-
return 1
|
|
153
|
-
`;
|
|
154
|
-
try {
|
|
155
|
-
yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").eval(script, 0, instanceId);
|
|
156
|
-
}
|
|
157
|
-
catch (_a) {
|
|
158
|
-
// no-op as we're shutting down
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
}, _RedisClient_cleanupDisconnectedClients = function _RedisClient_cleanupDisconnectedClients() {
|
|
162
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
163
|
-
const script = `
|
|
164
|
-
-- Array of instance IDs that have cache data but no heartbeat
|
|
165
|
-
local dead_instances = cjson.decode(ARGV[1])
|
|
166
|
-
local affected_channels = {}
|
|
167
|
-
local keys_to_delete = {}
|
|
168
|
-
local batch_size = 100
|
|
169
|
-
|
|
170
|
-
-- Process each dead instance
|
|
171
|
-
for _, instance_id in ipairs(dead_instances) do
|
|
172
|
-
-- Find and process all cache keys for this instance
|
|
173
|
-
local cache_cursor = '0'
|
|
174
|
-
repeat
|
|
175
|
-
local result = redis.call('SCAN', cache_cursor, 'MATCH', '*_cache:' .. instance_id .. ':*', 'COUNT', batch_size)
|
|
176
|
-
cache_cursor = result[1]
|
|
177
|
-
|
|
178
|
-
for _, key in ipairs(result[2]) do
|
|
179
|
-
-- Extract channel info before deletion
|
|
180
|
-
local _, _, endpoint_id, channel_id = string.match(key, '_cache:[^:]+:([^:]+):([^:]+)')
|
|
181
|
-
if endpoint_id and channel_id then
|
|
182
|
-
affected_channels[endpoint_id .. ":" .. channel_id] = true
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
-- Add to deletion batch
|
|
186
|
-
table.insert(keys_to_delete, key)
|
|
187
|
-
|
|
188
|
-
-- If batch is full, process it
|
|
189
|
-
if #keys_to_delete >= batch_size then
|
|
190
|
-
redis.call('DEL', unpack(keys_to_delete))
|
|
191
|
-
keys_to_delete = {}
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
until cache_cursor == '0'
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
-- Delete any remaining keys
|
|
198
|
-
if #keys_to_delete > 0 then
|
|
199
|
-
redis.call('DEL', unpack(keys_to_delete))
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
-- Convert affected_channels to array
|
|
203
|
-
local channels = {}
|
|
204
|
-
for pair in pairs(affected_channels) do
|
|
205
|
-
table.insert(channels, pair)
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
return channels
|
|
209
|
-
`;
|
|
210
|
-
try {
|
|
211
|
-
const [activeInstances, cachedInstances] = yield Promise.all([
|
|
212
|
-
__classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_getActiveInstances).call(this),
|
|
213
|
-
__classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_getCachedInstances).call(this),
|
|
214
|
-
]);
|
|
215
|
-
const deadInstances = cachedInstances.filter((id) => !activeInstances.includes(id));
|
|
216
|
-
if (deadInstances.length === 0) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
const affectedChannels = yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").eval(script, 0, JSON.stringify(deadInstances));
|
|
220
|
-
if (affectedChannels.length > 0) {
|
|
221
|
-
const promises = affectedChannels.map((pair) => {
|
|
222
|
-
const [endpointId, channelId] = pair.split(':');
|
|
223
|
-
return __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_emitStateSyncEvent).call(this, endpointId, channelId);
|
|
224
|
-
});
|
|
225
|
-
yield Promise.all(promises);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
catch (error) {
|
|
229
|
-
console.error('Error cleaning up disconnected clients:', error);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
}, _RedisClient_startPeriodicCleanup = function _RedisClient_startPeriodicCleanup() {
|
|
233
|
-
__classPrivateFieldSet(this, _RedisClient_cleanupTimer, setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
234
|
-
try {
|
|
235
|
-
yield __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_cleanupDisconnectedClients).call(this);
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
console.error('Error in periodic cleanup:', error);
|
|
239
|
-
}
|
|
240
|
-
}), __classPrivateFieldGet(this, _RedisClient_cleanupInterval, "f")), "f");
|
|
241
|
-
}, _RedisClient_startHeartbeat = function _RedisClient_startHeartbeat() {
|
|
242
|
-
__classPrivateFieldSet(this, _RedisClient_heartbeatTimer, setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
243
|
-
try {
|
|
244
|
-
yield __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_registerInstance).call(this);
|
|
245
|
-
}
|
|
246
|
-
catch (error) {
|
|
247
|
-
console.error('Error performing heartbeat:', error);
|
|
248
|
-
}
|
|
249
|
-
}), __classPrivateFieldGet(this, _RedisClient_heartbeatInterval, "f")), "f");
|
|
250
|
-
}, _RedisClient_unsubscribeFromChannels = function _RedisClient_unsubscribeFromChannels() {
|
|
251
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
252
|
-
yield __classPrivateFieldGet(this, _RedisClient_subClient, "f").unsubscribe(__classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_presence_changes_channel_get), __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_assigns_changes_channel_get), __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_channel_messages_channel_get), __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_user_leaves_channel_get));
|
|
253
|
-
});
|
|
254
|
-
}, _RedisClient_subscribeToChannels = function _RedisClient_subscribeToChannels() {
|
|
255
|
-
return new Promise((resolve, reject) => {
|
|
256
|
-
__classPrivateFieldGet(this, _RedisClient_subClient, "f").subscribe(__classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_presence_changes_channel_get), __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_assigns_changes_channel_get), __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_channel_messages_channel_get), __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_user_leaves_channel_get), (err) => {
|
|
257
|
-
if (err) {
|
|
258
|
-
reject(err);
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
resolve();
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
__classPrivateFieldGet(this, _RedisClient_subClient, "f").on('message', __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_handleRedisMessage).bind(this));
|
|
265
|
-
});
|
|
266
|
-
}, _RedisClient_handleRedisMessage = function _RedisClient_handleRedisMessage(channel, message) {
|
|
267
|
-
const data = JSON.parse(message);
|
|
268
|
-
switch (channel) {
|
|
269
|
-
case __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_presence_changes_channel_get):
|
|
270
|
-
__classPrivateFieldGet(this, _RedisClient_presencePublisher, "f").publish(data);
|
|
271
|
-
break;
|
|
272
|
-
case __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_assigns_changes_channel_get):
|
|
273
|
-
__classPrivateFieldGet(this, _RedisClient_assignsPublisher, "f").publish(data);
|
|
274
|
-
break;
|
|
275
|
-
case __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_channel_messages_channel_get):
|
|
276
|
-
__classPrivateFieldGet(this, _RedisClient_channelMessagePublisher, "f").publish(data);
|
|
277
|
-
break;
|
|
278
|
-
case __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_user_leaves_channel_get):
|
|
279
|
-
__classPrivateFieldGet(this, _RedisClient_userLeavesPublisher, "f").publish(data);
|
|
280
|
-
break;
|
|
281
|
-
default:
|
|
282
|
-
throw new Error(`Unknown channel: ${channel}`);
|
|
283
|
-
}
|
|
284
|
-
}, _RedisClient_getPresenceCacheChannel = function _RedisClient_getPresenceCacheChannel(endpointId, channelId) {
|
|
285
|
-
return `presence_cache:${__classPrivateFieldGet(this, _RedisClient_instanceId, "f")}:${endpointId}:${channelId}`;
|
|
286
|
-
}, _RedisClient_getAssignsCacheChannel = function _RedisClient_getAssignsCacheChannel(endpointId, channelId) {
|
|
287
|
-
return `assigns_cache:${__classPrivateFieldGet(this, _RedisClient_instanceId, "f")}:${endpointId}:${channelId}`;
|
|
288
|
-
}, _RedisClient_subscribeToCacheChanges = function _RedisClient_subscribeToCacheChanges(endpoint, channel, presence, callback) {
|
|
289
|
-
const subject = presence ? __classPrivateFieldGet(this, _RedisClient_presencePublisher, "f") : __classPrivateFieldGet(this, _RedisClient_assignsPublisher, "f");
|
|
290
|
-
return subject.subscribe((_a) => {
|
|
291
|
-
var { endpointId, channelId } = _a, data = __rest(_a, ["endpointId", "channelId"]);
|
|
292
|
-
if (endpointId === endpoint && channelId === channel) {
|
|
293
|
-
return callback(data);
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
}, _RedisClient_subscribeToChannelMessages = function _RedisClient_subscribeToChannelMessages(endpoint, channel, callback) {
|
|
297
|
-
return __classPrivateFieldGet(this, _RedisClient_channelMessagePublisher, "f").subscribe(({ endpointId, channelId, message }) => {
|
|
298
|
-
if (endpointId === endpoint && channelId === channel) {
|
|
299
|
-
return callback(message);
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
}, _RedisClient_subscribeToUserLeaves = function _RedisClient_subscribeToUserLeaves(endpoint, channel, callback) {
|
|
303
|
-
return __classPrivateFieldGet(this, _RedisClient_userLeavesPublisher, "f").subscribe(({ endpointId, channelId, userId }) => {
|
|
304
|
-
if (endpointId === endpoint && channelId === channel) {
|
|
305
|
-
return callback(userId);
|
|
306
|
-
}
|
|
307
|
-
});
|
|
308
|
-
}, _RedisClient_subscribeToStateSync = function _RedisClient_subscribeToStateSync(endpoint, channel, callback) {
|
|
309
|
-
const interval = setInterval(() => __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_emitStateSyncEvent).call(this, endpoint, channel), __classPrivateFieldGet(this, _RedisClient_stateSyncInterval, "f"));
|
|
310
|
-
const subscription = __classPrivateFieldGet(this, _RedisClient_stateSyncPublisher, "f").subscribe((_a) => {
|
|
311
|
-
var { endpointId, channelId } = _a, data = __rest(_a, ["endpointId", "channelId"]);
|
|
312
|
-
if (endpointId === endpoint && channelId === channel) {
|
|
313
|
-
return callback(data);
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
void __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_emitStateSyncEvent).call(this, endpoint, channel, true);
|
|
317
|
-
return () => {
|
|
318
|
-
clearInterval(interval);
|
|
319
|
-
subscription();
|
|
320
|
-
};
|
|
321
|
-
}, _RedisClient_publishPresenceChange = function _RedisClient_publishPresenceChange(endpointId, channelId, userId, state) {
|
|
322
|
-
const message = {
|
|
323
|
-
userId,
|
|
324
|
-
channelId,
|
|
325
|
-
endpointId,
|
|
326
|
-
state,
|
|
327
|
-
};
|
|
328
|
-
const key = __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_presence_changes_channel_get);
|
|
329
|
-
const cacheKey = __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_getPresenceCacheChannel).call(this, message.endpointId, message.channelId);
|
|
330
|
-
return __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_publishCacheMessage).call(this, key, cacheKey, message);
|
|
331
|
-
}, _RedisClient_publishAssignsChange = function _RedisClient_publishAssignsChange(endpointId, channelId, userId, state) {
|
|
332
|
-
const message = {
|
|
333
|
-
userId,
|
|
334
|
-
channelId,
|
|
335
|
-
endpointId,
|
|
336
|
-
state,
|
|
337
|
-
};
|
|
338
|
-
const key = __classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_assigns_changes_channel_get);
|
|
339
|
-
const cacheKey = __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_getAssignsCacheChannel).call(this, message.endpointId, message.channelId);
|
|
340
|
-
return __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_publishCacheMessage).call(this, key, cacheKey, message);
|
|
341
|
-
}, _RedisClient_publishChannelMessage = function _RedisClient_publishChannelMessage(endpointId, channelId, message) {
|
|
342
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
343
|
-
const messageData = JSON.stringify({
|
|
344
|
-
endpointId,
|
|
345
|
-
channelId,
|
|
346
|
-
message,
|
|
347
|
-
});
|
|
348
|
-
yield __classPrivateFieldGet(this, _RedisClient_pubClient, "f").publish(__classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_channel_messages_channel_get), messageData);
|
|
349
|
-
});
|
|
350
|
-
}, _RedisClient_publishUserLeave = function _RedisClient_publishUserLeave(endpointId, channelId, userId) {
|
|
351
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
352
|
-
const message = JSON.stringify({
|
|
353
|
-
endpointId,
|
|
354
|
-
channelId,
|
|
355
|
-
userId,
|
|
356
|
-
});
|
|
357
|
-
yield __classPrivateFieldGet(this, _RedisClient_pubClient, "f").publish(__classPrivateFieldGet(this, _RedisClient_instances, "a", _RedisClient_user_leaves_channel_get), message);
|
|
358
|
-
});
|
|
359
|
-
}, _RedisClient_publishCacheMessage = function _RedisClient_publishCacheMessage(key, cacheKey, message) {
|
|
360
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
361
|
-
const script = `
|
|
362
|
-
local messageData = ARGV[1]
|
|
363
|
-
local userId = ARGV[2]
|
|
364
|
-
local state = ARGV[3]
|
|
365
|
-
|
|
366
|
-
if state ~= '' then
|
|
367
|
-
redis.call('HSET', KEYS[1], userId, state)
|
|
368
|
-
else
|
|
369
|
-
redis.call('HDEL', KEYS[1], userId)
|
|
370
|
-
end
|
|
371
|
-
|
|
372
|
-
redis.call('PUBLISH', KEYS[2], messageData)
|
|
373
|
-
|
|
374
|
-
return 1
|
|
375
|
-
`;
|
|
376
|
-
const messageData = JSON.stringify(message);
|
|
377
|
-
const state = message.state ? JSON.stringify(message.state) : '';
|
|
378
|
-
yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").eval(script, 2, cacheKey, key, messageData, message.userId, state);
|
|
379
|
-
});
|
|
380
|
-
}, _RedisClient_emitStateSyncEvent = function _RedisClient_emitStateSyncEvent(endpointId_1, channelId_1) {
|
|
381
|
-
return __awaiter(this, arguments, void 0, function* (endpointId, channelId, initialFetch = false) {
|
|
382
|
-
const script = `
|
|
383
|
-
local instances = ARGV[1]
|
|
384
|
-
local presence_data = {}
|
|
385
|
-
local assigns_data = {}
|
|
386
|
-
|
|
387
|
-
-- Parse instance IDs array
|
|
388
|
-
local instance_ids = cjson.decode(instances)
|
|
389
|
-
|
|
390
|
-
for _, instance in ipairs(instance_ids) do
|
|
391
|
-
local presence_key = 'presence_cache:' .. instance .. ':' .. ARGV[2] .. ':' .. ARGV[3]
|
|
392
|
-
local assigns_key = 'assigns_cache:' .. instance .. ':' .. ARGV[2] .. ':' .. ARGV[3]
|
|
393
|
-
|
|
394
|
-
-- Get all presence data for this instance and channel
|
|
395
|
-
local presence = redis.call('HGETALL', presence_key)
|
|
396
|
-
for i = 1, #presence, 2 do
|
|
397
|
-
presence_data[presence[i]] = presence[i + 1]
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
-- Get all assigns data for this instance and channel
|
|
401
|
-
local assigns = redis.call('HGETALL', assigns_key)
|
|
402
|
-
for i = 1, #assigns, 2 do
|
|
403
|
-
assigns_data[assigns[i]] = assigns[i + 1]
|
|
404
|
-
end
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
return {cjson.encode(presence_data), cjson.encode(assigns_data)}
|
|
408
|
-
`;
|
|
409
|
-
try {
|
|
410
|
-
// Get active instances
|
|
411
|
-
const activeInstances = yield __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_getActiveInstances).call(this);
|
|
412
|
-
// Get the data using our active instances
|
|
413
|
-
const [presenceData, assignsData] = yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").eval(script, 0, JSON.stringify(activeInstances), endpointId, channelId);
|
|
414
|
-
const event = {
|
|
415
|
-
endpointId,
|
|
416
|
-
channelId,
|
|
417
|
-
initialFetch,
|
|
418
|
-
presence: __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_generateCache).call(this, presenceData),
|
|
419
|
-
assigns: __classPrivateFieldGet(this, _RedisClient_instances, "m", _RedisClient_generateCache).call(this, assignsData),
|
|
420
|
-
};
|
|
421
|
-
__classPrivateFieldGet(this, _RedisClient_stateSyncPublisher, "f").publish(event);
|
|
422
|
-
}
|
|
423
|
-
catch (error) {
|
|
424
|
-
console.error('Error emitting state sync event:', error);
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
}, _RedisClient_getActiveInstances = function _RedisClient_getActiveInstances() {
|
|
428
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
429
|
-
const script = `
|
|
430
|
-
local active_instances = {}
|
|
431
|
-
|
|
432
|
-
local cursor = '0'
|
|
433
|
-
repeat
|
|
434
|
-
local result = redis.call('SCAN', cursor, 'MATCH', 'heartbeat:*', 'COUNT', 100)
|
|
435
|
-
cursor = result[1]
|
|
436
|
-
|
|
437
|
-
for _, key in ipairs(result[2]) do
|
|
438
|
-
local id = string.match(key, 'heartbeat:([^:]+)')
|
|
439
|
-
if id then
|
|
440
|
-
table.insert(active_instances, id)
|
|
441
|
-
end
|
|
442
|
-
end
|
|
443
|
-
until cursor == '0'
|
|
444
|
-
|
|
445
|
-
return active_instances
|
|
446
|
-
`;
|
|
447
|
-
try {
|
|
448
|
-
return yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").eval(script, 0);
|
|
449
|
-
}
|
|
450
|
-
catch (error) {
|
|
451
|
-
console.error('Error getting active instances:', error);
|
|
452
|
-
return [];
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
}, _RedisClient_getCachedInstances = function _RedisClient_getCachedInstances() {
|
|
456
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
457
|
-
const script = `
|
|
458
|
-
local seen = {}
|
|
459
|
-
local cached_instances = {}
|
|
460
|
-
|
|
461
|
-
local cursor = '0'
|
|
462
|
-
repeat
|
|
463
|
-
local result = redis.call('SCAN', cursor, 'MATCH', '*_cache:*', 'COUNT', 100)
|
|
464
|
-
cursor = result[1]
|
|
465
|
-
|
|
466
|
-
for _, key in ipairs(result[2]) do
|
|
467
|
-
local id = string.match(key, '_cache:([^:]+)')
|
|
468
|
-
if id and not seen[id] then
|
|
469
|
-
seen[id] = true
|
|
470
|
-
table.insert(cached_instances, id)
|
|
471
|
-
end
|
|
472
|
-
end
|
|
473
|
-
until cursor == '0'
|
|
474
|
-
|
|
475
|
-
return cached_instances
|
|
476
|
-
`;
|
|
477
|
-
try {
|
|
478
|
-
return yield __classPrivateFieldGet(this, _RedisClient_redisClient, "f").eval(script, 0);
|
|
479
|
-
}
|
|
480
|
-
catch (error) {
|
|
481
|
-
console.error('Error getting cached instances:', error);
|
|
482
|
-
return [];
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
}, _RedisClient_generateCache = function _RedisClient_generateCache(data) {
|
|
486
|
-
const first = Object.entries(JSON.parse(data));
|
|
487
|
-
const second = first.map(([key, value]) => [key, JSON.parse(String(value))]);
|
|
488
|
-
return new Map(second);
|
|
489
|
-
}, _RedisClient_handleExit = function _RedisClient_handleExit() {
|
|
490
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
491
|
-
try {
|
|
492
|
-
yield this.shutdown();
|
|
493
|
-
process.exit(0);
|
|
494
|
-
}
|
|
495
|
-
catch (error) {
|
|
496
|
-
process.exit(1);
|
|
497
|
-
}
|
|
498
|
-
});
|
|
499
|
-
};
|
package/errors/redisError.js
DELETED