teraslice 0.87.0 → 0.88.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 +11 -15
- 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,188 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
pRaceWithTimeout, logError, cloneDeep, Queue
|
|
5
|
-
} = require('@terascope/utils');
|
|
6
|
-
const { makeLogger } = require('../helpers/terafoundation');
|
|
7
|
-
|
|
8
|
-
function recoveryModule(context, stateStore, executionContext) {
|
|
9
|
-
const events = context.apis.foundation.getSystemEvents();
|
|
10
|
-
const slicersToRecover = executionContext.config.slicers;
|
|
11
|
-
const recoveryQueue = new Queue();
|
|
12
|
-
|
|
13
|
-
const cleanupType = executionContext.config.recovered_slice_type;
|
|
14
|
-
const recoverExecution = executionContext.config.recovered_execution;
|
|
15
|
-
const autorecover = Boolean(executionContext.config.autorecover);
|
|
16
|
-
const { exId } = executionContext;
|
|
17
|
-
|
|
18
|
-
let recoverComplete = true;
|
|
19
|
-
let isShutdown = false;
|
|
20
|
-
|
|
21
|
-
const logger = makeLogger(context, 'execution_recovery');
|
|
22
|
-
|
|
23
|
-
const retryState = {};
|
|
24
|
-
|
|
25
|
-
function initialize() {
|
|
26
|
-
events.on('slice:success', _sliceComplete);
|
|
27
|
-
recoverComplete = false;
|
|
28
|
-
|
|
29
|
-
// once we have fully recovered, clean up event listners
|
|
30
|
-
events.once('execution:recovery:complete', () => {
|
|
31
|
-
events.removeListener('slice:success', _sliceComplete);
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function _sliceComplete(sliceData) {
|
|
36
|
-
retryState[sliceData.slice.slice_id] = false;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function _setId(slice) {
|
|
40
|
-
retryState[slice.slice_id] = true;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function _processIncompleteSlices(slicerID) {
|
|
44
|
-
const slices = await stateStore.recoverSlices(recoverExecution, slicerID, cleanupType);
|
|
45
|
-
slices.forEach((slice) => {
|
|
46
|
-
_setId(slice);
|
|
47
|
-
recoveryQueue.enqueue(slice);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
return slices.length;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function _recoveryBatchCompleted() {
|
|
54
|
-
return Object.values(retryState).every((v) => v === false);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function _retryState() {
|
|
58
|
-
return cloneDeep(retryState);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function _waitForRecoveryBatchCompletion() {
|
|
62
|
-
return new Promise((resolve) => {
|
|
63
|
-
const checkingBatch = setInterval(() => {
|
|
64
|
-
if (_recoveryBatchCompleted()) {
|
|
65
|
-
clearInterval(checkingBatch);
|
|
66
|
-
events.emit('execution:recovery:complete');
|
|
67
|
-
recoverComplete = true;
|
|
68
|
-
resolve();
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (isShutdown) {
|
|
73
|
-
clearInterval(checkingBatch);
|
|
74
|
-
recoverComplete = false;
|
|
75
|
-
resolve();
|
|
76
|
-
}
|
|
77
|
-
}, 100);
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
let slicerID = 0;
|
|
82
|
-
|
|
83
|
-
async function handle() {
|
|
84
|
-
if (recoverComplete) return true;
|
|
85
|
-
|
|
86
|
-
const recoveredSlicesCount = await _processIncompleteSlices(slicerID);
|
|
87
|
-
if (recoveredSlicesCount === 0) {
|
|
88
|
-
slicerID++;
|
|
89
|
-
// all slicers have been recovered
|
|
90
|
-
if (slicerID > slicersToRecover) {
|
|
91
|
-
logger.warn(`recovered data for execution: ${exId} has successfully been enqueued`);
|
|
92
|
-
await _waitForRecoveryBatchCompletion();
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
await _waitForRecoveryBatchCompletion();
|
|
98
|
-
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function getSlice() {
|
|
103
|
-
if (recoveryQueue.size() > 0) {
|
|
104
|
-
return recoveryQueue.dequeue();
|
|
105
|
-
}
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function getSlices(max = 1) {
|
|
110
|
-
const count = max > sliceCount() ? sliceCount() : max;
|
|
111
|
-
|
|
112
|
-
const slices = [];
|
|
113
|
-
|
|
114
|
-
for (let i = 0; i < count; i++) {
|
|
115
|
-
const slice = recoveryQueue.dequeue();
|
|
116
|
-
if (!slice) return slices;
|
|
117
|
-
|
|
118
|
-
slices.push(slice);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return slices;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function sliceCount() {
|
|
125
|
-
return recoveryQueue.size();
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
async function shutdown() {
|
|
129
|
-
let checkInterval;
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
await pRaceWithTimeout(
|
|
133
|
-
new Promise((resolve) => {
|
|
134
|
-
checkInterval = setInterval(() => {
|
|
135
|
-
if (recoverComplete) {
|
|
136
|
-
resolve();
|
|
137
|
-
}
|
|
138
|
-
}, 100);
|
|
139
|
-
}),
|
|
140
|
-
context.sysconfig.teraslice.shutdown_timeout,
|
|
141
|
-
(err) => { logError(logger, err); }
|
|
142
|
-
);
|
|
143
|
-
} finally {
|
|
144
|
-
isShutdown = true;
|
|
145
|
-
clearInterval(checkInterval);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function recoveryComplete() {
|
|
150
|
-
return recoverComplete;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Whether or not the execution will continue to process
|
|
155
|
-
* slices after recovering.
|
|
156
|
-
*
|
|
157
|
-
* @returns {boolean}
|
|
158
|
-
*/
|
|
159
|
-
function exitAfterComplete() {
|
|
160
|
-
if (autorecover) return false;
|
|
161
|
-
if (!cleanupType) return false;
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function testContext() {
|
|
166
|
-
return {
|
|
167
|
-
_retryState,
|
|
168
|
-
_recoveryBatchCompleted,
|
|
169
|
-
_setId,
|
|
170
|
-
_waitForRecoveryBatchCompletion,
|
|
171
|
-
_sliceComplete
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
initialize,
|
|
177
|
-
getSlice,
|
|
178
|
-
getSlices,
|
|
179
|
-
sliceCount,
|
|
180
|
-
exitAfterComplete,
|
|
181
|
-
recoveryComplete,
|
|
182
|
-
handle,
|
|
183
|
-
shutdown,
|
|
184
|
-
__test_context: testContext
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
module.exports = recoveryModule;
|
|
@@ -1,461 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
Queue, noop, pDelay, get, toString, makeISODate, logError, pWhile
|
|
5
|
-
} = require('@terascope/utils');
|
|
6
|
-
const makeExecutionRecovery = require('./recovery');
|
|
7
|
-
const { makeLogger } = require('../helpers/terafoundation');
|
|
8
|
-
|
|
9
|
-
class Scheduler {
|
|
10
|
-
constructor(context, executionContext) {
|
|
11
|
-
this.context = context;
|
|
12
|
-
this.logger = makeLogger(context, 'execution_scheduler');
|
|
13
|
-
this.events = context.apis.foundation.getSystemEvents();
|
|
14
|
-
this.executionContext = executionContext;
|
|
15
|
-
this.exId = executionContext.exId;
|
|
16
|
-
|
|
17
|
-
const recoverFromExId = get(executionContext.config, 'recovered_execution');
|
|
18
|
-
const slicerCanRecover = executionContext.slicer().isRecoverable();
|
|
19
|
-
if (recoverFromExId && !slicerCanRecover) {
|
|
20
|
-
throw new Error('Slicer is not recoverable');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
this.recoverFromExId = recoverFromExId;
|
|
24
|
-
this.recoverExecution = Boolean(recoverFromExId && slicerCanRecover);
|
|
25
|
-
this.recovering = Boolean(this.recoverExecution);
|
|
26
|
-
this.autorecover = Boolean(executionContext.config.autorecover);
|
|
27
|
-
|
|
28
|
-
this._creating = 0;
|
|
29
|
-
this.ready = false;
|
|
30
|
-
this.paused = true;
|
|
31
|
-
this.stopped = false;
|
|
32
|
-
this.slicersDone = false;
|
|
33
|
-
this.slicersFailed = false;
|
|
34
|
-
this.queue = new Queue();
|
|
35
|
-
this.startingPoints = [];
|
|
36
|
-
|
|
37
|
-
this._resolveRun = noop;
|
|
38
|
-
this._processCleanup = noop;
|
|
39
|
-
|
|
40
|
-
this._processSlicers();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Initialize the recovery instance or the execution context,
|
|
45
|
-
* if recovery is initialized, the execution context will not be
|
|
46
|
-
* initialized until the execution if finished and the cleanup
|
|
47
|
-
* type is set.
|
|
48
|
-
*/
|
|
49
|
-
async initialize() {
|
|
50
|
-
if (this.recoverExecution) {
|
|
51
|
-
await this._initializeRecovery();
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
await this._initializeExecution();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Run the execution, this will block until complete (or failed)
|
|
60
|
-
*/
|
|
61
|
-
async run() {
|
|
62
|
-
if (this.recoverExecution) {
|
|
63
|
-
await this.exStore.setStatus(this.exId, 'recovering');
|
|
64
|
-
|
|
65
|
-
this.logger.info(`execution: ${this.exId} is starting in recovery mode`);
|
|
66
|
-
this.ready = true;
|
|
67
|
-
this.start();
|
|
68
|
-
|
|
69
|
-
await this._waitForRecovery();
|
|
70
|
-
await this._recoveryComplete();
|
|
71
|
-
|
|
72
|
-
if (this.recover.exitAfterComplete()) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
await this._initializeExecution();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
await this.exStore.setStatus(this.exId, 'running');
|
|
80
|
-
this.ready = true;
|
|
81
|
-
|
|
82
|
-
const promise = new Promise((resolve) => {
|
|
83
|
-
const handler = (err) => {
|
|
84
|
-
if (err) {
|
|
85
|
-
this.slicersFailed = true;
|
|
86
|
-
} else {
|
|
87
|
-
this.slicersDone = true;
|
|
88
|
-
}
|
|
89
|
-
this._resolveRun();
|
|
90
|
-
this._resolveRun = noop;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
this._resolveRun = () => {
|
|
94
|
-
this.events.removeListener('slicers:finished', handler);
|
|
95
|
-
resolve();
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
this.events.once('slicers:finished', handler);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
this.start();
|
|
102
|
-
|
|
103
|
-
this.logger.trace('running the scheduler');
|
|
104
|
-
|
|
105
|
-
await promise;
|
|
106
|
-
|
|
107
|
-
const n = this.pendingSlices + this.pendingSlicerCount;
|
|
108
|
-
this.logger.debug(
|
|
109
|
-
`execution ${this.exId} is finished scheduling, ${n} remaining slices in the queue`
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
await pWhile(async () => {
|
|
113
|
-
if (!this._creating) return true;
|
|
114
|
-
await pDelay(100);
|
|
115
|
-
return false;
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
start() {
|
|
120
|
-
this.paused = false;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async stop() {
|
|
124
|
-
if (this.stopped) return;
|
|
125
|
-
|
|
126
|
-
this.logger.debug('stopping scheduler...');
|
|
127
|
-
|
|
128
|
-
this.stopped = true;
|
|
129
|
-
|
|
130
|
-
const drain = this._drainPendingSlices(false);
|
|
131
|
-
|
|
132
|
-
this._processCleanup();
|
|
133
|
-
this._processCleanup = noop;
|
|
134
|
-
this._resolveRun();
|
|
135
|
-
this._resolveRun = noop;
|
|
136
|
-
|
|
137
|
-
await drain;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
pause() {
|
|
141
|
-
this.paused = true;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
get maxQueueLength() {
|
|
145
|
-
return this.executionContext.slicer().maxQueueLength();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
get queueLength() {
|
|
149
|
-
return this.queue.size();
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
get isQueueFull() {
|
|
153
|
-
const maxLength = this.maxQueueLength;
|
|
154
|
-
return this.pendingSlices + this.pendingSlicerCount > maxLength;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
get pendingSlicerCount() {
|
|
158
|
-
if (!this.ready) return 0;
|
|
159
|
-
|
|
160
|
-
if (this.recovering && this.recover) {
|
|
161
|
-
return this.recover.sliceCount();
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return this.executionContext.slicer().sliceCount();
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
get pendingSlices() {
|
|
168
|
-
return this.queueLength + this._creating;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
get queueRemainder() {
|
|
172
|
-
const remainder = this.maxQueueLength - this.pendingSlices;
|
|
173
|
-
return remainder > 0 ? remainder : 0;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
get isFinished() {
|
|
177
|
-
const isDone = this.slicersDone || this.slicersFailed || this.stopped;
|
|
178
|
-
return isDone && !this.pendingSlices;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
canAllocateSlice() {
|
|
182
|
-
return this.ready && !this.paused && !this.isQueueFull;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
canComplete() {
|
|
186
|
-
const { lifecycle } = this.executionContext.config;
|
|
187
|
-
return this.ready && lifecycle === 'once' && !this.recovering;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
isRecovering() {
|
|
191
|
-
return this.ready && this.recovering;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async shutdown() {
|
|
195
|
-
await this.stop();
|
|
196
|
-
|
|
197
|
-
if (this.recover) {
|
|
198
|
-
try {
|
|
199
|
-
await this.recover.shutdown();
|
|
200
|
-
} catch (err) {
|
|
201
|
-
logError(this.logger, err, 'failed to shutdown recovery');
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
this.queue.each((slice) => {
|
|
206
|
-
this.queue.remove(slice.slice_id, 'slice_id');
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
getSlices(limit = 1) {
|
|
211
|
-
if (this.queue.size() === 0) return [];
|
|
212
|
-
if (limit < 1) return [];
|
|
213
|
-
|
|
214
|
-
const slices = [];
|
|
215
|
-
|
|
216
|
-
for (let i = 0; i < limit; i += 1) {
|
|
217
|
-
const slice = this.queue.dequeue();
|
|
218
|
-
if (slice == null) break;
|
|
219
|
-
|
|
220
|
-
slices.push(slice);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (slices.length > 0) {
|
|
224
|
-
this.events.emit('slicers:queued', this.queueLength);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return slices;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
enqueueSlice(slice, addToStart) {
|
|
231
|
-
return this.enqueueSlices([slice], addToStart);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
enqueueSlices(slices, addToStart = false) {
|
|
235
|
-
if (this.stopped) return;
|
|
236
|
-
|
|
237
|
-
slices.forEach((slice) => {
|
|
238
|
-
if (this.queue.exists('slice_id', slice.slice_id)) {
|
|
239
|
-
this.logger.warn(`slice ${slice.slice_id} has already been enqueued`);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
this.logger.trace('enqueuing slice', slice);
|
|
244
|
-
|
|
245
|
-
if (addToStart) {
|
|
246
|
-
this.queue.unshift(slice);
|
|
247
|
-
} else {
|
|
248
|
-
this.queue.enqueue(slice);
|
|
249
|
-
this.executionContext.onSliceEnqueued(slice);
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
this.events.emit('slicers:queued', this.queueLength);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
_processSlicers() {
|
|
257
|
-
const { events, logger, exId } = this;
|
|
258
|
-
|
|
259
|
-
let _handling = false;
|
|
260
|
-
let _finished = false;
|
|
261
|
-
|
|
262
|
-
let createInterval;
|
|
263
|
-
let handleInterval;
|
|
264
|
-
|
|
265
|
-
const resetCleanup = () => {
|
|
266
|
-
this._processCleanup = noop;
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
const cleanup = () => {
|
|
270
|
-
clearInterval(createInterval);
|
|
271
|
-
createInterval = null;
|
|
272
|
-
clearInterval(handleInterval);
|
|
273
|
-
handleInterval = null;
|
|
274
|
-
resetCleanup();
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const drain = () => {
|
|
278
|
-
const n = this.pendingSlicerCount;
|
|
279
|
-
if (n) {
|
|
280
|
-
logger.debug(`draining the remaining ${n} pending slices from the slicer`);
|
|
281
|
-
}
|
|
282
|
-
return this._drainPendingSlices(false);
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
const onSlicerFinished = async () => {
|
|
286
|
-
cleanup();
|
|
287
|
-
logger.info(`all slicers for execution: ${exId} have been completed`);
|
|
288
|
-
|
|
289
|
-
await drain();
|
|
290
|
-
events.emit('slicers:finished');
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
const onSlicerFailure = async (err) => {
|
|
294
|
-
cleanup();
|
|
295
|
-
logger.warn('slicer failed', toString(err));
|
|
296
|
-
|
|
297
|
-
await drain();
|
|
298
|
-
|
|
299
|
-
// before removing listeners make sure we've received all of the events
|
|
300
|
-
await pDelay(100);
|
|
301
|
-
events.emit('slicers:finished', err);
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
const makeSlices = async () => {
|
|
305
|
-
try {
|
|
306
|
-
if (this.recovering && this.recover) {
|
|
307
|
-
_finished = await this.recover.handle();
|
|
308
|
-
} else {
|
|
309
|
-
_finished = await this.executionContext.slicer().handle();
|
|
310
|
-
}
|
|
311
|
-
} catch (err) {
|
|
312
|
-
await onSlicerFailure(err);
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (!_finished) {
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (!this.recovering) {
|
|
321
|
-
clearInterval(handleInterval);
|
|
322
|
-
handleInterval = null;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (this.canComplete()) {
|
|
326
|
-
await onSlicerFinished();
|
|
327
|
-
}
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
handleInterval = setInterval(() => {
|
|
331
|
-
if (!this.canAllocateSlice()) return;
|
|
332
|
-
if (_handling) return;
|
|
333
|
-
|
|
334
|
-
_handling = true;
|
|
335
|
-
|
|
336
|
-
makeSlices()
|
|
337
|
-
.then(() => {
|
|
338
|
-
_handling = false;
|
|
339
|
-
})
|
|
340
|
-
.catch((err) => {
|
|
341
|
-
_handling = false;
|
|
342
|
-
logError(this.logger, err, 'failure to run slicers');
|
|
343
|
-
});
|
|
344
|
-
}, 3);
|
|
345
|
-
|
|
346
|
-
createInterval = setInterval(() => {
|
|
347
|
-
if (!this.pendingSlicerCount) return;
|
|
348
|
-
|
|
349
|
-
this._drainPendingSlices().catch(onSlicerFailure);
|
|
350
|
-
}, 5);
|
|
351
|
-
|
|
352
|
-
this._processCleanup = cleanup;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
async _drainPendingSlices(once = true) {
|
|
356
|
-
const slices = this._getPendingSlices();
|
|
357
|
-
if (!slices.length) return;
|
|
358
|
-
|
|
359
|
-
await this._createSlices(slices);
|
|
360
|
-
|
|
361
|
-
if (once) return;
|
|
362
|
-
|
|
363
|
-
await this._drainPendingSlices();
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
_getPendingSlices() {
|
|
367
|
-
if (!this.ready) return [];
|
|
368
|
-
|
|
369
|
-
if (this.recovering && this.recover) {
|
|
370
|
-
return this.recover.getSlices(this.queueRemainder);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
return this.executionContext.slicer().getSlices(this.queueRemainder);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
async _createSlices(allSlices) {
|
|
377
|
-
// filter out anything that doesn't need to be created
|
|
378
|
-
const slices = [];
|
|
379
|
-
|
|
380
|
-
for (const slice of allSlices) {
|
|
381
|
-
// In the case of recovery slices have already been
|
|
382
|
-
// created, so its important to skip this step
|
|
383
|
-
if (slice._created) {
|
|
384
|
-
this.enqueueSlice(slice);
|
|
385
|
-
} else {
|
|
386
|
-
slice._created = makeISODate();
|
|
387
|
-
slices.push(slice);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
if (!slices.length) return;
|
|
392
|
-
|
|
393
|
-
this._creating += slices.length;
|
|
394
|
-
|
|
395
|
-
try {
|
|
396
|
-
const count = await this.stateStore.createSlices(this.exId, slices);
|
|
397
|
-
this.enqueueSlices(slices);
|
|
398
|
-
this._creating -= count;
|
|
399
|
-
} catch (err) {
|
|
400
|
-
const { lifecycle } = this.executionContext.config;
|
|
401
|
-
if (lifecycle === 'once') {
|
|
402
|
-
throw err;
|
|
403
|
-
} else {
|
|
404
|
-
logError(this.logger, err, 'failure creating slices');
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
async _initializeRecovery() {
|
|
410
|
-
this.recover = this.recover
|
|
411
|
-
|| makeExecutionRecovery(this.context, this.stateStore, this.executionContext);
|
|
412
|
-
|
|
413
|
-
await this.recover.initialize();
|
|
414
|
-
|
|
415
|
-
const { slicers: prevSlicers } = await this.exStore.get(this.recoverFromExId);
|
|
416
|
-
this.events.emit('slicers:registered', prevSlicers);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
async _initializeExecution() {
|
|
420
|
-
await this.executionContext.initialize(this.startingPoints);
|
|
421
|
-
const slicers = this.executionContext.slicer().slicers();
|
|
422
|
-
this.events.emit('slicers:registered', slicers);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
async _waitForRecovery() {
|
|
426
|
-
if (!this.recoverExecution) return;
|
|
427
|
-
|
|
428
|
-
if (!this.recover.recoveryComplete()) {
|
|
429
|
-
await new Promise((resolve) => {
|
|
430
|
-
this.events.once('execution:recovery:complete', () => {
|
|
431
|
-
resolve();
|
|
432
|
-
});
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
async _recoveryComplete() {
|
|
438
|
-
this.recovering = false;
|
|
439
|
-
this.ready = false;
|
|
440
|
-
|
|
441
|
-
if (this.recover.exitAfterComplete()) {
|
|
442
|
-
this.logger.warn('execution recovery has been marked as completed');
|
|
443
|
-
this.slicersDone = true;
|
|
444
|
-
this._processCleanup();
|
|
445
|
-
this._processCleanup = noop;
|
|
446
|
-
this._resolveRun();
|
|
447
|
-
this._resolveRun = noop;
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
const { slicers: prevSlicers } = await this.exStore.get(this.recoverFromExId);
|
|
452
|
-
this.startingPoints = await this.stateStore.getStartingPoints(
|
|
453
|
-
this.recoverFromExId,
|
|
454
|
-
prevSlicers,
|
|
455
|
-
);
|
|
456
|
-
|
|
457
|
-
this.logger.info(`execution: ${this.exId} finished its recovery`);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
module.exports = Scheduler;
|