qdone 2.0.26-alpha → 2.0.28-alpha

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qdone",
3
- "version": "2.0.26-alpha",
3
+ "version": "2.0.28-alpha",
4
4
  "description": "Language agnostic job queue for SQS",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/src/consumer.js CHANGED
@@ -86,18 +86,24 @@ export async function processMessages (queues, callback, options) {
86
86
  const activeQrls = new Set()
87
87
  let maxReturnCount = 0
88
88
  const listen = async (qname, qrl, maxMessages) => {
89
+ if (opt.verbose) {
90
+ console.error(chalk.blue('Listening on: '), qname)
91
+ }
89
92
  activeQrls.add(qrl)
90
93
  maxReturnCount += maxMessages
91
94
  try {
92
95
  const messages = await getMessages(qrl, opt, maxMessages)
93
- if (messages.length) {
94
- for (const message of messages) {
95
- jobExecutor.executeJob(message, callback, qname, qrl, () => queueManager.updateIcehouse(qrl, true))
96
+
97
+ if (!shutdownRequested) {
98
+ if (messages.length) {
99
+ for (const message of messages) {
100
+ jobExecutor.executeJob(message, callback, qname, qrl, () => queueManager.updateIcehouse(qrl, true))
101
+ }
102
+ queueManager.updateIcehouse(qrl, false)
103
+ } else {
104
+ // If we didn't get any, update the icehouse so we can back off
105
+ queueManager.updateIcehouse(qrl, true)
96
106
  }
97
- queueManager.updateIcehouse(qrl, false)
98
- } else {
99
- // If we didn't get any, update the icehouse so we can back off
100
- queueManager.updateIcehouse(qrl, true)
101
107
  }
102
108
 
103
109
  // Max job accounting
@@ -138,16 +144,16 @@ export async function processMessages (queues, callback, options) {
138
144
  const overallFactor = Math.min(latencyFactor, freememFactor, loadFactor)
139
145
  const targetJobs = Math.round(allowedJobs * overallFactor)
140
146
  let jobsLeft = targetJobs
141
- debug({ jobCount: jobExecutor.activeJobCount(), freeMemory, totalMemory, freememThreshold, remainingMemory, memoryThreshold, maxReturnCount, allowedJobs, maxLatency, latency, latencyFactor, freememFactor, oneMinuteLoad, loadPerCore, loadFactor, overallFactor, targetJobs, activeQrls })
147
+
148
+ if (opt.verbose) {
149
+ console.error({ jobCount: jobExecutor.activeJobCount(), freeMemory, totalMemory, freememThreshold, remainingMemory, memoryThreshold, maxReturnCount, allowedJobs, maxLatency, latency, latencyFactor, freememFactor, oneMinuteLoad, loadPerCore, loadFactor, overallFactor, targetJobs, activeQrls })
150
+ }
142
151
  for (const { qname, qrl } of queueManager.getPairs()) {
143
152
  // debug({ evaluating: { qname, qrl, jobsLeft, activeQrlsHasQrl: activeQrls.has(qrl) } })
144
153
  if (jobsLeft <= 0 || activeQrls.has(qrl)) continue
145
154
  const maxMessages = Math.min(10, jobsLeft)
146
155
  listen(qname, qrl, maxMessages)
147
156
  jobsLeft -= maxMessages
148
- if (opt.verbose) {
149
- console.error(chalk.blue('Listening on: '), qname)
150
- }
151
157
  // debug({ listenedTo: { qname, maxMessages, jobsLeft } })
152
158
  }
153
159
  await delay(1000)
@@ -35,6 +35,9 @@ export class JobExecutor {
35
35
  this.shutdownRequested = true
36
36
  // Trigger a maintenance run right away in case it speeds us up
37
37
  clearTimeout(this.maintainVisibilityTimeout)
38
+ if (this.opt.verbose) {
39
+ console.error(chalk.blue('Shutting down jobExecutor'))
40
+ }
38
41
  await this.maintainPromise
39
42
  await this.maintainVisibility()
40
43
  }
@@ -47,7 +50,21 @@ export class JobExecutor {
47
50
  * Changes message visibility on all running jobs using as few calls as possible.
48
51
  */
49
52
  async maintainVisibility () {
53
+ // Bail if we are shutting down
54
+ if (this.shutdownRequested && this.stats.activeJobs === 0 && this.jobs.length === 0) {
55
+ if (this.opt.verbose) {
56
+ console.error(chalk.blue('All workers done, finishing shutdown of jobExecutor'))
57
+ }
58
+ return
59
+ }
60
+
61
+ // Reset our timeout
50
62
  clearTimeout(this.maintainVisibilityTimeout)
63
+ const nextCheckInMs = this.shutdownRequested ? 1000 : 10 * 1000
64
+ this.maintainVisibilityTimeout = setTimeout(() => {
65
+ this.maintainPromise = this.maintainVisibility()
66
+ }, nextCheckInMs)
67
+
51
68
  // debug('maintainVisibility', this.jobs)
52
69
  const start = new Date()
53
70
  const jobsToExtendByQrl = {}
@@ -84,7 +101,7 @@ export class JobExecutor {
84
101
  const secondsUntilMax = Math.max(1, maxJobSeconds - jobRunTime)
85
102
  // const secondsUntilKill = Math.max(1, this.opt.killAfter - jobRunTime)
86
103
  job.visibilityTimeout = Math.min(doubled, secondsUntilMax) //, secondsUntilKill)
87
- job.extendAtSecond = Math.round(jobRunTime + job.visibilityTimeout) // this is what we use next time
104
+ job.extendAtSecond = Math.round(jobRunTime + job.visibilityTimeout / 2) // this is what we use next time
88
105
  debug({ doubled, secondsUntilMax, job })
89
106
  }
90
107
  }
@@ -102,7 +119,7 @@ export class JobExecutor {
102
119
  while (messageId++ < 10 && jobsToExtend.length) {
103
120
  const job = jobsToExtend.shift()
104
121
  const entry = {
105
- Id: '' + messageId,
122
+ Id: job.message.MessageId,
106
123
  ReceiptHandle: job.message.ReceiptHandle,
107
124
  VisibilityTimeout: job.visibilityTimeout
108
125
  }
@@ -116,6 +133,12 @@ export class JobExecutor {
116
133
  const result = await getSQSClient().send(new ChangeMessageVisibilityBatchCommand(input))
117
134
  debug('ChangeMessageVisibilityBatch returned', result)
118
135
  this.stats.sqsCalls++
136
+ if (result.Failed) {
137
+ console.error('FAILED_MESSAGES', result.Failed)
138
+ for (const failed of result.Failed) {
139
+ console.error('FAILED_TO_EXTEND_JOB', this.jobsByMessageId[failed.Id])
140
+ }
141
+ }
119
142
  if (result.Successful) {
120
143
  const count = result.Successful.length || 0
121
144
  this.stats.timeoutsExtended += count
@@ -139,7 +162,7 @@ export class JobExecutor {
139
162
  while (messageId++ < 10 && jobsToDelete.length) {
140
163
  const job = jobsToDelete.shift()
141
164
  const entry = {
142
- Id: '' + messageId,
165
+ Id: job.message.MessageId,
143
166
  ReceiptHandle: job.message.ReceiptHandle,
144
167
  VisibilityTimeout: job.visibilityTimeout
145
168
  }
@@ -152,6 +175,12 @@ export class JobExecutor {
152
175
  debug({ DeleteMessageBatch: input })
153
176
  const result = await getSQSClient().send(new DeleteMessageBatchCommand(input))
154
177
  this.stats.sqsCalls++
178
+ if (result.Failed) {
179
+ console.error('FAILED_MESSAGES', result.Failed)
180
+ for (const failed of result.Failed) {
181
+ console.error('FAILED_TO_DELETE_JOB', this.jobsByMessageId[failed.Id])
182
+ }
183
+ }
155
184
  if (result.Successful) {
156
185
  const count = result.Successful.length || 0
157
186
  this.stats.jobsDeleted += count
@@ -176,24 +205,10 @@ export class JobExecutor {
176
205
  return true
177
206
  }
178
207
  })
179
-
180
- // Bail if we are shutting down
181
- if (this.shutdownRequested && this.stats.activeJobs === 0 && this.jobs.length === 0) return
182
-
183
- // Check later, but count the time we spent. Make sure we check at least
184
- // every period seconds.
185
- const msElapsed = new Date() - start
186
- const msPeriod = this.shutdownRequested ? 1 * 1000 : 10 * 1000
187
- const msLeft = Math.max(0, msPeriod - msElapsed)
188
- const msMin = this.shutdownRequested ? 1000 : 0
189
- const nextCheckInMs = Math.max(msMin, msLeft)
190
- debug({ msElapsed, msPeriod, msLeft, msMin, nextCheckInMs })
191
- this.maintainVisibilityTimeout = setTimeout(() => {
192
- this.maintainPromise = this.maintainVisibility()
193
- }, nextCheckInMs)
194
208
  }
195
209
 
196
210
  async executeJob (message, callback, qname, qrl, failedCallback) {
211
+ if (this.shutdownRequested) throw new Error('jobExecutor is shutting down so cannot execute new job')
197
212
  // Create job entry and track it
198
213
  const payload = this.opt.json ? JSON.parse(message.Body) : message.Body
199
214
  const visibilityTimeout = 60
@@ -69,6 +69,12 @@ export class QueueManager {
69
69
  async resolveQueues () {
70
70
  clearTimeout(this.resolveTimeout)
71
71
  if (this.shutdownRequested) return
72
+ this.resolveTimeout = setTimeout(() => {
73
+ this.resolvePromise = this.resolveQueues()
74
+ }, this.resolveSeconds * 1000)
75
+ if (this.opt.verbose) {
76
+ console.error(chalk.blue('Will resolve queues again in ' + this.resolveSeconds + ' seconds'))
77
+ }
72
78
 
73
79
  // Start processing
74
80
  const qnames = this.queues.map(queue => normalizeQueueName(queue, this.opt))
@@ -120,16 +126,9 @@ export class QueueManager {
120
126
 
121
127
  // Finished resolving
122
128
  if (this.opt.verbose) {
123
- console.error(chalk.blue(' done'))
129
+ console.error(chalk.blue('Done resolving'))
124
130
  console.error()
125
131
  }
126
-
127
- if (this.opt.verbose) {
128
- console.error(chalk.blue('Will resolve queues again in ' + this.resolveSeconds + ' seconds'))
129
- }
130
- this.resolveTimeout = setTimeout(() => {
131
- this.resolvePromise = this.resolveQueues()
132
- }, this.resolveSeconds * 1000)
133
132
  }
134
133
 
135
134
  // Return the next queue in the lineup
@@ -147,6 +146,9 @@ export class QueueManager {
147
146
  async shutdown () {
148
147
  this.shutdownRequested = true
149
148
  clearTimeout(this.resolveTimeout)
149
+ if (this.opt.verbose) {
150
+ console.error(chalk.blue('Waiting for queues to resolve'))
151
+ }
150
152
  await this.resolvePromise
151
153
  }
152
154
  }