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.
- package/cluster-service.js +24 -18
- package/dist/src/index.js +42 -0
- package/package.json +10 -14
- package/service.js +4 -6
- package/worker-service.js +6 -6
- package/index.js +0 -21
- package/lib/cluster/cluster_master.js +0 -164
- package/lib/cluster/node_master.js +0 -393
- package/lib/cluster/services/api.js +0 -581
- package/lib/cluster/services/assets.js +0 -211
- package/lib/cluster/services/cluster/backends/kubernetes/deployments/worker.hbs +0 -86
- package/lib/cluster/services/cluster/backends/kubernetes/index.js +0 -225
- package/lib/cluster/services/cluster/backends/kubernetes/jobs/execution_controller.hbs +0 -69
- package/lib/cluster/services/cluster/backends/kubernetes/k8s.js +0 -450
- package/lib/cluster/services/cluster/backends/kubernetes/k8sResource.js +0 -443
- package/lib/cluster/services/cluster/backends/kubernetes/k8sState.js +0 -67
- package/lib/cluster/services/cluster/backends/kubernetes/utils.js +0 -58
- package/lib/cluster/services/cluster/backends/native/index.js +0 -611
- package/lib/cluster/services/cluster/backends/native/messaging.js +0 -563
- package/lib/cluster/services/cluster/backends/state-utils.js +0 -49
- package/lib/cluster/services/cluster/index.js +0 -15
- package/lib/cluster/services/execution.js +0 -459
- package/lib/cluster/services/jobs.js +0 -303
- package/lib/config/default-sysconfig.js +0 -47
- package/lib/config/index.js +0 -32
- package/lib/config/schemas/system.js +0 -333
- package/lib/processors/save_file/index.js +0 -9
- package/lib/processors/save_file/processor.js +0 -17
- package/lib/processors/save_file/schema.js +0 -17
- package/lib/processors/script.js +0 -130
- package/lib/processors/stdout/index.js +0 -9
- package/lib/processors/stdout/processor.js +0 -19
- package/lib/processors/stdout/schema.js +0 -18
- package/lib/storage/analytics.js +0 -106
- package/lib/storage/assets.js +0 -275
- package/lib/storage/backends/elasticsearch_store.js +0 -567
- package/lib/storage/backends/mappings/analytics.json +0 -49
- package/lib/storage/backends/mappings/asset.json +0 -40
- package/lib/storage/backends/mappings/ex.json +0 -55
- package/lib/storage/backends/mappings/job.json +0 -31
- package/lib/storage/backends/mappings/state.json +0 -37
- package/lib/storage/execution.js +0 -331
- package/lib/storage/index.js +0 -16
- package/lib/storage/jobs.js +0 -97
- package/lib/storage/state.js +0 -302
- package/lib/utils/api_utils.js +0 -173
- package/lib/utils/asset_utils.js +0 -117
- package/lib/utils/date_utils.js +0 -58
- package/lib/utils/encoding_utils.js +0 -29
- package/lib/utils/events.js +0 -7
- package/lib/utils/file_utils.js +0 -118
- package/lib/utils/id_utils.js +0 -19
- package/lib/utils/port_utils.js +0 -83
- package/lib/workers/assets/loader.js +0 -109
- package/lib/workers/assets/spawn.js +0 -78
- package/lib/workers/context/execution-context.js +0 -16
- package/lib/workers/context/terafoundation-context.js +0 -10
- package/lib/workers/execution-controller/execution-analytics.js +0 -211
- package/lib/workers/execution-controller/index.js +0 -1033
- package/lib/workers/execution-controller/recovery.js +0 -188
- package/lib/workers/execution-controller/scheduler.js +0 -461
- package/lib/workers/execution-controller/slice-analytics.js +0 -115
- package/lib/workers/helpers/job.js +0 -93
- package/lib/workers/helpers/op-analytics.js +0 -22
- package/lib/workers/helpers/terafoundation.js +0 -43
- package/lib/workers/helpers/worker-shutdown.js +0 -187
- package/lib/workers/metrics/index.js +0 -139
- package/lib/workers/worker/index.js +0 -344
- 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
|
-
}
|