teraslice 2.10.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 -93
  118. package/service.js +0 -0
@@ -0,0 +1,378 @@
1
+ /* eslint-disable prefer-const */
2
+ import { get, getFullErrorStack, isFatalError, logError, pWhile } from '@terascope/utils';
3
+ import { ExecutionController, formatURL } from '@terascope/teraslice-messaging';
4
+ import { isPromAvailable } from '@terascope/job-components';
5
+ import { StateStorage, AnalyticsStorage } from '../../storage/index.js';
6
+ import { generateWorkerId, makeLogger } from '../helpers/terafoundation.js';
7
+ import { waitForWorkerShutdown } from '../helpers/worker-shutdown.js';
8
+ import { Metrics } from '../metrics/index.js';
9
+ import { SliceExecution } from './slice.js';
10
+ import { getPackageJSON } from '../../utils/file_utils.js';
11
+ export class Worker {
12
+ stateStorage;
13
+ analyticsStorage;
14
+ client;
15
+ metrics;
16
+ executionContext;
17
+ shutdownTimeout;
18
+ context;
19
+ workerId;
20
+ slice;
21
+ logger;
22
+ events;
23
+ isShuttingDown = false;
24
+ isProcessing = false;
25
+ isInitialized = false;
26
+ shouldShutdown = false;
27
+ forceShutdown = false;
28
+ slicesProcessed = 0;
29
+ isShutdown = false;
30
+ constructor(context, executionContext) {
31
+ const workerId = generateWorkerId(context);
32
+ // Use the bunyan logger.level() function to set the log level of context.logger equal
33
+ // to the log level of executionContext.logger.
34
+ // If a log_level was given in the job config, it will have overwritten the default
35
+ // log_level in the execution context.
36
+ context.logger.level(executionContext.logger.level());
37
+ const logger = makeLogger(context, 'worker');
38
+ const events = context.apis.foundation.getSystemEvents();
39
+ const { slicer_port: slicerPort, slicer_hostname: slicerHostname, performance_metrics: performanceMetrics } = executionContext.config;
40
+ const config = context.sysconfig.teraslice;
41
+ const networkLatencyBuffer = get(config, 'network_latency_buffer');
42
+ const actionTimeout = get(config, 'action_timeout');
43
+ const workerDisconnectTimeout = get(config, 'worker_disconnect_timeout');
44
+ const slicerTimeout = get(config, 'slicer_timeout');
45
+ const shutdownTimeout = get(config, 'shutdown_timeout');
46
+ this.stateStorage = new StateStorage(context);
47
+ this.analyticsStorage = new AnalyticsStorage(context);
48
+ this.client = new ExecutionController.Client({
49
+ executionControllerUrl: formatURL(slicerHostname, slicerPort),
50
+ workerId,
51
+ networkLatencyBuffer,
52
+ workerDisconnectTimeout,
53
+ // the connect timeout should be set to the same timeout that will
54
+ // cause the execution fail if no Workers connect
55
+ connectTimeout: slicerTimeout,
56
+ actionTimeout,
57
+ logger
58
+ });
59
+ this.metrics = performanceMetrics
60
+ ? new Metrics({
61
+ logger
62
+ })
63
+ : null;
64
+ this.executionContext = executionContext;
65
+ this.shutdownTimeout = shutdownTimeout;
66
+ this.context = context;
67
+ this.workerId = workerId;
68
+ this.logger = logger;
69
+ this.events = events;
70
+ }
71
+ async initialize() {
72
+ if (this.context.sysconfig.teraslice.cluster_manager_type === 'native') {
73
+ this.logger.warn('Skipping PromMetricsAPI initialization: incompatible with native clustering.');
74
+ }
75
+ else {
76
+ const { terafoundation } = this.context.sysconfig;
77
+ const { config, exId, jobId } = this.executionContext;
78
+ await this.context.apis.foundation.promMetrics.init({
79
+ terasliceName: this.context.sysconfig.teraslice.name,
80
+ assignment: 'worker',
81
+ logger: this.logger,
82
+ tf_prom_metrics_add_default: terafoundation.prom_metrics_add_default,
83
+ tf_prom_metrics_enabled: terafoundation.prom_metrics_enabled,
84
+ tf_prom_metrics_port: terafoundation.prom_metrics_port,
85
+ job_prom_metrics_add_default: config.prom_metrics_add_default,
86
+ job_prom_metrics_enabled: config.prom_metrics_enabled,
87
+ job_prom_metrics_port: config.prom_metrics_port,
88
+ labels: {
89
+ ex_id: exId,
90
+ job_id: jobId,
91
+ job_name: config.name,
92
+ assignment: 'worker'
93
+ },
94
+ prefix: 'teraslice_job_',
95
+ prom_metrics_display_url: terafoundation.prom_metrics_display_url
96
+ });
97
+ await this.setupPromMetrics();
98
+ }
99
+ const { context } = this;
100
+ this.isInitialized = true;
101
+ await Promise.all([
102
+ this.stateStorage.initialize(),
103
+ this.analyticsStorage.initialize(),
104
+ ]);
105
+ this.slice = new SliceExecution(context, this.executionContext, this.stateStorage, this.analyticsStorage);
106
+ this.client.onServerShutdown(() => {
107
+ this.logger.warn('Execution Controller shutdown, exiting...');
108
+ this.shouldShutdown = true;
109
+ });
110
+ await this.client.start();
111
+ // initialize the execution context next
112
+ await this.executionContext.initialize();
113
+ if (this.metrics != null) {
114
+ await this.metrics.initialize();
115
+ }
116
+ const { exId } = this.executionContext;
117
+ this.logger.info(`execution: ${exId} initialized worker`);
118
+ }
119
+ async run() {
120
+ let running = false;
121
+ const _run = async () => {
122
+ running = true;
123
+ try {
124
+ await this.runOnce();
125
+ }
126
+ catch (err) {
127
+ process.exitCode = 1;
128
+ logError(this.logger, err, 'Worker must shutdown due to fatal error');
129
+ this.forceShutdown = true;
130
+ }
131
+ finally {
132
+ running = false;
133
+ }
134
+ };
135
+ await new Promise((resolve) => {
136
+ const interval = setInterval(() => {
137
+ if (this.forceShutdown) {
138
+ done();
139
+ return;
140
+ }
141
+ if (running)
142
+ return;
143
+ if (this.isShuttingDown || this.shouldShutdown) {
144
+ done();
145
+ return;
146
+ }
147
+ _run();
148
+ }, 5);
149
+ function done() {
150
+ clearInterval(interval);
151
+ resolve(true);
152
+ }
153
+ });
154
+ }
155
+ async runOnce() {
156
+ if (this.isShuttingDown || this.forceShutdown || this.shouldShutdown)
157
+ return;
158
+ this.logger.trace('waiting for new slice from execution controller');
159
+ const msg = await this.client.waitForSlice(() => this.isShuttingDown);
160
+ if (!msg) {
161
+ this.logger.debug(`${this.workerId} worker is idle`);
162
+ return;
163
+ }
164
+ this.isProcessing = true;
165
+ let sentSliceComplete = false;
166
+ const { slice_id: sliceId } = msg;
167
+ try {
168
+ await this.slice.initialize(msg);
169
+ await this.slice.run();
170
+ this.logger.info(`slice ${sliceId} completed`);
171
+ await this._sendSliceComplete({
172
+ slice: this.slice.slice,
173
+ analytics: this.slice.analyticsData
174
+ });
175
+ sentSliceComplete = true;
176
+ await this.executionContext.onSliceFinished();
177
+ }
178
+ catch (err) {
179
+ logError(this.logger, err, `slice ${sliceId} run error`);
180
+ if (!sentSliceComplete) {
181
+ await this._sendSliceComplete({
182
+ slice: this.slice.slice,
183
+ analytics: this.slice.analyticsData,
184
+ error: getFullErrorStack(err)
185
+ });
186
+ }
187
+ if (isFatalError(err)) {
188
+ throw err;
189
+ }
190
+ }
191
+ this.isProcessing = false;
192
+ this.slicesProcessed += 1;
193
+ }
194
+ async shutdown(event, shutdownError, block) {
195
+ if (this.isShutdown)
196
+ return;
197
+ if (!this.isInitialized)
198
+ return;
199
+ const { exId } = this.executionContext;
200
+ if (this.isShuttingDown) {
201
+ const msgs = [
202
+ 'worker',
203
+ `shutdown was called for ${exId}`,
204
+ 'but it was already shutting down',
205
+ block !== false ? ', will block until done' : ''
206
+ ];
207
+ this.logger.debug(msgs.join(' '));
208
+ if (block !== false) {
209
+ await waitForWorkerShutdown(this.context, 'worker:shutdown:complete');
210
+ }
211
+ return;
212
+ }
213
+ this.client.available = false;
214
+ this.isShuttingDown = true;
215
+ const shutdownErrs = [];
216
+ const pushError = (err) => {
217
+ shutdownErrs.push(err);
218
+ };
219
+ const extra = event ? ` due to event: ${event}` : '';
220
+ this.logger.warn(`worker shutdown was called for execution ${exId}${extra}`);
221
+ // set the slice to to failed to avoid
222
+ // flushing the slice at the end
223
+ // we need to check if this.executionContext.sliceState
224
+ // in case a slice isn't currently active
225
+ if (shutdownError && this.executionContext.sliceState) {
226
+ this.executionContext.onSliceFailed();
227
+ }
228
+ // attempt to flush the slice
229
+ // and wait for the slice to finish
230
+ await Promise.all([
231
+ this.slice.flush().catch(pushError),
232
+ this._waitForSliceToFinish().catch(pushError)
233
+ ]);
234
+ this.events.emit('worker:shutdown');
235
+ await this.executionContext.shutdown();
236
+ // make sure ->run() resolves the promise
237
+ this.forceShutdown = true;
238
+ await Promise.all([
239
+ (async () => {
240
+ await Promise.all([
241
+ (async () => {
242
+ try {
243
+ await this.stateStorage.shutdown(true);
244
+ }
245
+ catch (err) {
246
+ pushError(err);
247
+ }
248
+ })(),
249
+ (async () => {
250
+ try {
251
+ await this.analyticsStorage.shutdown(true);
252
+ }
253
+ catch (err) {
254
+ pushError(err);
255
+ }
256
+ })()
257
+ ]);
258
+ })(),
259
+ (async () => {
260
+ await this.slice.shutdown().catch(pushError);
261
+ })(),
262
+ (async () => {
263
+ await this.client.shutdown().catch(pushError);
264
+ })(),
265
+ (async () => {
266
+ if (this.metrics == null)
267
+ return;
268
+ await this.metrics.shutdown().catch(pushError);
269
+ })()
270
+ ]);
271
+ const n = this.slicesProcessed;
272
+ this.logger.warn(`worker ${this.workerId} is shutdown for execution ${exId}, processed ${n} slices`);
273
+ this.isShutdown = true;
274
+ if (shutdownErrs.length) {
275
+ const errMsg = shutdownErrs.map((e) => e.stack).join(', and');
276
+ const shutdownErr = new Error(`Failed to shutdown correctly: ${errMsg}`);
277
+ this.events.emit('worker:shutdown:complete', shutdownErr);
278
+ throw shutdownErr;
279
+ }
280
+ // TODO: investigate this this.events.emit(this.context, 'worker:shutdown:complete');
281
+ this.events.emit('worker:shutdown:complete');
282
+ }
283
+ _sendSliceComplete(payload) {
284
+ return pWhile(async () => {
285
+ try {
286
+ await this.client.sendSliceComplete(payload);
287
+ return true;
288
+ }
289
+ catch (err) {
290
+ if (this.isShuttingDown) {
291
+ throw err;
292
+ }
293
+ else if (err.retryable === false) {
294
+ this.logger.warn(`${err}, will not retry.`);
295
+ return true;
296
+ }
297
+ else {
298
+ this.logger.warn(err);
299
+ }
300
+ }
301
+ return false;
302
+ });
303
+ }
304
+ _waitForSliceToFinish() {
305
+ if (!this.isProcessing)
306
+ return Promise.resolve();
307
+ const { logger } = this;
308
+ const startTime = Date.now();
309
+ return new Promise((resolve, reject) => {
310
+ let timeout;
311
+ let interval;
312
+ const done = (err) => {
313
+ clearInterval(interval);
314
+ clearTimeout(timeout);
315
+ if (err) {
316
+ reject(err);
317
+ return;
318
+ }
319
+ resolve(true);
320
+ };
321
+ interval = setInterval(() => {
322
+ const elapsed = (Date.now() - startTime) / 1000;
323
+ if (!this.isProcessing) {
324
+ logger.trace(`is done with current slice, shutdown counter took ${elapsed} seconds`);
325
+ done();
326
+ return;
327
+ }
328
+ /* istanbul ignore if */
329
+ if (elapsed % 60 === 0) {
330
+ logger.info(`shutdown sequence initiated, but is still processing. Will force shutdown in ${elapsed} seconds`);
331
+ }
332
+ }, 100);
333
+ timeout = setTimeout(() => {
334
+ const seconds = this.shutdownTimeout / 1000;
335
+ const err = new Error(`Worker shutdown timeout after ${seconds} seconds, forcing shutdown`);
336
+ done(err);
337
+ }, this.shutdownTimeout);
338
+ });
339
+ }
340
+ getSlicesProcessed() {
341
+ return this.slicesProcessed;
342
+ }
343
+ /**
344
+ * Adds all prom metrics specific to the worker.
345
+ *
346
+ * If trying to add a new metric for the worker, it belongs here.
347
+ * @async
348
+ * @function setupPromMetrics
349
+ * @return {Promise<void>}
350
+ * @link https://terascope.github.io/teraslice/docs/development/k8s#prometheus-metrics-api
351
+ */
352
+ async setupPromMetrics() {
353
+ if (isPromAvailable(this.context)) {
354
+ this.logger.info(`adding ${this.context.assignment} prom metrics...`);
355
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
356
+ const self = this;
357
+ await Promise.all([
358
+ this.context.apis.foundation.promMetrics.addGauge('worker_info', 'Information about Teraslice worker', ['arch', 'clustering_type', 'name', 'node_version', 'platform', 'teraslice_version']),
359
+ this.context.apis.foundation.promMetrics.addGauge('slices_processed', 'Number of slices the worker has processed', [], function collect() {
360
+ const slicesProcessed = self.getSlicesProcessed();
361
+ const defaultLabels = {
362
+ ...self.context.apis.foundation.promMetrics.getDefaultLabels()
363
+ };
364
+ this.set(defaultLabels, slicesProcessed);
365
+ })
366
+ ]);
367
+ this.context.apis.foundation.promMetrics.set('worker_info', {
368
+ arch: this.context.arch,
369
+ clustering_type: this.context.sysconfig.teraslice.cluster_manager_type,
370
+ name: this.context.sysconfig.teraslice.name,
371
+ node_version: process.version,
372
+ platform: this.context.platform,
373
+ teraslice_version: `v${getPackageJSON().version}`
374
+ }, 1);
375
+ }
376
+ }
377
+ }
378
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,122 @@
1
+ import { TSError, getTypeOf, logError } from '@terascope/utils';
2
+ import { SliceState } from '../../storage/index.js';
3
+ import { makeLogger } from '../helpers/terafoundation.js';
4
+ import { logOpStats } from '../helpers/op-analytics.js';
5
+ export class SliceExecution {
6
+ context;
7
+ executionContext;
8
+ stateStorage;
9
+ analyticsStorage;
10
+ events;
11
+ logger;
12
+ isShutdown;
13
+ slice;
14
+ analyticsData;
15
+ constructor(context, executionContext, stateStorage, analyticsStorage) {
16
+ this.context = context;
17
+ this.events = context.apis.foundation.getSystemEvents();
18
+ this.executionContext = executionContext;
19
+ this.stateStorage = stateStorage;
20
+ this.analyticsStorage = analyticsStorage;
21
+ }
22
+ async initialize(slice) {
23
+ const { slice_id: sliceId } = slice;
24
+ await this.stateStorage.updateState(slice, SliceState.start);
25
+ this.slice = slice;
26
+ this.logger = makeLogger(this.context, 'slice', {
27
+ slice_id: sliceId
28
+ });
29
+ await this.executionContext.initializeSlice(slice);
30
+ }
31
+ async run() {
32
+ if (this.isShutdown)
33
+ throw new Error('Slice is already shutdown');
34
+ const { slice } = this;
35
+ let result;
36
+ let sliceSuccess = false;
37
+ try {
38
+ result = await this.executionContext.runSlice();
39
+ sliceSuccess = true;
40
+ await this._markCompleted();
41
+ }
42
+ catch (err) {
43
+ const error = err || new Error(`Unknown slice error, got ${getTypeOf(err)} error`);
44
+ // avoid incorrectly marking
45
+ // the slice as failed when it fails
46
+ // to mark it as "complete"
47
+ if (!sliceSuccess) {
48
+ await this._markFailed(error);
49
+ }
50
+ throw error;
51
+ }
52
+ finally {
53
+ if (result) {
54
+ await this._logAnalytics(result);
55
+ }
56
+ await this._onSliceFinalize(slice);
57
+ }
58
+ return result.results;
59
+ }
60
+ async flush() {
61
+ const result = await this.executionContext.flush();
62
+ if (result) {
63
+ await this._markCompleted();
64
+ await this._logAnalytics(result);
65
+ await this._onSliceFinalize(this.slice);
66
+ }
67
+ }
68
+ async shutdown() {
69
+ this.isShutdown = true;
70
+ }
71
+ async _onSliceFinalize(slice) {
72
+ try {
73
+ await this.executionContext.onSliceFinalizing();
74
+ }
75
+ catch (err) {
76
+ this.logger.error(new TSError(err, {
77
+ reason: `Slice: ${slice.slice_id} failure on lifecycle event onSliceFinalizing`
78
+ }));
79
+ }
80
+ }
81
+ async _onSliceFailure(slice) {
82
+ try {
83
+ await this.executionContext.onSliceFailed();
84
+ }
85
+ catch (err) {
86
+ this.logger.error(new TSError(err, {
87
+ reason: `Slice: ${slice.slice_id} failure on lifecycle event onSliceFailed`
88
+ }));
89
+ }
90
+ }
91
+ async _logAnalytics(sliceResult) {
92
+ const { analytics, status } = sliceResult;
93
+ if (analytics == null)
94
+ return;
95
+ this.analyticsData = analytics;
96
+ logOpStats(this.logger, this.slice, this.analyticsData);
97
+ try {
98
+ await this.analyticsStorage.log(this.executionContext, this.slice, this.analyticsData, status);
99
+ }
100
+ catch (_err) {
101
+ this.logger.error(new TSError(_err, {
102
+ reason: 'Failure to update analytics'
103
+ }));
104
+ }
105
+ }
106
+ async _markCompleted() {
107
+ const { slice } = this;
108
+ await this.stateStorage.updateState(slice, SliceState.completed);
109
+ this.logger.trace(`completed slice for execution: ${this.executionContext.exId}`, slice);
110
+ this.events.emit('slice:success', slice);
111
+ }
112
+ async _markFailed(err) {
113
+ const { stateStorage, slice } = this;
114
+ await stateStorage.updateState(slice, SliceState.error, err);
115
+ logError(this.logger, err, `slice state for ${this.executionContext.exId} has been marked as error`);
116
+ await this._onSliceFailure(slice);
117
+ throw new TSError(err, {
118
+ reason: 'Slice failed processing'
119
+ });
120
+ }
121
+ }
122
+ //# sourceMappingURL=slice.js.map
@@ -0,0 +1,37 @@
1
+ import convict from 'convict';
2
+ // @ts-expect-error no types
3
+ import convict_format_with_validator from 'convict-format-with-validator';
4
+ // @ts-expect-error no types
5
+ import convict_format_with_moment from 'convict-format-with-moment';
6
+ import { formats } from '@terascope/job-components';
7
+ import { config_schema } from '../../../src/lib/config/schemas/system.js';
8
+ // load any convict schema
9
+ convict.addFormats(convict_format_with_validator);
10
+ convict.addFormats(convict_format_with_moment);
11
+ formats.forEach((format) => convict.addFormat(format));
12
+ describe('system_schema', () => {
13
+ const schema = config_schema().schema.teraslice;
14
+ function checkValidation(config) {
15
+ try {
16
+ const validator = convict(schema);
17
+ validator.load(config);
18
+ validator.validate();
19
+ return validator.getProperties();
20
+ }
21
+ catch (err) {
22
+ return err.message;
23
+ }
24
+ }
25
+ it('schema has defaults', () => {
26
+ expect(schema.shutdown_timeout).toBeDefined();
27
+ expect(schema.hostname).toBeDefined();
28
+ expect(schema).toBeDefined();
29
+ expect(schema.port.default).toEqual(5678);
30
+ expect(schema.name.default).toEqual('teracluster');
31
+ expect(schema.state.default).toEqual({ connection: 'default' });
32
+ });
33
+ it('assets_directory is optional but requires a string', () => {
34
+ expect(checkValidation({ assets_directory: 234 })).toEqual('assets_directory: Invalid parameter assets_directory, it must either be a string or an array of strings: value was 234');
35
+ });
36
+ });
37
+ //# sourceMappingURL=system_schema-spec.js.map