teraslice 0.87.1 → 0.89.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 (69) hide show
  1. package/cluster-service.js +24 -18
  2. package/dist/src/index.js +42 -0
  3. package/package.json +10 -14
  4. package/service.js +4 -6
  5. package/worker-service.js +6 -6
  6. package/index.js +0 -21
  7. package/lib/cluster/cluster_master.js +0 -164
  8. package/lib/cluster/node_master.js +0 -393
  9. package/lib/cluster/services/api.js +0 -581
  10. package/lib/cluster/services/assets.js +0 -211
  11. package/lib/cluster/services/cluster/backends/kubernetes/deployments/worker.hbs +0 -86
  12. package/lib/cluster/services/cluster/backends/kubernetes/index.js +0 -225
  13. package/lib/cluster/services/cluster/backends/kubernetes/jobs/execution_controller.hbs +0 -69
  14. package/lib/cluster/services/cluster/backends/kubernetes/k8s.js +0 -450
  15. package/lib/cluster/services/cluster/backends/kubernetes/k8sResource.js +0 -443
  16. package/lib/cluster/services/cluster/backends/kubernetes/k8sState.js +0 -67
  17. package/lib/cluster/services/cluster/backends/kubernetes/utils.js +0 -58
  18. package/lib/cluster/services/cluster/backends/native/index.js +0 -611
  19. package/lib/cluster/services/cluster/backends/native/messaging.js +0 -563
  20. package/lib/cluster/services/cluster/backends/state-utils.js +0 -49
  21. package/lib/cluster/services/cluster/index.js +0 -15
  22. package/lib/cluster/services/execution.js +0 -459
  23. package/lib/cluster/services/jobs.js +0 -303
  24. package/lib/config/default-sysconfig.js +0 -47
  25. package/lib/config/index.js +0 -32
  26. package/lib/config/schemas/system.js +0 -333
  27. package/lib/processors/save_file/index.js +0 -9
  28. package/lib/processors/save_file/processor.js +0 -17
  29. package/lib/processors/save_file/schema.js +0 -17
  30. package/lib/processors/script.js +0 -130
  31. package/lib/processors/stdout/index.js +0 -9
  32. package/lib/processors/stdout/processor.js +0 -19
  33. package/lib/processors/stdout/schema.js +0 -18
  34. package/lib/storage/analytics.js +0 -106
  35. package/lib/storage/assets.js +0 -275
  36. package/lib/storage/backends/elasticsearch_store.js +0 -567
  37. package/lib/storage/backends/mappings/analytics.json +0 -49
  38. package/lib/storage/backends/mappings/asset.json +0 -40
  39. package/lib/storage/backends/mappings/ex.json +0 -55
  40. package/lib/storage/backends/mappings/job.json +0 -31
  41. package/lib/storage/backends/mappings/state.json +0 -37
  42. package/lib/storage/execution.js +0 -331
  43. package/lib/storage/index.js +0 -16
  44. package/lib/storage/jobs.js +0 -97
  45. package/lib/storage/state.js +0 -302
  46. package/lib/utils/api_utils.js +0 -173
  47. package/lib/utils/asset_utils.js +0 -117
  48. package/lib/utils/date_utils.js +0 -58
  49. package/lib/utils/encoding_utils.js +0 -29
  50. package/lib/utils/events.js +0 -7
  51. package/lib/utils/file_utils.js +0 -118
  52. package/lib/utils/id_utils.js +0 -19
  53. package/lib/utils/port_utils.js +0 -83
  54. package/lib/workers/assets/loader.js +0 -109
  55. package/lib/workers/assets/spawn.js +0 -78
  56. package/lib/workers/context/execution-context.js +0 -16
  57. package/lib/workers/context/terafoundation-context.js +0 -10
  58. package/lib/workers/execution-controller/execution-analytics.js +0 -211
  59. package/lib/workers/execution-controller/index.js +0 -1033
  60. package/lib/workers/execution-controller/recovery.js +0 -188
  61. package/lib/workers/execution-controller/scheduler.js +0 -461
  62. package/lib/workers/execution-controller/slice-analytics.js +0 -115
  63. package/lib/workers/helpers/job.js +0 -93
  64. package/lib/workers/helpers/op-analytics.js +0 -22
  65. package/lib/workers/helpers/terafoundation.js +0 -43
  66. package/lib/workers/helpers/worker-shutdown.js +0 -187
  67. package/lib/workers/metrics/index.js +0 -139
  68. package/lib/workers/worker/index.js +0 -344
  69. package/lib/workers/worker/slice.js +0 -143
@@ -1,115 +0,0 @@
1
- 'use strict';
2
-
3
- const _ = require('lodash');
4
- const { makeLogger } = require('../helpers/terafoundation');
5
-
6
- module.exports = function _sliceAnalytics(context, executionContext) {
7
- const logger = makeLogger(context, 'slice_analytics');
8
-
9
- const events = context.apis.foundation.getSystemEvents();
10
-
11
- const { operations } = executionContext.config;
12
-
13
- // create a container to hold all the slice analytics for this execution
14
- const sliceAnalytics = {
15
- time: [],
16
- size: [],
17
- memory: []
18
- };
19
-
20
- for (let i = 0; i < operations.length; i += 1) {
21
- sliceAnalytics.time.push({
22
- min: 0,
23
- max: 0,
24
- sum: 0,
25
- total: 0,
26
- average: 0
27
- });
28
- sliceAnalytics.size.push({
29
- min: 0,
30
- max: 0,
31
- sum: 0,
32
- total: 0,
33
- average: 0
34
- });
35
- sliceAnalytics.memory.push({
36
- min: 0,
37
- max: 0,
38
- sum: 0,
39
- total: 0,
40
- average: 0
41
- });
42
- }
43
-
44
- function addStat(input, stat) {
45
- if (!_.has(input, stat) || !_.has(sliceAnalytics, stat)) {
46
- logger.warn(`unsupported stat "${stat}"`);
47
- return;
48
- }
49
-
50
- for (let i = 0; i < operations.length; i += 1) {
51
- const val = input[stat][i];
52
- if (!_.isSafeInteger(val)) {
53
- return;
54
- }
55
-
56
- sliceAnalytics[stat][i].sum += val;
57
- sliceAnalytics[stat][i].total += 1;
58
-
59
- const {
60
- min, max, total, sum
61
- } = sliceAnalytics[stat][i];
62
-
63
- sliceAnalytics[stat][i].min = min !== 0 ? _.min([val, min]) : val;
64
- sliceAnalytics[stat][i].max = max !== 0 ? _.max([val, max]) : val;
65
- sliceAnalytics[stat][i].average = _.round(sum / total, 2);
66
- }
67
- }
68
-
69
- function addStats(data) {
70
- addStat(data, 'time');
71
- addStat(data, 'memory');
72
- addStat(data, 'size');
73
- }
74
-
75
- function analyzeStats() {
76
- logger.info('calculating statistics');
77
-
78
- for (let i = 0; i < operations.length; i += 1) {
79
- const name = operations[i]._op;
80
- const time = sliceAnalytics.time[i];
81
- const size = sliceAnalytics.size[i];
82
- const memory = sliceAnalytics.memory[i];
83
-
84
- logger.info(`
85
- operation ${name}
86
- average completion time of: ${time.average} ms, min: ${time.min} ms, and max: ${time.max} ms
87
- average size: ${size.average}, min: ${size.min}, and max: ${size.max}
88
- average memory: ${memory.average}, min: ${memory.min}, and max: ${memory.max}
89
- `);
90
- }
91
- }
92
-
93
- function getStats() {
94
- return sliceAnalytics;
95
- }
96
-
97
- function onSliceSuccess({ analytics }) {
98
- if (analytics) {
99
- addStats(analytics);
100
- }
101
- }
102
-
103
- events.on('slice:success', onSliceSuccess);
104
-
105
- async function shutdown() {
106
- events.removeListener('slice:success', onSliceSuccess);
107
- }
108
-
109
- return {
110
- addStats,
111
- analyzeStats,
112
- getStats,
113
- shutdown
114
- };
115
- };
@@ -1,93 +0,0 @@
1
- 'use strict';
2
-
3
- const { get, makeISODate } = require('@terascope/utils');
4
- const { JobValidator } = require('@terascope/job-components');
5
- const { terasliceOpPath } = require('../../config');
6
- const { makeJobStore, makeExStore, makeStateStore } = require('../../storage');
7
-
8
- async function validateJob(context, jobSpec) {
9
- const jobValidator = new JobValidator(context, {
10
- terasliceOpPath,
11
- assetPath: get(context, 'sysconfig.teraslice.assets_directory'),
12
- });
13
-
14
- try {
15
- return await jobValidator.validateConfig(jobSpec);
16
- } catch (error) {
17
- throw new Error(`validating job: ${error}`);
18
- }
19
- }
20
-
21
- async function initializeTestExecution({
22
- context,
23
- config,
24
- stores = {},
25
- isRecovery,
26
- cleanupType,
27
- createRecovery = true,
28
- shutdownStores = false,
29
- recoverySlices = [],
30
- lastStatus = 'failed'
31
- }) {
32
- stores.jobStore = stores.jobStore || (await makeJobStore(context));
33
- stores.exStore = stores.exStore || (await makeExStore(context));
34
- stores.stateStore = stores.stateStore || (await makeStateStore(context));
35
-
36
- const validJob = await validateJob(context, config, { skipRegister: true });
37
- const jobSpec = await stores.jobStore.create(config);
38
-
39
- const job = Object.assign({}, jobSpec, validJob, {
40
- job_id: jobSpec.job_id
41
- });
42
-
43
- const slicerHostname = job.slicer_hostname;
44
- const slicerPort = job.slicer_port;
45
-
46
- let ex;
47
- if (isRecovery) {
48
- ex = await stores.exStore.create(job, lastStatus);
49
-
50
- if (recoverySlices.length) {
51
- await Promise.all(recoverySlices
52
- .map(({ slice, state }) => stores.stateStore.createState(
53
- ex.ex_id,
54
- slice,
55
- state,
56
- slice.error
57
- )));
58
- await stores.stateStore.refresh();
59
- }
60
-
61
- if (createRecovery) {
62
- ex = await stores.exStore.createRecoveredExecution(ex, cleanupType);
63
- }
64
- } else {
65
- ex = await stores.exStore.create(job);
66
- }
67
-
68
- if (slicerHostname && slicerPort) {
69
- ex = await stores.exStore.updatePartial(ex.ex_id, (existing) => Object.assign(existing, {
70
- slicer_hostname: slicerHostname,
71
- slicer_port: slicerPort,
72
- _updated: makeISODate()
73
- }));
74
- }
75
-
76
- if (shutdownStores) {
77
- await Promise.all([
78
- stores.exStore.shutdown(true),
79
- stores.jobStore.shutdown(true),
80
- stores.stateStore.shutdown(true)
81
- ]);
82
- }
83
-
84
- return {
85
- job,
86
- ex,
87
- };
88
- }
89
-
90
- module.exports = {
91
- initializeTestExecution,
92
- validateJob,
93
- };
@@ -1,22 +0,0 @@
1
- 'use strict';
2
-
3
- const _ = require('lodash');
4
-
5
- function formatVal(value) {
6
- if (_.isString(value)) return `"${value}"`;
7
- if (_.isArray(value)) return `[${value.join(', ')}]`;
8
-
9
- return _.truncate(JSON.stringify(value));
10
- }
11
-
12
- function format(input) {
13
- return _.map(input, (value, key) => `${key}: ${formatVal(value)}`).join(', ');
14
- }
15
-
16
- function logOpStats(logger, slice, analyticsData) {
17
- const obj = Object.assign({}, _.omit(slice, 'request'), analyticsData);
18
-
19
- logger.info(`analytics for slice: ${format(obj)}`);
20
- }
21
-
22
- module.exports = { logOpStats };
@@ -1,43 +0,0 @@
1
- 'use strict';
2
-
3
- const { get, isFunction, isString } = require('@terascope/utils');
4
- const { makeContextLogger } = require('@terascope/job-components');
5
- const { safeDecode } = require('../../utils/encoding_utils');
6
-
7
- function generateWorkerId(context) {
8
- const { hostname } = context.sysconfig.teraslice;
9
- const clusterId = get(context, 'cluster.worker.id');
10
- return `${hostname}__${clusterId}`;
11
- }
12
-
13
- function makeLogger(context, moduleName, extra = {}) {
14
- if (!context || !context.apis) {
15
- throw new Error('makeLogger expected terafoundation context as first arg');
16
- }
17
-
18
- if (!moduleName || !isString(moduleName)) {
19
- throw new Error('makeLogger expected module name as second arg');
20
- }
21
-
22
- const exAPI = context.apis.executionContext;
23
- if (exAPI && isFunction(exAPI.makeLogger)) {
24
- return exAPI.makeLogger(moduleName, extra);
25
- }
26
-
27
- const defaultContext = {};
28
- if (process.env.EX) {
29
- const ex = safeDecode(process.env.EX);
30
- const exId = get(ex, 'ex_id');
31
- const jobId = get(ex, 'job_id');
32
- if (exId) {
33
- defaultContext.ex_id = exId;
34
- }
35
- if (jobId) {
36
- defaultContext.job_id = jobId;
37
- }
38
- }
39
-
40
- return makeContextLogger(context, moduleName, Object.assign(defaultContext, extra));
41
- }
42
-
43
- module.exports = { generateWorkerId, makeLogger };
@@ -1,187 +0,0 @@
1
- 'use strict';
2
-
3
- const {
4
- get, pDelay, pRaceWithTimeout, isError, logError
5
- } = require('@terascope/utils');
6
- const ms = require('ms');
7
- const { makeLogger } = require('./terafoundation');
8
-
9
- function waitForWorkerShutdown(context, eventName) {
10
- const shutdownTimeout = get(context, 'sysconfig.teraslice.shutdown_timeout', 30000);
11
- const events = context.apis.foundation.getSystemEvents();
12
-
13
- return new Promise((resolve, reject) => {
14
- const timeoutId = setTimeout(() => {
15
- events.removeListener(eventName, handler);
16
- reject(new Error('Timeout waiting for worker to shutdown'));
17
- }, shutdownTimeout);
18
-
19
- function handler(err) {
20
- clearTimeout(timeoutId);
21
- if (isError(err)) {
22
- reject(err);
23
- } else {
24
- resolve();
25
- }
26
- }
27
- events.once(eventName, handler);
28
- });
29
- }
30
-
31
- /* istanbul ignore next */
32
- function shutdownHandler(context, shutdownFn) {
33
- const assignment = context.assignment
34
- || process.env.NODE_TYPE
35
- || process.env.assignment
36
- || 'unknown-assignment';
37
-
38
- const isK8s = get(context, 'sysconfig.teraslice.cluster_manager_type') === 'kubernetes';
39
- // this is native clustering only
40
- const isProcessRestart = process.env.process_restart;
41
- // everything but the k8s execution_controller should not be allowed be allowed to
42
- // set a non-zero exit code (to avoid being restarted)
43
- const allowNonZeroExitCode = !(isK8s && assignment === 'execution_controller');
44
- const api = {
45
- exiting: false,
46
- exit
47
- };
48
-
49
- const shutdownTimeout = get(context, 'sysconfig.teraslice.shutdown_timeout', 20 * 1000);
50
-
51
- const events = context.apis.foundation.getSystemEvents();
52
- const logger = makeLogger(context, `${assignment}:shutdown_handler`);
53
-
54
- if (assignment === 'execution_controller' && isProcessRestart) {
55
- logger.fatal(
56
- 'Execution Controller runtime error led to a restart, terminating execution with failed status, please use the recover api to return slicer to a consistent state'
57
- );
58
- process.exit(0);
59
- }
60
-
61
- async function flushLogs() {
62
- try {
63
- await logger.flush();
64
- await pDelay(1000);
65
-
66
- const code = process.exitCode || 0;
67
- logger.debug(`flushed logs successfully, will exit with code ${code}`);
68
- } catch (err) {
69
- logError(logger, err, 'flush error on shutdown');
70
- }
71
- }
72
-
73
- let startTime;
74
-
75
- function exitingIn() {
76
- if (!api.exiting) {
77
- return `exiting in ${ms(shutdownTimeout)}...`;
78
- }
79
-
80
- const elapsed = Date.now() - startTime;
81
- return `already shutting down, remaining ${ms(shutdownTimeout - elapsed)}`;
82
- }
83
-
84
- async function callShutdownFn(event, err) {
85
- // avoid failing before the promse is try / catched in pRaceWithTimeout
86
- await pDelay(100);
87
- await shutdownFn(event, err);
88
- }
89
-
90
- async function shutdownWithTimeout(event, err) {
91
- const timeout = shutdownTimeout - 2000;
92
- await pRaceWithTimeout(callShutdownFn(event, err), timeout, (timeoutErr) => {
93
- logError(logger, timeoutErr, 'shutdown error after timeout');
94
- });
95
- }
96
-
97
- async function exit(event, err) {
98
- if (api.exiting) return;
99
-
100
- api.exiting = true;
101
- startTime = Date.now();
102
-
103
- try {
104
- await shutdownWithTimeout(event, err);
105
- } catch (error) {
106
- logError(logger, error, `${assignment} while shutting down`);
107
- } finally {
108
- await flushLogs();
109
- if (allowNonZeroExitCode) {
110
- const code = process.exitCode != null ? process.exitCode : 0;
111
- logger.info(`${assignment} shutdown took ${ms(Date.now() - startTime)}, exit with ${code} status code`);
112
- process.exit();
113
- } else {
114
- logger.info(`${assignment} shutdown took ${ms(Date.now() - startTime)}, exit with zero status code`);
115
- process.exit(0);
116
- }
117
- }
118
- }
119
-
120
- function setStatusCode(code) {
121
- if (api.exiting) return;
122
- if (process.exitCode == null) {
123
- process.exitCode = code;
124
- }
125
- }
126
-
127
- process.on('SIGINT', () => {
128
- logger.info(`${assignment} received process:SIGINT, ${exitingIn()}`);
129
- setStatusCode(0);
130
- exit('SIGINT');
131
- });
132
-
133
- process.on('SIGTERM', () => {
134
- logger.info(`${assignment} received process:SIGTERM, ${exitingIn()}`);
135
- setStatusCode(0);
136
- exit('SIGTERM');
137
- });
138
-
139
- process.on('uncaughtException', (err) => {
140
- logError(logger, err, `${assignment} received an uncaughtException, ${exitingIn()}`);
141
- setStatusCode(1);
142
- exit('uncaughtException', err);
143
- });
144
-
145
- process.once('unhandledRejection', (err) => {
146
- logError(logger, err, `${assignment} received an unhandledRejection, ${exitingIn()}`);
147
- setStatusCode(1);
148
- exit('unhandledRejection', err);
149
- });
150
-
151
- // See https://github.com/trentm/node-bunyan/issues/246
152
- function handleStdError(err) {
153
- if (err.code === 'EPIPE' || err.code === 'ERR_STREAM_DESTROYED') {
154
- // ignore
155
- } else {
156
- throw err;
157
- }
158
- }
159
-
160
- process.stdout.on('error', handleStdError);
161
- process.stderr.on('error', handleStdError);
162
-
163
- // event is fired from terafoundation when an error occurs during instantiation of a client
164
- // **DEPRECATED:** This handler should be removed on teraslice v1
165
- events.once('client:initialization:error', (err) => {
166
- logError(logger, err, `${assignment} received a client initialization error, ${exitingIn()}`);
167
- setStatusCode(1);
168
- exit('client:initialization:error', err);
169
- });
170
-
171
- events.once('worker:shutdown:complete', (err) => {
172
- setStatusCode(0);
173
- if (err) {
174
- logError(logger, err, `${assignment} shutdown error, ${exitingIn()}`);
175
- } else {
176
- logger.info(`${assignment} shutdown, ${exitingIn()}`);
177
- }
178
- exit('worker:shutdown:complete', err);
179
- });
180
-
181
- return api;
182
- }
183
-
184
- module.exports = {
185
- shutdownHandler,
186
- waitForWorkerShutdown
187
- };
@@ -1,139 +0,0 @@
1
- /* eslint-disable no-console */
2
-
3
- 'use strict';
4
-
5
- const { EventEmitter } = require('events');
6
- const { pDelay, debugLogger, isTest } = require('@terascope/utils');
7
-
8
- const _logger = debugLogger('metrics');
9
-
10
- class Metrics extends EventEmitter {
11
- constructor({ logger } = {}) {
12
- super();
13
-
14
- this._logger = logger
15
- ? logger.child({
16
- module: 'performance:metrics'
17
- })
18
- : _logger;
19
-
20
- this.eventLoopInterval = isTest ? 100 : 5000;
21
- this._intervals = [];
22
- this._typesCollectedAt = {};
23
-
24
- // never cause an unwanted error
25
- try {
26
- this.gcStats = require('gc-stats')();
27
- } catch (err) {
28
- this._logger.error(err, 'Failure to construct gc-stats');
29
- }
30
-
31
- try {
32
- this.eventLoopStats = require('event-loop-stats');
33
- } catch (err) {
34
- this._logger.error(err, 'Failure to construct event-loop-stats');
35
- }
36
- }
37
-
38
- async initialize() {
39
- const gcEnabled = this.gcStats != null;
40
- const loopEnabled = this.eventLoopStats != null;
41
-
42
- this._logger.info('initializing performance metrics', {
43
- gcEnabled,
44
- loopEnabled
45
- });
46
-
47
- if (gcEnabled) {
48
- // https://github.com/dainis/node-gcstats#property-insights
49
- const typesToName = {
50
- 1: 'Scavenge',
51
- 2: 'MarkSweepCompact',
52
- 4: 'IncrementalMarking',
53
- 8: 'WeakPhantomCallbackProcessing',
54
- 15: 'All'
55
- };
56
-
57
- this.gcStats.on('stats', (metrics) => {
58
- // never cause an unwanted error
59
- if (!metrics) {
60
- this._logger.warn('invalid metrics received for gc stats', metrics);
61
- return;
62
- }
63
-
64
- const typeName = typesToName[metrics.gctype];
65
- this._emitMetric('gc-stats', Object.assign({ typeName }, metrics));
66
- });
67
- }
68
-
69
- if (loopEnabled) {
70
- // https://github.com/bripkens/event-loop-stats#property-insights
71
- this._intervals.push(
72
- setInterval(() => {
73
- // never cause an unwanted error
74
- let metrics;
75
- try {
76
- metrics = this.eventLoopStats.sense();
77
- } catch (err) {
78
- this._logger.error(err, 'failure getting for event-loop-stats');
79
- return;
80
- }
81
-
82
- if (!metrics) {
83
- this._logger.warn('invalid metrics received for event-loop-stats', metrics);
84
- return;
85
- }
86
-
87
- this._emitMetric('event-loop-stats', metrics);
88
- }, this.eventLoopInterval)
89
- );
90
- }
91
- }
92
-
93
- async shutdown() {
94
- try {
95
- this._intervals.forEach(clearInterval);
96
- this._intervals = [];
97
- this.removeAllListeners();
98
- this._typesCollectedAt = {};
99
- } catch (err) {
100
- this.logger.debug(err, 'failure shutting down metrics');
101
- }
102
- }
103
-
104
- _emitMetric(type, metrics) {
105
- const lastCollectedAt = this._typesCollectedAt[type] || Date.now();
106
- const msg = {
107
- type,
108
- timeSinceLast: Date.now() - lastCollectedAt,
109
- metrics
110
- };
111
- this._typesCollectedAt[type] = Date.now();
112
- this.emit('metric', msg);
113
- this._logger.info(msg, `${type} performance metrics`);
114
- }
115
- }
116
-
117
- if (require.main === module) {
118
- (async () => {
119
- const metrics = new Metrics();
120
- metrics.on('metric', (metric) => {
121
- console.dir(metric);
122
- });
123
-
124
- try {
125
- await metrics.initialize();
126
- console.log('staying alive for 30s...');
127
- await pDelay(30000);
128
- console.log('done, exiting...');
129
- } catch (err) {
130
- console.error(err);
131
- process.exitCode = 1;
132
- } finally {
133
- await metrics.shutdown();
134
- process.exit();
135
- }
136
- })();
137
- } else {
138
- module.exports = Metrics;
139
- }