screwdriver-queue-service 2.0.41 → 2.0.43
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/config/custom-environment-variables.yaml +11 -1
- package/config/default.yaml +9 -1
- package/config/redis.js +31 -7
- package/lib/queue.js +3 -5
- package/package.json +3 -3
- package/plugins/queue/scheduler.js +12 -14
- package/plugins/redis.js +11 -6
- package/plugins/worker/lib/jobs.js +5 -4
- package/plugins/worker/worker.js +16 -12
|
@@ -219,6 +219,8 @@ ecosystem:
|
|
|
219
219
|
max_size_mb: CACHE_MAX_SIZE_MB
|
|
220
220
|
|
|
221
221
|
queue:
|
|
222
|
+
# redis or redisCluster(beta)
|
|
223
|
+
connectionType: REDIS_TYPE
|
|
222
224
|
redisConnection:
|
|
223
225
|
host: REDIS_HOST
|
|
224
226
|
port: REDIS_PORT
|
|
@@ -226,6 +228,14 @@ queue:
|
|
|
226
228
|
password: REDIS_PASSWORD
|
|
227
229
|
tls: REDIS_TLS_ENABLED
|
|
228
230
|
database: REDIS_DB_NUMBER
|
|
231
|
+
redisClusterConnection:
|
|
232
|
+
hosts:
|
|
233
|
+
__name: REDIS_CLUSTER_HOSTS
|
|
234
|
+
__format: json
|
|
235
|
+
options:
|
|
236
|
+
password: REDIS_PASSWORD
|
|
237
|
+
tls: REDIS_TLS_ENABLED
|
|
238
|
+
slotsRefreshTimeout: REDIS_CLUSTER_SLOTS_REFRESH_TIMEOUT
|
|
229
239
|
prefix: REDIS_QUEUE_PREFIX
|
|
230
240
|
|
|
231
241
|
plugins:
|
|
@@ -292,4 +302,4 @@ kafka:
|
|
|
292
302
|
# AWS region
|
|
293
303
|
region: AWS_REGION
|
|
294
304
|
# Flag to use Short Region Name like use2,usw2
|
|
295
|
-
shortRegion : USE_SHORT_REGION_NAME
|
|
305
|
+
shortRegion : USE_SHORT_REGION_NAME
|
package/config/default.yaml
CHANGED
|
@@ -149,6 +149,8 @@ ecosystem:
|
|
|
149
149
|
max_size_mb: 0
|
|
150
150
|
|
|
151
151
|
queue:
|
|
152
|
+
# redis or redisCluster(beta)
|
|
153
|
+
connectionType: redis
|
|
152
154
|
redisConnection:
|
|
153
155
|
host: REDIS_HOST
|
|
154
156
|
port: REDIS_PORT
|
|
@@ -156,7 +158,13 @@ queue:
|
|
|
156
158
|
password: a-secure-password
|
|
157
159
|
tls: false
|
|
158
160
|
database: 0
|
|
159
|
-
|
|
161
|
+
redisClusterConnection:
|
|
162
|
+
hosts: []
|
|
163
|
+
options:
|
|
164
|
+
password: a-secure-password
|
|
165
|
+
tls: false
|
|
166
|
+
slotsRefreshTimeout: 1000
|
|
167
|
+
prefix: ""
|
|
160
168
|
|
|
161
169
|
plugins:
|
|
162
170
|
blockedBy:
|
package/config/redis.js
CHANGED
|
@@ -3,18 +3,41 @@
|
|
|
3
3
|
const config = require('config');
|
|
4
4
|
|
|
5
5
|
const queueConfig = config.get('queue');
|
|
6
|
-
const
|
|
6
|
+
const connectionType = queueConfig.connectionType;
|
|
7
|
+
|
|
8
|
+
if (!connectionType || (connectionType !== 'redis' && connectionType !== 'redisCluster')) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`connectionType '${connectionType}' is not supported, use 'redis' or 'redisCluster' for the queue.connectionType setting`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const redisConfig = queueConfig[`${connectionType}Connection`];
|
|
7
15
|
const connectionDetails = {
|
|
8
|
-
|
|
9
|
-
host: redisConfig.host,
|
|
10
|
-
options: {
|
|
16
|
+
redisOptions: {
|
|
11
17
|
password: redisConfig.options && redisConfig.options.password,
|
|
12
18
|
tls: redisConfig.options ? redisConfig.options.tls : false
|
|
13
|
-
}
|
|
14
|
-
port: redisConfig.port,
|
|
15
|
-
database: redisConfig.database
|
|
19
|
+
}
|
|
16
20
|
};
|
|
17
21
|
|
|
22
|
+
let queueNamespace;
|
|
23
|
+
|
|
24
|
+
// for redisCluster config
|
|
25
|
+
if (connectionType === 'redisCluster') {
|
|
26
|
+
connectionDetails.redisClusterHosts = redisConfig.hosts;
|
|
27
|
+
connectionDetails.slotsRefreshTimeout = parseInt(redisConfig.slotsRefreshTimeout, 10);
|
|
28
|
+
// NOTE: node-resque has an issue in multi-key operation for Redis Cluster
|
|
29
|
+
// https://github.com/actionhero/node-resque/issues/786
|
|
30
|
+
// so we have to set the namespace option with a hash tag so that the resque's keys are set in the same slots in Redis Cluster
|
|
31
|
+
// https://redis.io/docs/manual/scaling/#redis-cluster-data-sharding
|
|
32
|
+
queueNamespace = 'resque:{screwdriver-resque}';
|
|
33
|
+
} else {
|
|
34
|
+
// for non-cluster redis config
|
|
35
|
+
connectionDetails.redisOptions.host = redisConfig.host;
|
|
36
|
+
connectionDetails.redisOptions.port = redisConfig.port;
|
|
37
|
+
connectionDetails.redisOptions.database = redisConfig.database;
|
|
38
|
+
queueNamespace = 'resque';
|
|
39
|
+
}
|
|
40
|
+
|
|
18
41
|
const queuePrefix = queueConfig.prefix || '';
|
|
19
42
|
|
|
20
43
|
const runningJobsPrefix = `${queuePrefix}running_job_`;
|
|
@@ -22,6 +45,7 @@ const waitingJobsPrefix = `${queuePrefix}waiting_job_`;
|
|
|
22
45
|
|
|
23
46
|
module.exports = {
|
|
24
47
|
connectionDetails,
|
|
48
|
+
queueNamespace,
|
|
25
49
|
queuePrefix,
|
|
26
50
|
runningJobsPrefix,
|
|
27
51
|
waitingJobsPrefix
|
package/lib/queue.js
CHANGED
|
@@ -6,6 +6,8 @@ const Breaker = fuses.breaker;
|
|
|
6
6
|
const FuseBox = fuses.box;
|
|
7
7
|
const logger = require('screwdriver-logger');
|
|
8
8
|
const redis = require('../plugins/redis');
|
|
9
|
+
const { queueNamespace } = require('../config/redis');
|
|
10
|
+
const resqueConnection = { redis, namespace: queueNamespace };
|
|
9
11
|
|
|
10
12
|
module.exports = class ExecutorQueue {
|
|
11
13
|
/**
|
|
@@ -37,14 +39,10 @@ module.exports = class ExecutorQueue {
|
|
|
37
39
|
this.unzipQueue = `${this.prefix}unzip`;
|
|
38
40
|
this.webhookQueue = `${this.prefix}webhooks`;
|
|
39
41
|
|
|
40
|
-
const redisConnection = { ...config.redisConnection, pkg: 'ioredis' };
|
|
41
|
-
|
|
42
|
-
this.redisConnection = redisConnection;
|
|
43
|
-
|
|
44
42
|
this.redis = redis;
|
|
45
43
|
|
|
46
44
|
// eslint-disable-next-line new-cap
|
|
47
|
-
this.queue = new Resque.Queue({ connection:
|
|
45
|
+
this.queue = new Resque.Queue({ connection: resqueConnection });
|
|
48
46
|
this.queue.on('error', error => logger.info(`Resque queue error >> ${error}`));
|
|
49
47
|
this.queueBreaker = new Breaker((funcName, ...args) => {
|
|
50
48
|
const callback = args.pop();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "screwdriver-queue-service",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.43",
|
|
4
4
|
"description": "Screwdriver Queue Service API",
|
|
5
5
|
"main": "app.js",
|
|
6
6
|
"directories": {
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"config": "^1.31.0",
|
|
21
21
|
"cron-parser": "^4.2.1",
|
|
22
22
|
"hapi-auth-jwt2": "^10.2.0",
|
|
23
|
-
"ioredis": "^5.
|
|
23
|
+
"ioredis": "^5.2.3",
|
|
24
24
|
"joi": "^17.5.0",
|
|
25
25
|
"js-yaml": "^3.14.1",
|
|
26
26
|
"jsonwebtoken": "^8.5.1",
|
|
27
|
-
"node-resque": "^
|
|
27
|
+
"node-resque": "^9.2.0",
|
|
28
28
|
"npm-auto-version": "^1.0.0",
|
|
29
29
|
"redlock": "^4.2.0",
|
|
30
30
|
"screwdriver-aws-producer-service": "^1.3.0",
|
|
@@ -4,10 +4,11 @@ const logger = require('screwdriver-logger');
|
|
|
4
4
|
const configSchema = require('screwdriver-data-schema').config;
|
|
5
5
|
const TOKEN_CONFIG_SCHEMA = configSchema.tokenConfig;
|
|
6
6
|
const { merge, reach } = require('@hapi/hoek');
|
|
7
|
-
const
|
|
7
|
+
const { MultiWorker, Scheduler, Plugins } = require('node-resque');
|
|
8
8
|
const cron = require('./utils/cron');
|
|
9
9
|
const helper = require('../helper');
|
|
10
10
|
const { timeOutOfWindows } = require('./utils/freezeWindows');
|
|
11
|
+
const { queueNamespace } = require('../../config/redis');
|
|
11
12
|
const DEFAULT_BUILD_TIMEOUT = 90;
|
|
12
13
|
const RETRY_LIMIT = 3;
|
|
13
14
|
const RETRY_DELAY = 5;
|
|
@@ -455,9 +456,9 @@ async function start(executor, config) {
|
|
|
455
456
|
async function init(executor) {
|
|
456
457
|
if (executor.multiWorker) return 'Scheduler running';
|
|
457
458
|
|
|
458
|
-
const {
|
|
459
|
+
const resqueConnection = { redis: executor.redis, namespace: queueNamespace };
|
|
459
460
|
const retryOptions = {
|
|
460
|
-
plugins: [
|
|
461
|
+
plugins: [Plugins.Retry],
|
|
461
462
|
pluginOptions: {
|
|
462
463
|
Retry: {
|
|
463
464
|
retryLimit: RETRY_LIMIT,
|
|
@@ -512,9 +513,9 @@ async function init(executor) {
|
|
|
512
513
|
}
|
|
513
514
|
};
|
|
514
515
|
|
|
515
|
-
executor.multiWorker = new
|
|
516
|
+
executor.multiWorker = new MultiWorker(
|
|
516
517
|
{
|
|
517
|
-
connection:
|
|
518
|
+
connection: resqueConnection,
|
|
518
519
|
queues: [executor.periodicBuildQueue, executor.frozenBuildQueue],
|
|
519
520
|
minTaskProcessors: 1,
|
|
520
521
|
maxTaskProcessors: 10,
|
|
@@ -525,7 +526,7 @@ async function init(executor) {
|
|
|
525
526
|
jobs
|
|
526
527
|
);
|
|
527
528
|
|
|
528
|
-
executor.scheduler = new
|
|
529
|
+
executor.scheduler = new Scheduler({ connection: resqueConnection });
|
|
529
530
|
|
|
530
531
|
executor.multiWorker.on('start', workerId => logger.info(`worker[${workerId}] started`));
|
|
531
532
|
executor.multiWorker.on('end', workerId => logger.info(`worker[${workerId}] ended`));
|
|
@@ -538,22 +539,19 @@ async function init(executor) {
|
|
|
538
539
|
executor.multiWorker.on('reEnqueue', (workerId, queue, job, plugin) =>
|
|
539
540
|
logger.info(`worker[${workerId}] reEnqueue job (${plugin}) ${queue} ${JSON.stringify(job)}`)
|
|
540
541
|
);
|
|
541
|
-
executor.multiWorker.on('success', (workerId, queue, job, result) =>
|
|
542
|
-
logger.info(`worker[${workerId}] job success ${queue} ${JSON.stringify(job)} >> ${result}`)
|
|
542
|
+
executor.multiWorker.on('success', (workerId, queue, job, result, duration) =>
|
|
543
|
+
logger.info(`worker[${workerId}] job success ${queue} ${JSON.stringify(job)} >> ${result} (${duration}ms)`)
|
|
543
544
|
);
|
|
544
|
-
executor.multiWorker.on('failure', (workerId, queue, job, failure) =>
|
|
545
|
-
logger.info(`worker[${workerId}] job failure ${queue} ${JSON.stringify(job)} >> ${failure}`)
|
|
545
|
+
executor.multiWorker.on('failure', (workerId, queue, job, failure, duration) =>
|
|
546
|
+
logger.info(`worker[${workerId}] job failure ${queue} ${JSON.stringify(job)} >> ${failure} (${duration}ms)`)
|
|
546
547
|
);
|
|
547
548
|
executor.multiWorker.on('error', (workerId, queue, job, error) =>
|
|
548
549
|
logger.error(`worker[${workerId}] error ${queue} ${JSON.stringify(job)} >> ${error}`)
|
|
549
550
|
);
|
|
550
551
|
|
|
551
|
-
// multiWorker emitters
|
|
552
|
-
executor.multiWorker.on('internalError', error => logger.error(error));
|
|
553
|
-
|
|
554
552
|
executor.scheduler.on('start', () => logger.info('scheduler started'));
|
|
555
553
|
executor.scheduler.on('end', () => logger.info('scheduler ended'));
|
|
556
|
-
executor.scheduler.on('
|
|
554
|
+
executor.scheduler.on('leader', () => logger.info(`scheduler became leader`));
|
|
557
555
|
executor.scheduler.on('error', error => logger.info(`scheduler error >> ${error}`));
|
|
558
556
|
executor.scheduler.on('workingTimestamp', timestamp => logger.info(`scheduler working timestamp ${timestamp}`));
|
|
559
557
|
executor.scheduler.on('cleanStuckWorker', (workerName, errorPayload, delta) =>
|
package/plugins/redis.js
CHANGED
|
@@ -4,12 +4,17 @@ const Redis = require('ioredis');
|
|
|
4
4
|
const logger = require('screwdriver-logger');
|
|
5
5
|
const { connectionDetails } = require('../config/redis');
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
let redis;
|
|
8
|
+
|
|
9
|
+
if (connectionDetails.redisClusterHosts) {
|
|
10
|
+
redis = new Redis.Cluster(connectionDetails.redisClusterHosts, {
|
|
11
|
+
redisOptions: connectionDetails.redisOptions,
|
|
12
|
+
slotsRefreshTimeout: connectionDetails.slotsRefreshTimeout,
|
|
13
|
+
clusterRetryStrategy: () => 100
|
|
14
|
+
});
|
|
15
|
+
} else {
|
|
16
|
+
redis = new Redis(connectionDetails.redisOptions);
|
|
17
|
+
}
|
|
13
18
|
|
|
14
19
|
redis.on('connecting', () => {
|
|
15
20
|
logger.info('Connecting to Redis.');
|
|
@@ -6,6 +6,7 @@ const hoek = require('@hapi/hoek');
|
|
|
6
6
|
const ExecutorRouter = require('screwdriver-executor-router');
|
|
7
7
|
const logger = require('screwdriver-logger');
|
|
8
8
|
const AWSProducer = require('screwdriver-aws-producer-service');
|
|
9
|
+
const { Plugins } = require('node-resque');
|
|
9
10
|
const helper = require('../../helper');
|
|
10
11
|
const { BlockedBy } = require('./BlockedBy');
|
|
11
12
|
const { Filter } = require('./Filter');
|
|
@@ -346,7 +347,7 @@ async function sendWebhook(configs) {
|
|
|
346
347
|
|
|
347
348
|
module.exports = {
|
|
348
349
|
start: {
|
|
349
|
-
plugins: [Filter,
|
|
350
|
+
plugins: [Filter, Plugins.Retry, BlockedBy],
|
|
350
351
|
pluginOptions: {
|
|
351
352
|
Retry: retryOptions,
|
|
352
353
|
BlockedBy: blockedByOptions
|
|
@@ -354,21 +355,21 @@ module.exports = {
|
|
|
354
355
|
perform: start
|
|
355
356
|
},
|
|
356
357
|
stop: {
|
|
357
|
-
plugins: [Filter,
|
|
358
|
+
plugins: [Filter, Plugins.Retry], // stop shouldn't use blockedBy
|
|
358
359
|
pluginOptions: {
|
|
359
360
|
Retry: retryOptions
|
|
360
361
|
},
|
|
361
362
|
perform: stop
|
|
362
363
|
},
|
|
363
364
|
clear: {
|
|
364
|
-
plugins: [CacheFilter,
|
|
365
|
+
plugins: [CacheFilter, Plugins.Retry],
|
|
365
366
|
pluginOptions: {
|
|
366
367
|
Retry: retryOptions
|
|
367
368
|
},
|
|
368
369
|
perform: clear
|
|
369
370
|
},
|
|
370
371
|
sendWebhook: {
|
|
371
|
-
plugins: [
|
|
372
|
+
plugins: [Plugins.Retry],
|
|
372
373
|
pluginOptions: {
|
|
373
374
|
Retry: retryOptions
|
|
374
375
|
},
|
package/plugins/worker/worker.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { MultiWorker, Scheduler } = require('node-resque');
|
|
4
4
|
const config = require('config');
|
|
5
5
|
const logger = require('screwdriver-logger');
|
|
6
6
|
const Redlock = require('redlock');
|
|
@@ -8,7 +8,7 @@ const jobs = require('./lib/jobs');
|
|
|
8
8
|
const timeout = require('./lib/timeout');
|
|
9
9
|
const helper = require('../helper');
|
|
10
10
|
const workerConfig = config.get('worker');
|
|
11
|
-
const {
|
|
11
|
+
const { queueNamespace, queuePrefix } = require('../../config/redis');
|
|
12
12
|
const redis = require('../redis');
|
|
13
13
|
// https://github.com/mike-marcacci/node-redlock
|
|
14
14
|
const redlock = new Redlock([redis], {
|
|
@@ -17,6 +17,7 @@ const redlock = new Redlock([redis], {
|
|
|
17
17
|
retryDelay: 200, // time in ms
|
|
18
18
|
retryJitter: 200 // time in ms
|
|
19
19
|
});
|
|
20
|
+
const resqueConnection = { redis, namespace: queueNamespace };
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Shutdown both worker and scheduler and then exit the process
|
|
@@ -38,9 +39,9 @@ async function shutDownAll(worker, scheduler) {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
const multiWorker = new
|
|
42
|
+
const multiWorker = new MultiWorker(
|
|
42
43
|
{
|
|
43
|
-
connection:
|
|
44
|
+
connection: resqueConnection,
|
|
44
45
|
queues: [`${queuePrefix}builds`, `${queuePrefix}cache`, `${queuePrefix}webhooks`],
|
|
45
46
|
minTaskProcessors: workerConfig.minTaskProcessors,
|
|
46
47
|
maxTaskProcessors: workerConfig.maxTaskProcessors,
|
|
@@ -50,7 +51,7 @@ const multiWorker = new NodeResque.MultiWorker(
|
|
|
50
51
|
jobs
|
|
51
52
|
);
|
|
52
53
|
|
|
53
|
-
const scheduler = new
|
|
54
|
+
const scheduler = new Scheduler({ connection: resqueConnection });
|
|
54
55
|
|
|
55
56
|
/**
|
|
56
57
|
* Start worker & scheduler
|
|
@@ -81,10 +82,14 @@ async function invoke() {
|
|
|
81
82
|
)}`
|
|
82
83
|
)
|
|
83
84
|
);
|
|
84
|
-
multiWorker.on('success', (workerId, queue, job, result) =>
|
|
85
|
-
logger.info(
|
|
85
|
+
multiWorker.on('success', (workerId, queue, job, result, duration) =>
|
|
86
|
+
logger.info(
|
|
87
|
+
`queueWorker->worker[${workerId}] ${job} success ${queue} ${JSON.stringify(
|
|
88
|
+
job
|
|
89
|
+
)} >> ${result} (${duration}ms)`
|
|
90
|
+
)
|
|
86
91
|
);
|
|
87
|
-
multiWorker.on('failure', (workerId, queue, job, failure) =>
|
|
92
|
+
multiWorker.on('failure', (workerId, queue, job, failure, duration) =>
|
|
88
93
|
helper
|
|
89
94
|
.updateBuildStatus({
|
|
90
95
|
redisInstance: redis,
|
|
@@ -96,14 +101,14 @@ async function invoke() {
|
|
|
96
101
|
logger.info(
|
|
97
102
|
`queueWorker->worker[${workerId}] ${JSON.stringify(job)} ` +
|
|
98
103
|
`failure ${queue} ` +
|
|
99
|
-
`${JSON.stringify(job)} >> successfully update build status: ${failure}`
|
|
104
|
+
`${JSON.stringify(job)} >> successfully update build status: ${failure} (${duration}ms)`
|
|
100
105
|
);
|
|
101
106
|
})
|
|
102
107
|
.catch(err => {
|
|
103
108
|
logger.error(
|
|
104
109
|
`queueWorker->worker[${workerId}] ${job} failure ` +
|
|
105
110
|
`${queue} ${JSON.stringify(job)} ` +
|
|
106
|
-
`>> ${failure} ${err}`
|
|
111
|
+
`>> ${failure} (${duration}ms) ${err}`
|
|
107
112
|
);
|
|
108
113
|
})
|
|
109
114
|
);
|
|
@@ -113,7 +118,6 @@ async function invoke() {
|
|
|
113
118
|
multiWorker.on('pause', workerId => logger.info(`queueWorker->worker[${workerId}] paused`));
|
|
114
119
|
|
|
115
120
|
// multiWorker emitters
|
|
116
|
-
multiWorker.on('internalError', error => logger.error(error));
|
|
117
121
|
multiWorker.on('multiWorkerAction', (verb, delay) =>
|
|
118
122
|
logger.info(`queueWorker->*** checked for worker status: ${verb} (event loop delay: ${delay}ms)`)
|
|
119
123
|
);
|
|
@@ -121,7 +125,7 @@ async function invoke() {
|
|
|
121
125
|
scheduler.on('start', () => logger.info('queueWorker->scheduler started'));
|
|
122
126
|
scheduler.on('end', () => logger.info('queueWorker->scheduler ended'));
|
|
123
127
|
scheduler.on('poll', () => logger.info('queueWorker->scheduler polling'));
|
|
124
|
-
scheduler.on('
|
|
128
|
+
scheduler.on('leader', () => logger.info(`queueWorker->scheduler became leader`));
|
|
125
129
|
scheduler.on('error', error => logger.info(`queueWorker->scheduler error >> ${error}`));
|
|
126
130
|
scheduler.on('workingTimestamp', timestamp =>
|
|
127
131
|
logger.info(`queueWorker->scheduler working timestamp ${timestamp}`)
|