teraslice 2.11.0 → 2.12.0

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 (118) hide show
  1. package/dist/src/interfaces.js +12 -0
  2. package/dist/src/lib/cluster/cluster_master.js +246 -0
  3. package/dist/src/lib/cluster/node_master.js +355 -0
  4. package/dist/src/lib/cluster/services/api.js +663 -0
  5. package/dist/src/lib/cluster/services/assets.js +226 -0
  6. package/dist/src/lib/cluster/services/cluster/backends/kubernetes/index.js +192 -0
  7. package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8s.js +481 -0
  8. package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8sResource.js +414 -0
  9. package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8sState.js +59 -0
  10. package/dist/src/lib/cluster/services/cluster/backends/kubernetes/utils.js +43 -0
  11. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/index.js +192 -0
  12. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/interfaces.js +2 -0
  13. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8s.js +423 -0
  14. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sDeploymentResource.js +60 -0
  15. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sJobResource.js +55 -0
  16. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sResource.js +359 -0
  17. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sServiceResource.js +37 -0
  18. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sState.js +60 -0
  19. package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/utils.js +170 -0
  20. package/dist/src/lib/cluster/services/cluster/backends/native/dispatch.js +13 -0
  21. package/dist/src/lib/cluster/services/cluster/backends/native/index.js +526 -0
  22. package/dist/src/lib/cluster/services/cluster/backends/native/messaging.js +547 -0
  23. package/dist/src/lib/cluster/services/cluster/backends/state-utils.js +26 -0
  24. package/dist/src/lib/cluster/services/cluster/index.js +17 -0
  25. package/dist/src/lib/cluster/services/execution.js +435 -0
  26. package/dist/src/lib/cluster/services/index.js +6 -0
  27. package/dist/src/lib/cluster/services/interfaces.js +2 -0
  28. package/dist/src/lib/cluster/services/jobs.js +454 -0
  29. package/dist/src/lib/config/default-sysconfig.js +26 -0
  30. package/dist/src/lib/config/index.js +22 -0
  31. package/dist/src/lib/config/schemas/system.js +360 -0
  32. package/dist/src/lib/storage/analytics.js +86 -0
  33. package/dist/src/lib/storage/assets.js +401 -0
  34. package/dist/src/lib/storage/backends/elasticsearch_store.js +494 -0
  35. package/dist/src/lib/storage/backends/mappings/analytics.js +50 -0
  36. package/dist/src/lib/storage/backends/mappings/asset.js +41 -0
  37. package/dist/src/lib/storage/backends/mappings/ex.js +62 -0
  38. package/dist/src/lib/storage/backends/mappings/job.js +38 -0
  39. package/dist/src/lib/storage/backends/mappings/state.js +38 -0
  40. package/dist/src/lib/storage/backends/s3_store.js +237 -0
  41. package/dist/src/lib/storage/execution.js +300 -0
  42. package/dist/src/lib/storage/index.js +7 -0
  43. package/dist/src/lib/storage/jobs.js +81 -0
  44. package/dist/src/lib/storage/state.js +255 -0
  45. package/dist/src/lib/utils/api_utils.js +157 -0
  46. package/dist/src/lib/utils/asset_utils.js +94 -0
  47. package/dist/src/lib/utils/date_utils.js +52 -0
  48. package/dist/src/lib/utils/encoding_utils.js +27 -0
  49. package/dist/src/lib/utils/events.js +4 -0
  50. package/dist/src/lib/utils/file_utils.js +124 -0
  51. package/dist/src/lib/utils/id_utils.js +15 -0
  52. package/dist/src/lib/utils/port_utils.js +32 -0
  53. package/dist/src/lib/workers/assets/index.js +3 -0
  54. package/dist/src/lib/workers/assets/loader-executable.js +40 -0
  55. package/dist/src/lib/workers/assets/loader.js +73 -0
  56. package/dist/src/lib/workers/assets/spawn.js +55 -0
  57. package/dist/src/lib/workers/context/execution-context.js +12 -0
  58. package/dist/src/lib/workers/context/terafoundation-context.js +8 -0
  59. package/dist/src/lib/workers/execution-controller/execution-analytics.js +188 -0
  60. package/dist/src/lib/workers/execution-controller/index.js +1024 -0
  61. package/dist/src/lib/workers/execution-controller/recovery.js +151 -0
  62. package/dist/src/lib/workers/execution-controller/scheduler.js +390 -0
  63. package/dist/src/lib/workers/execution-controller/slice-analytics.js +96 -0
  64. package/dist/src/lib/workers/helpers/job.js +80 -0
  65. package/dist/src/lib/workers/helpers/op-analytics.js +22 -0
  66. package/dist/src/lib/workers/helpers/terafoundation.js +34 -0
  67. package/dist/src/lib/workers/helpers/worker-shutdown.js +169 -0
  68. package/dist/src/lib/workers/metrics/index.js +108 -0
  69. package/dist/src/lib/workers/worker/index.js +378 -0
  70. package/dist/src/lib/workers/worker/slice.js +122 -0
  71. package/dist/test/config/schemas/system_schema-spec.js +37 -0
  72. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8s-spec.js +316 -0
  73. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sResource-spec.js +795 -0
  74. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sState-multicluster-spec.js +67 -0
  75. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sState-spec.js +84 -0
  76. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/utils-spec.js +132 -0
  77. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8s-v2-spec.js +455 -0
  78. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sResource-v2-spec.js +818 -0
  79. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-multicluster-v2-spec.js +67 -0
  80. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-v2-spec.js +84 -0
  81. package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/utils-v2-spec.js +320 -0
  82. package/dist/test/lib/cluster/services/cluster/backends/state-utils-spec.js +37 -0
  83. package/dist/test/node_master-spec.js +188 -0
  84. package/dist/test/services/api-spec.js +80 -0
  85. package/dist/test/services/assets-spec.js +158 -0
  86. package/dist/test/services/messaging-spec.js +440 -0
  87. package/dist/test/storage/assets_storage-spec.js +95 -0
  88. package/dist/test/storage/s3_store-spec.js +138 -0
  89. package/dist/test/test.config.js +8 -0
  90. package/dist/test/test.setup.js +6 -0
  91. package/dist/test/utils/api_utils-spec.js +86 -0
  92. package/dist/test/utils/asset_utils-spec.js +141 -0
  93. package/dist/test/utils/elastic_utils-spec.js +25 -0
  94. package/dist/test/workers/execution-controller/execution-controller-spec.js +371 -0
  95. package/dist/test/workers/execution-controller/execution-special-test-cases-spec.js +520 -0
  96. package/dist/test/workers/execution-controller/execution-test-cases-spec.js +338 -0
  97. package/dist/test/workers/execution-controller/recovery-spec.js +160 -0
  98. package/dist/test/workers/execution-controller/scheduler-spec.js +249 -0
  99. package/dist/test/workers/execution-controller/slice-analytics-spec.js +121 -0
  100. package/dist/test/workers/fixtures/ops/example-op/processor.js +20 -0
  101. package/dist/test/workers/fixtures/ops/example-op/schema.js +19 -0
  102. package/dist/test/workers/fixtures/ops/example-reader/fetcher.js +20 -0
  103. package/dist/test/workers/fixtures/ops/example-reader/schema.js +41 -0
  104. package/dist/test/workers/fixtures/ops/example-reader/slicer.js +37 -0
  105. package/dist/test/workers/fixtures/ops/new-op/processor.js +29 -0
  106. package/dist/test/workers/fixtures/ops/new-op/schema.js +18 -0
  107. package/dist/test/workers/fixtures/ops/new-reader/fetcher.js +19 -0
  108. package/dist/test/workers/fixtures/ops/new-reader/schema.js +23 -0
  109. package/dist/test/workers/fixtures/ops/new-reader/slicer.js +13 -0
  110. package/dist/test/workers/helpers/configs.js +130 -0
  111. package/dist/test/workers/helpers/execution-controller-helper.js +49 -0
  112. package/dist/test/workers/helpers/index.js +5 -0
  113. package/dist/test/workers/helpers/test-context.js +210 -0
  114. package/dist/test/workers/helpers/zip-directory.js +25 -0
  115. package/dist/test/workers/worker/slice-spec.js +333 -0
  116. package/dist/test/workers/worker/worker-spec.js +356 -0
  117. package/package.json +94 -94
  118. package/service.js +0 -0
@@ -0,0 +1,547 @@
1
+ import { nanoid } from 'nanoid';
2
+ import { pDelay, Queue, isFunction, isEmpty, get, toNumber, isKey } from '@terascope/utils';
3
+ import socketIOClient from 'socket.io-client';
4
+ import socketIOServer from 'socket.io';
5
+ import { isProcessAssignment, ProcessAssignment } from '../../../../../../interfaces.js';
6
+ // messages send to cluster_master
7
+ const clusterMasterMessages = {
8
+ ipc: {
9
+ 'process:SIGTERM': 'SIGTERM',
10
+ 'process:SIGINT': 'SIGINT',
11
+ },
12
+ intraProcess: {
13
+ 'network:disconnect': 'disconnect',
14
+ 'network:error': 'error',
15
+ },
16
+ network: {
17
+ 'node:online': 'node:online',
18
+ 'node:state': 'node:state',
19
+ 'execution:recovery:failed': 'execution:recovery:failed',
20
+ 'execution:finished': 'execution:finished',
21
+ 'cluster:analytics': 'cluster:analytics',
22
+ 'execution:error:terminal': 'execution:error:terminal',
23
+ 'assets:preloaded': 'assets:preloaded',
24
+ 'assets:service:available': 'assets:service:available'
25
+ }
26
+ };
27
+ const nodeMasterMessages = {
28
+ ipc: {
29
+ 'cluster:error:terminal': 'cluster:error:terminal',
30
+ 'child:exit': 'exit'
31
+ },
32
+ intraProcess: {
33
+ 'network:connect': 'connect',
34
+ 'network:disconnect': 'disconnect',
35
+ 'network:error': 'error',
36
+ },
37
+ network: {
38
+ 'cluster:execution_controller:create': 'cluster:execution_controller:create',
39
+ 'cluster:workers:create': 'cluster:workers:create',
40
+ 'cluster:workers:remove': 'cluster:workers:remove',
41
+ 'cluster:node:state': 'cluster:node:state',
42
+ 'cluster:execution:stop': 'cluster:execution:stop',
43
+ 'cluster:node:get_port': 'cluster:node:get_port',
44
+ }
45
+ };
46
+ const assetServiceMessages = {
47
+ ipc: {
48
+ 'process:SIGTERM': 'SIGTERM',
49
+ 'process:SIGINT': 'SIGINT',
50
+ },
51
+ network: {}
52
+ };
53
+ // messaging destination relative towards each process type
54
+ export const routing = Object.freeze({
55
+ cluster_master: {
56
+ node_master: 'network',
57
+ },
58
+ node_master: {
59
+ cluster_process: 'ipc',
60
+ cluster_master: 'network',
61
+ },
62
+ assets_service: {
63
+ cluster_master: 'ipc'
64
+ }
65
+ });
66
+ export class Messaging {
67
+ context;
68
+ logger;
69
+ events;
70
+ configTimeout;
71
+ networkLatencyBuffer;
72
+ hostURL;
73
+ messsagingOnline = false;
74
+ messagingQueue = new Queue();
75
+ childHookFn;
76
+ config;
77
+ self;
78
+ selfMessages;
79
+ processContext;
80
+ functionMapping;
81
+ io;
82
+ constructor(context, logger, __io) {
83
+ if (__io) {
84
+ this.io = __io;
85
+ }
86
+ this.context = context;
87
+ this.functionMapping = {};
88
+ // processContext is set in _makeConfigurations
89
+ this.config = this._makeConfigurations();
90
+ const { hostURL } = this.config;
91
+ this.hostURL = hostURL;
92
+ this.configTimeout = context.sysconfig.teraslice.action_timeout;
93
+ this.networkLatencyBuffer = context.sysconfig.teraslice.network_latency_buffer;
94
+ this.self = this.config.assignment;
95
+ this.events = context.apis.foundation.getSystemEvents();
96
+ this.selfMessages = this._getMessages(this.self);
97
+ this.logger = logger;
98
+ // @ts-expect-error TODO: fixme
99
+ this.childHookFn = null;
100
+ this.logger.debug(`messaging service configuration for assignment ${this.config.assignment}`);
101
+ // set a default listener the is used for forwarding/completing responses
102
+ this.functionMapping['messaging:response'] = this._handleResponse.bind(this);
103
+ this.processContext.on('messaging:response', this._handleResponse.bind(this));
104
+ // all child processes need to set up a process listener on the 'message' event
105
+ if (this.config.clients.ipcClient) {
106
+ process.on('message', this._handleIpcMessages().bind(this));
107
+ }
108
+ else {
109
+ this.processContext.on('online', (worker) => {
110
+ this.logger.debug('worker process has come online');
111
+ if (this.childHookFn) {
112
+ this.childHookFn();
113
+ }
114
+ // @ts-expect-error
115
+ const contextWorker = this.processContext.workers[worker.id];
116
+ // don't double subscribe
117
+ contextWorker.removeListener('message', this._handleWorkerMessage.bind(this));
118
+ // set up a message handler on each child created, if a child is talking to cluster
119
+ // then pass it on, else invoke process event handler on node_master
120
+ contextWorker.on('message', this._handleWorkerMessage.bind(this));
121
+ });
122
+ }
123
+ }
124
+ _handleResponse(msgResponse) {
125
+ // if msg has returned to source then emit it else pass it along
126
+ if (msgResponse.__source === this.config.assignment) {
127
+ this.logger.trace(`node message ${msgResponse.__msgId} has been processed`);
128
+ // we are in the right spot, emit to complete the promise from send
129
+ this.events.emit(msgResponse.__msgId, msgResponse);
130
+ }
131
+ else {
132
+ this._forwardMessage(msgResponse);
133
+ }
134
+ }
135
+ respond(incoming, outgoing) {
136
+ const outgoingResponse = (outgoing && typeof outgoing === 'object') ? outgoing : {};
137
+ if (incoming.__msgId) {
138
+ outgoingResponse.__msgId = incoming.__msgId;
139
+ }
140
+ outgoingResponse.__source = incoming.__source;
141
+ outgoingResponse.message = 'messaging:response';
142
+ outgoingResponse.to = incoming.__source;
143
+ return this.send(outgoingResponse);
144
+ }
145
+ _findAndSend(filterFn, msg, msgHookFn) {
146
+ const childProcesses = this.context.cluster.workers;
147
+ const children = Object.values(childProcesses).filter(filterFn);
148
+ if (children.length === 0 && msg.response) {
149
+ // if there are no child processes found and it needs a response, answer back so
150
+ // that it does not hold for a long time
151
+ this.respond(msg);
152
+ }
153
+ children.forEach((childProcess) => {
154
+ if (msgHookFn)
155
+ msgHookFn(childProcess);
156
+ // @ts-expect-error
157
+ if (childProcess.connected) {
158
+ childProcess.send(msg);
159
+ }
160
+ else {
161
+ this.logger.warn('cannot send message to process', msg);
162
+ }
163
+ });
164
+ }
165
+ _sendToProcesses(msg) {
166
+ const msgExId = msg.ex_id || get(msg, 'payload.ex_id');
167
+ if (msgExId) {
168
+ // all processes that have the same assignment and exId
169
+ const filterFn = (process) => {
170
+ if (process.assignment !== msg.to)
171
+ return false;
172
+ if (process.ex_id !== msgExId)
173
+ return false;
174
+ return true;
175
+ };
176
+ this._findAndSend(filterFn, msg);
177
+ }
178
+ else {
179
+ // all processes that have the same assignment
180
+ const filterFn = (process) => process.assignment === msg.to;
181
+ this._findAndSend(filterFn, msg);
182
+ }
183
+ }
184
+ register(eventConfig) {
185
+ const eventName = eventConfig.event;
186
+ const { callback, identifier } = eventConfig;
187
+ const selfHasEvent = Object.values(this.selfMessages)
188
+ .some((type) => type[eventName] != null);
189
+ if (!selfHasEvent) {
190
+ throw new Error(`"${self}" cannot register for event, "${eventName}", in messaging module`);
191
+ }
192
+ if (this.selfMessages.ipc[eventName]) {
193
+ const realKey = this.selfMessages.ipc[eventName];
194
+ // this needs to be directly allocated so that IPC messaging can happen if
195
+ // network was not instantiated
196
+ this.processContext.on(realKey, callback);
197
+ }
198
+ else {
199
+ // we attach events etc later when the connection is made, this is async
200
+ // while others are sync registration
201
+ let trueEventName = this.selfMessages.network[eventName];
202
+ if (!trueEventName) {
203
+ trueEventName = this.selfMessages.intraProcess[eventName];
204
+ }
205
+ if (identifier) {
206
+ callback.__socketIdentifier = identifier;
207
+ }
208
+ this.functionMapping[trueEventName] = callback;
209
+ }
210
+ }
211
+ _registerFns(socket) {
212
+ for (const [key, func] of Object.entries(this.functionMapping)) {
213
+ if (func.__socketIdentifier) {
214
+ const wrappedFunc = (msg = {}) => {
215
+ const identifier = func.__socketIdentifier;
216
+ let id = msg[identifier];
217
+ // if already set, extract value else set it on socket
218
+ if (socket[identifier]) {
219
+ id = socket[identifier];
220
+ }
221
+ else {
222
+ socket[identifier] = id;
223
+ }
224
+ // if network host (slicer, cluster_master) and connection
225
+ // or retry event, join room
226
+ if (key === 'node:online') {
227
+ const rooms = Object.keys(socket.rooms);
228
+ const hasRoom = rooms.some((r) => r === id);
229
+ if (!hasRoom) {
230
+ this.logger.info(`joining room ${id}`);
231
+ socket.join(id);
232
+ }
233
+ }
234
+ // not all events have messages, if so then pass it, else just pass identifier
235
+ if (msg) {
236
+ func(msg, id, identifier);
237
+ }
238
+ else {
239
+ func(id);
240
+ }
241
+ };
242
+ socket.on(key, wrappedFunc);
243
+ }
244
+ else {
245
+ this.logger.trace(`setting listener key ${key}`);
246
+ socket.on(key, func);
247
+ }
248
+ }
249
+ }
250
+ _determinePathForMessage(messageSent) {
251
+ const { to } = messageSent;
252
+ let destinationType = undefined;
253
+ if (isKey(routing, this.self) && isKey(routing[this.self], to)) {
254
+ destinationType = routing[this.self][to];
255
+ }
256
+ // cluster_master has two types of connections to node_master, if it does not have a
257
+ // address then its talking to its own node_master through ipc
258
+ // TODO: reference self message, remove cluster_master specific code
259
+ if (this.self === 'cluster_master' && !messageSent.address && (messageSent.message in clusterMasterMessages.ipc)) {
260
+ destinationType = 'ipc';
261
+ }
262
+ if (destinationType === undefined) {
263
+ throw new Error(`could not determine how to pass on message to: ${JSON.stringify(messageSent)}`);
264
+ }
265
+ return destinationType;
266
+ }
267
+ // join rooms before on connect to avoid race conditions
268
+ _attachRoomsSocketIO() {
269
+ if (!this.io)
270
+ return;
271
+ // middleware
272
+ this.io.use((socket, next) => {
273
+ const { node_id: nodeId, } = socket.handshake.query;
274
+ if (nodeId) {
275
+ this.logger.info(`node ${nodeId} joining room on connect`);
276
+ socket.join(nodeId);
277
+ }
278
+ return next();
279
+ });
280
+ }
281
+ _createIOServer(options) {
282
+ const { server, port } = options;
283
+ const opts = {
284
+ path: '/native-clustering',
285
+ pingTimeout: this.configTimeout,
286
+ pingInterval: this.configTimeout + this.networkLatencyBuffer,
287
+ perMessageDeflate: false,
288
+ serveClient: false,
289
+ };
290
+ if (server) {
291
+ this.io = socketIOServer(server, opts);
292
+ }
293
+ else if (port) {
294
+ this.io = socketIOServer(port, opts);
295
+ }
296
+ this._attachRoomsSocketIO();
297
+ this.io.on('connection', (socket) => {
298
+ this.logger.debug('a connection to cluster_master has been made');
299
+ this._registerFns(socket);
300
+ });
301
+ }
302
+ listen(options = {}) {
303
+ const { query } = options;
304
+ this.messsagingOnline = true;
305
+ if (this.config.clients.networkClient) {
306
+ // node_master, worker
307
+ this.io = socketIOClient(this.hostURL, {
308
+ forceNew: true,
309
+ path: '/native-clustering',
310
+ query
311
+ });
312
+ this._registerFns(this.io);
313
+ if (this.self === 'node_master') {
314
+ this.io.on('networkMessage', (networkMsg) => {
315
+ const { message } = networkMsg;
316
+ const func = this.functionMapping[message];
317
+ if (func) {
318
+ func(networkMsg);
319
+ }
320
+ else {
321
+ // if no function is registered, it is meant to by passed along to child
322
+ this._sendToProcesses(networkMsg);
323
+ }
324
+ });
325
+ }
326
+ this.logger.debug('client network connection is online');
327
+ }
328
+ else {
329
+ // cluster_master and test processes
330
+ this._createIOServer(options);
331
+ }
332
+ // TODO: message queuing will be used until formal process lifecycles are implemented
333
+ while (this.messagingQueue.size() > 0) {
334
+ const cachedMessages = this.messagingQueue.dequeue();
335
+ // they are put in as a tuple, [realMsg, ipcMessage]
336
+ this.processContext.emit(cachedMessages[0], cachedMessages[1]);
337
+ }
338
+ }
339
+ broadcast(eventName, payload = {}) {
340
+ this.logger.trace('broadcasting a network message', { eventName, payload });
341
+ if (!payload.message) {
342
+ payload.message = eventName;
343
+ }
344
+ this.io.emit('networkMessage', payload);
345
+ }
346
+ _forwardMessage(messageSent) {
347
+ const messageType = this._determinePathForMessage(messageSent);
348
+ if (messageType === 'network') {
349
+ // worker and node_master communicate through broadcast to slicer/cluster_master
350
+ if (this.self === 'node_master') {
351
+ this.io.emit(messageSent.message, messageSent);
352
+ }
353
+ else if (this.self === 'cluster_master') {
354
+ this.io.sockets.in(messageSent.address).emit('networkMessage', messageSent);
355
+ }
356
+ else {
357
+ this.io.sockets.in(messageSent.address).emit(messageSent.message, messageSent);
358
+ }
359
+ }
360
+ else if (this.self === 'node_master') {
361
+ this._sendToProcesses(messageSent);
362
+ }
363
+ else if (this.processContext) {
364
+ if (this.processContext.connected) {
365
+ this.processContext.send(messageSent);
366
+ }
367
+ else {
368
+ this.logger.warn('cannot send to process because it is not connected', messageSent);
369
+ }
370
+ }
371
+ }
372
+ send(messageSent) {
373
+ if (!messageSent.__source) {
374
+ messageSent.__source = this.self;
375
+ }
376
+ const needsReply = messageSent.response;
377
+ if (!needsReply) {
378
+ this._forwardMessage(messageSent);
379
+ return Promise.resolve(true);
380
+ }
381
+ return new Promise((resolve, reject) => {
382
+ let timer;
383
+ const msgID = nanoid(8);
384
+ const actionTimeout = messageSent.timeout || this.configTimeout;
385
+ const messageTimeout = toNumber(actionTimeout) + this.networkLatencyBuffer;
386
+ messageSent.__msgId = msgID;
387
+ this.events.once(msgID, (nodeMasterData) => {
388
+ clearTimeout(timer);
389
+ if (nodeMasterData.error) {
390
+ reject(new Error(`${nodeMasterData.error} occurred on node: ${nodeMasterData.__source}`));
391
+ }
392
+ else {
393
+ resolve(nodeMasterData);
394
+ }
395
+ });
396
+ this._forwardMessage(messageSent);
397
+ timer = setTimeout(() => {
398
+ // remove listener to prevent memory leaks
399
+ this.events.removeAllListeners(msgID);
400
+ reject(new Error(`timeout error while communicating with ${messageSent.to}, msg: ${messageSent.message}, data: ${JSON.stringify(messageSent)}`));
401
+ }, messageTimeout);
402
+ });
403
+ }
404
+ _makeConfigurations() {
405
+ let host;
406
+ let port;
407
+ const options = {
408
+ node_master: { networkClient: true, ipcClient: false },
409
+ cluster_master: { networkClient: false, ipcClient: true },
410
+ execution_controller: { networkClient: false, ipcClient: true },
411
+ worker: { networkClient: true, ipcClient: true },
412
+ assets_service: { networkClient: true, ipcClient: true }
413
+ };
414
+ // @ts-expect-error
415
+ const env = this.context.__testingModule ? this.context.__testingModule.env : process.env;
416
+ const processConfig = {};
417
+ // @ts-expect-error
418
+ const testProcess = this.context.__testingModule;
419
+ const { assignment } = env;
420
+ if (!isProcessAssignment(assignment)) {
421
+ throw new Error(`assignment must be on of: ${Object.values(ProcessAssignment).toString()}. Received ${assignment}`);
422
+ }
423
+ processConfig.clients = options[assignment];
424
+ if (processConfig.clients.ipcClient) {
425
+ // all children of node_master
426
+ this.processContext = testProcess ? testProcess : process;
427
+ }
428
+ else {
429
+ // node_master
430
+ this.processContext = this.context.cluster;
431
+ }
432
+ if (processConfig.clients.networkClient) {
433
+ if (assignment === 'node_master' || assignment === 'assets_service') {
434
+ host = this.context.sysconfig.teraslice.master_hostname;
435
+ ({ port } = this.context.sysconfig.teraslice);
436
+ }
437
+ processConfig.hostURL = this._makeHostName(host, port);
438
+ }
439
+ processConfig.assignment = assignment;
440
+ return processConfig;
441
+ }
442
+ _makeHostName(host, port, nameSpace) {
443
+ let name;
444
+ let hostname = host;
445
+ if (!hostname.match(/http/)) {
446
+ hostname = `http://${hostname}`;
447
+ }
448
+ const lastChar = hostname[hostname.length - 1];
449
+ if (lastChar !== ':') {
450
+ name = `${hostname}:${port}`;
451
+ }
452
+ else {
453
+ name = hostname + port;
454
+ }
455
+ if (nameSpace) {
456
+ return `${name}/${nameSpace}`;
457
+ }
458
+ return name;
459
+ }
460
+ _getMessages(type) {
461
+ if (type === 'cluster_master')
462
+ return clusterMasterMessages;
463
+ if (type === 'node_master')
464
+ return nodeMasterMessages;
465
+ if (type === 'assets_service')
466
+ return assetServiceMessages;
467
+ return new Error(`could not find message model for type: ${type}`);
468
+ }
469
+ getHostUrl() {
470
+ if (this.hostURL) {
471
+ return this.hostURL;
472
+ }
473
+ return null;
474
+ }
475
+ getClientCounts() {
476
+ if (this.io) {
477
+ return this.io.eio.clientsCount;
478
+ }
479
+ // there are no connected clients because the network is not instantiated
480
+ return 0;
481
+ }
482
+ listRooms() {
483
+ const connected = get(this.io, 'sockets.connected', {});
484
+ if (isEmpty(connected))
485
+ return [];
486
+ return Object.values(connected).flatMap((meta) => Object.keys(meta.rooms));
487
+ }
488
+ registerChildOnlineHook(fn) {
489
+ this.childHookFn = fn;
490
+ }
491
+ _isExecutionStateQuery(msg) {
492
+ const stateQuery = {
493
+ 'cluster:execution:pause': 'cluster:execution:pause',
494
+ 'cluster:execution:resume': 'cluster:execution:resume',
495
+ 'cluster:slicer:analytics': 'cluster:slicer:analytics',
496
+ };
497
+ return (isKey(stateQuery, msg) && stateQuery[msg] !== undefined);
498
+ }
499
+ _emitIpcMessage(fn) {
500
+ return (ipcMessage) => {
501
+ const msg = ipcMessage.message;
502
+ const realMsg = get(this.selfMessages, `ipc.${msg}`, null);
503
+ if (realMsg) {
504
+ fn(realMsg, ipcMessage);
505
+ }
506
+ else {
507
+ this.logger.error(`process: ${self} has received a message: ${msg}, which is not registered in the messaging module`);
508
+ }
509
+ };
510
+ }
511
+ _handleIpcMessages() {
512
+ if (this.self === 'execution_controller') {
513
+ const checkAndEmit = (realMsg, ipcMessage) => {
514
+ if (this.messsagingOnline || !this._isExecutionStateQuery(realMsg)) {
515
+ this.processContext.emit(realMsg, ipcMessage);
516
+ }
517
+ else {
518
+ this.messagingQueue.enqueue([realMsg, ipcMessage]);
519
+ }
520
+ };
521
+ return this._emitIpcMessage(checkAndEmit);
522
+ }
523
+ // for everything else just emit the message
524
+ const emitFn = (realMsg, ipcMessage) => this.processContext.emit(realMsg, ipcMessage);
525
+ return this._emitIpcMessage(emitFn);
526
+ }
527
+ _handleWorkerMessage(ipcMessage) {
528
+ if (ipcMessage.to === 'cluster_master') {
529
+ this.logger.trace('network passing process message', ipcMessage.message, ipcMessage);
530
+ this.send(ipcMessage);
531
+ }
532
+ else if (ipcMessage.to === 'node_master') {
533
+ this.logger.trace('process message', ipcMessage.message, ipcMessage);
534
+ this.processContext.emit(ipcMessage.message, ipcMessage);
535
+ }
536
+ else {
537
+ this._sendToProcesses(ipcMessage);
538
+ }
539
+ }
540
+ async shutdown() {
541
+ if (this.io && isFunction(this.io.close)) {
542
+ this.io.close();
543
+ await pDelay(100);
544
+ }
545
+ }
546
+ }
547
+ //# sourceMappingURL=messaging.js.map
@@ -0,0 +1,26 @@
1
+ import { cloneDeep } from '@terascope/utils';
2
+ export function iterateState(clusterState, cb) {
3
+ // I clone here, because the code below accidentally modifies clusterState.
4
+ // Not sure if this is the best choice.
5
+ const state = cloneDeep(clusterState);
6
+ return Object.values(state)
7
+ .filter((node) => node.state === 'connected')
8
+ .flatMap((node) => {
9
+ const workers = node.active.filter(cb);
10
+ return workers.map((worker) => {
11
+ worker.node_id = node.node_id;
12
+ worker.hostname = node.hostname;
13
+ return worker;
14
+ });
15
+ });
16
+ }
17
+ export function findAllSlicers(clusterState) {
18
+ return iterateState(clusterState, (worker) => worker.assignment === 'execution_controller');
19
+ }
20
+ export function findWorkersByExecutionID(clusterState, exId) {
21
+ return iterateState(clusterState, (worker) => worker.assignment === 'worker' && worker.ex_id === exId);
22
+ }
23
+ export function findSlicersByExecutionID(clusterState, exIdDict) {
24
+ return iterateState(clusterState, (worker) => worker.assignment === 'execution_controller' && exIdDict[worker.ex_id]);
25
+ }
26
+ //# sourceMappingURL=state-utils.js.map
@@ -0,0 +1,17 @@
1
+ import { NativeClustering } from './backends/native/index.js';
2
+ import { KubernetesClusterBackend } from './backends/kubernetes/index.js';
3
+ import { KubernetesClusterBackendV2 } from './backends/kubernetesV2/index.js';
4
+ export function makeClustering(context, { clusterMasterServer }) {
5
+ const clusterType = context.sysconfig.teraslice.cluster_manager_type;
6
+ if (clusterType === 'native') {
7
+ return new NativeClustering(context, clusterMasterServer);
8
+ }
9
+ if (clusterType === 'kubernetes') {
10
+ return new KubernetesClusterBackend(context, clusterMasterServer);
11
+ }
12
+ if (clusterType === 'kubernetesV2') {
13
+ return new KubernetesClusterBackendV2(context, clusterMasterServer);
14
+ }
15
+ throw new Error(`unknown cluster service ${clusterType}, cannot find cluster module`);
16
+ }
17
+ //# sourceMappingURL=index.js.map