-
Notifications
You must be signed in to change notification settings - Fork 1
avoid scheduling duplicate repeating tasks when task times out #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -175,7 +175,9 @@ taskSchema.statics.expireTimedOutTasks = async function expireTimedOutTasks(opti | |
| const task = await Task.findOneAndUpdate( | ||
| { | ||
| status: 'in_progress', | ||
| timeoutAt: { $exists: true, $lte: now } | ||
| // If task is still in_progress 10 minutes after it was supposed | ||
| // to time out, assume the task is stuck and mark it as timed_out | ||
| timeoutAt: { $exists: true, $lte: now.valueOf() - 10 * 60 * 1000 } | ||
| }, | ||
| { | ||
| $set: { | ||
|
|
@@ -219,31 +221,35 @@ taskSchema.statics.registerHandler = async function registerHandler(name, fn) { | |
| }; | ||
|
|
||
| async function _handleRepeatingTask(Task, task) { | ||
| let scheduledAt; | ||
| if (task.nextScheduledAt != null) { | ||
| const scheduledAt = new Date(task.nextScheduledAt); | ||
| return Task.create({ | ||
| name: task.name, | ||
| scheduledAt, | ||
| repeatAfterMS: task.repeatAfterMS, | ||
| params: task.params, | ||
| previousTaskId: task._id, | ||
| originalTaskId: task.originalTaskId || task._id, | ||
| timeoutMS: task.timeoutMS, | ||
| schedulingTimeoutAt: scheduledAt.valueOf() + 10 * 60 * 1000 | ||
| }); | ||
| scheduledAt = new Date(task.nextScheduledAt); | ||
| } else if (task.repeatAfterMS != null) { | ||
| const scheduledAt = new Date(task.scheduledAt.valueOf() + task.repeatAfterMS); | ||
| return Task.create({ | ||
| scheduledAt = new Date(task.scheduledAt.valueOf() + task.repeatAfterMS); | ||
| } else { | ||
| return; | ||
| } | ||
|
|
||
| return Task.updateOne( | ||
| { | ||
| name: task.name, | ||
| scheduledAt, | ||
| repeatAfterMS: task.repeatAfterMS, | ||
| params: task.params, | ||
| previousTaskId: task._id, | ||
| originalTaskId: task.originalTaskId || task._id, | ||
| timeoutMS: task.timeoutMS, | ||
| schedulingTimeoutAt: scheduledAt.valueOf() + 10 * 60 * 1000 | ||
| }); | ||
| } | ||
| previousTaskId: task._id | ||
| }, | ||
|
Comment on lines
+234
to
+238
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When two workers enter Useful? React with 👍 / 👎. |
||
| { | ||
| $setOnInsert: { | ||
| name: task.name, | ||
| scheduledAt, | ||
| repeatAfterMS: task.repeatAfterMS, | ||
| params: task.params, | ||
| previousTaskId: task._id, | ||
| originalTaskId: task.originalTaskId || task._id, | ||
| timeoutMS: task.timeoutMS, | ||
| schedulingTimeoutAt: scheduledAt.valueOf() + 10 * 60 * 1000 | ||
| } | ||
| }, | ||
| { upsert: true } | ||
| ); | ||
|
Comment on lines
+233
to
+252
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a reasonable concern, but highly unlikely given the buffer we give for hanging tasks. Will consider improving this for the future. |
||
| } | ||
|
|
||
| taskSchema.statics.registerHandlers = async function registerHandlers(obj, prefix) { | ||
|
|
@@ -337,7 +343,10 @@ taskSchema.statics.execute = async function(task, options = {}) { | |
| this._handlers.get(task.name).call(task, task.params, task) | ||
| ), | ||
| new Promise((_, reject) => { | ||
| timeoutId = setTimeout(() => reject(new Error(`Task timed out after ${task.timeoutMS} ms`)), task.timeoutMS); | ||
| timeoutId = setTimeout( | ||
| () => reject(new TimeoutError(`Task timed out after ${task.timeoutMS} ms`)), | ||
| task.timeoutMS | ||
| ); | ||
| }) | ||
| ]).finally(() => clearTimeout(timeoutId)); | ||
| } else { | ||
|
|
@@ -350,7 +359,7 @@ taskSchema.statics.execute = async function(task, options = {}) { | |
| task.result = result; | ||
| await task.save(); | ||
| } catch (error) { | ||
| task.status = 'failed'; | ||
| task.status = error instanceof TimeoutError ? 'timed_out' : 'failed'; | ||
| task.error.message = error.message; | ||
| task.error.stack = error.stack; | ||
| task.finishedRunningAt = currentTime(); | ||
|
|
@@ -379,4 +388,11 @@ taskSchema.statics.schedule = async function schedule(name, scheduledAt, params, | |
| }); | ||
| }; | ||
|
|
||
| class TimeoutError extends Error { | ||
| constructor(message) { | ||
| super(message); | ||
| this.name = 'TimeoutError'; | ||
| } | ||
| } | ||
|
|
||
| module.exports = taskSchema; | ||
Uh oh!
There was an error while loading. Please reload this page.