screwdriver-queue-service 2.0.41 → 2.0.42

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.
@@ -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
@@ -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
- prefix: ''
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 redisConfig = queueConfig.redisConnection;
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
- pkg: 'ioredis',
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: redisConnection });
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.41",
3
+ "version": "2.0.42",
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.0.4",
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": "^5.5.3",
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 Resque = require('node-resque');
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 { redisConnection } = executor;
459
+ const resqueConnection = { redis: executor.redis, namespace: queueNamespace };
459
460
  const retryOptions = {
460
- plugins: ['Retry'],
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 Resque.MultiWorker(
516
+ executor.multiWorker = new MultiWorker(
516
517
  {
517
- connection: redisConnection,
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 Resque.Scheduler({ connection: redisConnection });
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('master', state => logger.info(`scheduler became master ${state}`));
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
- const redis = new Redis({
8
- port: connectionDetails.port,
9
- host: connectionDetails.host,
10
- password: connectionDetails.options.password,
11
- tls: connectionDetails.options.tls
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, 'Retry', BlockedBy],
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, 'Retry'], // stop shouldn't use blockedBy
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, 'Retry'],
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: ['Retry'],
372
+ plugins: [Plugins.Retry],
372
373
  pluginOptions: {
373
374
  Retry: retryOptions
374
375
  },
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const NodeResque = require('node-resque');
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 { connectionDetails, queuePrefix } = require('../../config/redis');
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 NodeResque.MultiWorker(
42
+ const multiWorker = new MultiWorker(
42
43
  {
43
- connection: connectionDetails,
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 NodeResque.Scheduler({ connection: connectionDetails });
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(`queueWorker->worker[${workerId}] ${job} success ${queue} ${JSON.stringify(job)} >> ${result}`)
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('master', state => logger.info(`queueWorker->scheduler became master ${state}`));
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}`)