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.
- package/dist/src/interfaces.js +12 -0
- package/dist/src/lib/cluster/cluster_master.js +246 -0
- package/dist/src/lib/cluster/node_master.js +355 -0
- package/dist/src/lib/cluster/services/api.js +663 -0
- package/dist/src/lib/cluster/services/assets.js +226 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetes/index.js +192 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8s.js +481 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8sResource.js +414 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetes/k8sState.js +59 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetes/utils.js +43 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/index.js +192 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/interfaces.js +2 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8s.js +423 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sDeploymentResource.js +60 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sJobResource.js +55 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sResource.js +359 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sServiceResource.js +37 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/k8sState.js +60 -0
- package/dist/src/lib/cluster/services/cluster/backends/kubernetesV2/utils.js +170 -0
- package/dist/src/lib/cluster/services/cluster/backends/native/dispatch.js +13 -0
- package/dist/src/lib/cluster/services/cluster/backends/native/index.js +526 -0
- package/dist/src/lib/cluster/services/cluster/backends/native/messaging.js +547 -0
- package/dist/src/lib/cluster/services/cluster/backends/state-utils.js +26 -0
- package/dist/src/lib/cluster/services/cluster/index.js +17 -0
- package/dist/src/lib/cluster/services/execution.js +435 -0
- package/dist/src/lib/cluster/services/index.js +6 -0
- package/dist/src/lib/cluster/services/interfaces.js +2 -0
- package/dist/src/lib/cluster/services/jobs.js +454 -0
- package/dist/src/lib/config/default-sysconfig.js +26 -0
- package/dist/src/lib/config/index.js +22 -0
- package/dist/src/lib/config/schemas/system.js +360 -0
- package/dist/src/lib/storage/analytics.js +86 -0
- package/dist/src/lib/storage/assets.js +401 -0
- package/dist/src/lib/storage/backends/elasticsearch_store.js +494 -0
- package/dist/src/lib/storage/backends/mappings/analytics.js +50 -0
- package/dist/src/lib/storage/backends/mappings/asset.js +41 -0
- package/dist/src/lib/storage/backends/mappings/ex.js +62 -0
- package/dist/src/lib/storage/backends/mappings/job.js +38 -0
- package/dist/src/lib/storage/backends/mappings/state.js +38 -0
- package/dist/src/lib/storage/backends/s3_store.js +237 -0
- package/dist/src/lib/storage/execution.js +300 -0
- package/dist/src/lib/storage/index.js +7 -0
- package/dist/src/lib/storage/jobs.js +81 -0
- package/dist/src/lib/storage/state.js +255 -0
- package/dist/src/lib/utils/api_utils.js +157 -0
- package/dist/src/lib/utils/asset_utils.js +94 -0
- package/dist/src/lib/utils/date_utils.js +52 -0
- package/dist/src/lib/utils/encoding_utils.js +27 -0
- package/dist/src/lib/utils/events.js +4 -0
- package/dist/src/lib/utils/file_utils.js +124 -0
- package/dist/src/lib/utils/id_utils.js +15 -0
- package/dist/src/lib/utils/port_utils.js +32 -0
- package/dist/src/lib/workers/assets/index.js +3 -0
- package/dist/src/lib/workers/assets/loader-executable.js +40 -0
- package/dist/src/lib/workers/assets/loader.js +73 -0
- package/dist/src/lib/workers/assets/spawn.js +55 -0
- package/dist/src/lib/workers/context/execution-context.js +12 -0
- package/dist/src/lib/workers/context/terafoundation-context.js +8 -0
- package/dist/src/lib/workers/execution-controller/execution-analytics.js +188 -0
- package/dist/src/lib/workers/execution-controller/index.js +1024 -0
- package/dist/src/lib/workers/execution-controller/recovery.js +151 -0
- package/dist/src/lib/workers/execution-controller/scheduler.js +390 -0
- package/dist/src/lib/workers/execution-controller/slice-analytics.js +96 -0
- package/dist/src/lib/workers/helpers/job.js +80 -0
- package/dist/src/lib/workers/helpers/op-analytics.js +22 -0
- package/dist/src/lib/workers/helpers/terafoundation.js +34 -0
- package/dist/src/lib/workers/helpers/worker-shutdown.js +169 -0
- package/dist/src/lib/workers/metrics/index.js +108 -0
- package/dist/src/lib/workers/worker/index.js +378 -0
- package/dist/src/lib/workers/worker/slice.js +122 -0
- package/dist/test/config/schemas/system_schema-spec.js +37 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8s-spec.js +316 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sResource-spec.js +795 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sState-multicluster-spec.js +67 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/k8sState-spec.js +84 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/utils-spec.js +132 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8s-v2-spec.js +455 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sResource-v2-spec.js +818 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-multicluster-v2-spec.js +67 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/k8sState-v2-spec.js +84 -0
- package/dist/test/lib/cluster/services/cluster/backends/kubernetes/v2/utils-v2-spec.js +320 -0
- package/dist/test/lib/cluster/services/cluster/backends/state-utils-spec.js +37 -0
- package/dist/test/node_master-spec.js +188 -0
- package/dist/test/services/api-spec.js +80 -0
- package/dist/test/services/assets-spec.js +158 -0
- package/dist/test/services/messaging-spec.js +440 -0
- package/dist/test/storage/assets_storage-spec.js +95 -0
- package/dist/test/storage/s3_store-spec.js +138 -0
- package/dist/test/test.config.js +8 -0
- package/dist/test/test.setup.js +6 -0
- package/dist/test/utils/api_utils-spec.js +86 -0
- package/dist/test/utils/asset_utils-spec.js +141 -0
- package/dist/test/utils/elastic_utils-spec.js +25 -0
- package/dist/test/workers/execution-controller/execution-controller-spec.js +371 -0
- package/dist/test/workers/execution-controller/execution-special-test-cases-spec.js +520 -0
- package/dist/test/workers/execution-controller/execution-test-cases-spec.js +338 -0
- package/dist/test/workers/execution-controller/recovery-spec.js +160 -0
- package/dist/test/workers/execution-controller/scheduler-spec.js +249 -0
- package/dist/test/workers/execution-controller/slice-analytics-spec.js +121 -0
- package/dist/test/workers/fixtures/ops/example-op/processor.js +20 -0
- package/dist/test/workers/fixtures/ops/example-op/schema.js +19 -0
- package/dist/test/workers/fixtures/ops/example-reader/fetcher.js +20 -0
- package/dist/test/workers/fixtures/ops/example-reader/schema.js +41 -0
- package/dist/test/workers/fixtures/ops/example-reader/slicer.js +37 -0
- package/dist/test/workers/fixtures/ops/new-op/processor.js +29 -0
- package/dist/test/workers/fixtures/ops/new-op/schema.js +18 -0
- package/dist/test/workers/fixtures/ops/new-reader/fetcher.js +19 -0
- package/dist/test/workers/fixtures/ops/new-reader/schema.js +23 -0
- package/dist/test/workers/fixtures/ops/new-reader/slicer.js +13 -0
- package/dist/test/workers/helpers/configs.js +130 -0
- package/dist/test/workers/helpers/execution-controller-helper.js +49 -0
- package/dist/test/workers/helpers/index.js +5 -0
- package/dist/test/workers/helpers/test-context.js +210 -0
- package/dist/test/workers/helpers/zip-directory.js +25 -0
- package/dist/test/workers/worker/slice-spec.js +333 -0
- package/dist/test/workers/worker/worker-spec.js +356 -0
- package/package.json +94 -94
- 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
|