This add-on is operated by MISC CO BV
Reliable and powerful task scheduling as a service.
Last updated May 28, 2023
Table of Contents
- Provisioning the add-on
- Defining tasks
- Testing tasks
- Scheduling tasks
- Debugging tasks
- Long-running tasks
- Dyno Type
- Concurrent one-off dyno limits
- Service API
- Heroku CLI Plugin
- Task monitoring
- Tasks timing out
- Import Heroku Scheduler Jobs
- Advanced Scheduler and Container Registry
- Migrating between plans
- Removing the add-on
Advanced Scheduler is an add-on that provides task scheduling as a service on Heroku using one-off dynos and cron expressions.
The service is built on the same principles as Heroku Scheduler, and it distinguishes itself by providing greater flexibility, transparency, and reliability in a cost-effective way.
All features are accessible from the Advanced Scheduler dashboard. It allows you to configure triggers that execute your tasks once on a specific date and time, or at a certain time interval. When executed, these tasks run in one-off dynos and show up in your application’s logs. This service is language-agnostic, meaning it can be integrated regardless of the programming language you use.
Advanced Scheduler depends on the Heroku Platform API for task execution. If the API is unavailable, execution of your scheduled tasks can be missed. If scheduled tasks are a critical component of your application, it is recommended to run a custom clock process instead.
Provisioning the add-on
Advanced Scheduler can be attached to a Heroku application via the CLI:
A list of all plans available can be found here.
$ heroku addons:create advanced-scheduler
-----> Adding advanced-scheduler to sharp-mountain-4005... done, v18 (free)
After you install Advanced Scheduler, your application should be configured to fully integrate with the add-on. If you are new to task scheduling, you can read up on defining, testing, scheduling and debugging tasks below.
Tasks are any command that can be run in your application.
Almost every framework or language has a convention to define such a command. Some have a built-in feature for setting up tasks, while others require you to add a script to
bin/ that will perform the task.
Note that each framework or language might define tasks in a different way. Refer to the best practices for the framework or language you are using inside your application.
For Rails, the convention is to set up rake tasks. To create your task in Rails, copy the code below into
lib/tasks/scheduler.rake and customize it to fit your needs.
desc "This task is called by the Advanced Scheduler scheduler add-on"
task :update_feed => :environment do
puts "Updating feed..."
task :send_reminders => :environment do
DB = Sequel.connect ENV["DATABASE_URL"]
puts "Cleaning old sessions..."
DB["DELETE FROM sessions WHERE last_seen_at < ?", Time.now - 24*60*60]
Note that if you are using a script, an interpreter might be needed in the command above to execute your task correctly. To prevent this, add a shebang at the top of your script (e.g.,
#!/usr/bin/env node for Node.js or
#!/usr/bin/env ruby for Ruby). This tells the system which interpreter to use to execute the script.
Once you have written your task and see that it is functioning locally, the next step is to deploy your application and test your task on Heroku.
To do so, use
heroku run to run your task on Heroku:
$ heroku run <your-task>
To schedule a task, you need to create a new active trigger for it. Triggers are configured using the Advanced Scheduler dashboard.
Enter the task and specify if the trigger needs to be executed only once or at a certain time interval. For one-off triggers, define a date and time at which the task should be executed. For recurring triggers, provide a standard cron expression or use the schedule helper to define the interval. As a tip, check your cron expressions here to verify they are correct. By default, a new trigger will be activated directly after creation. If you do not want this behaviour, you can configure the trigger to stay inactive.
Instead of specifying a command, you can specify one of the process types in your app’s Procfile. The command associated with the process type will then be executed, together with any parameters you supply. See the syntax for one-off dynos to learn more.
Note that for each plan there are limits on the total number of tasks that can be scheduled at one point in time, as well as the total number of task executions in one month. Once one of these limits is exceeded, attempts to schedule a new task will fail.
To debug a task, you have to check your application’s logs. You can check your logs in real time or use any of the logging add-ons available in the Heroku Marketplace.
To check your real-time logs, use the
heroku logs command:
$ heroku logs -t -a <your-app> -d advanced-scheduler
2020-01-15T14:10:16+00:00 heroku[advanced-scheduler.1]: State changed from created to starting
2020-01-15T14:10:16+00:00 app[advanced-scheduler.1]: Starting process with command `node bin/send-newsletter.js`
2020-01-15T14:10:19+00:00 app[advanced-scheduler.1]: Sending newsletters...
2020-01-15T14:10:27+00:00 app[advanced-scheduler.1]: done.
2020-01-15T14:10:28+00:00 heroku[advanced-scheduler.1]: State changed from up to complete
A running task is also visible with the
heroku ps command:
$ heroku ps
=== advanced-scheduler (Free): node bin/send-newsletter.js (1)
advanced-scheduler.1: up 2020/01/15 14:10:16 +0100 (~ 2s ago)
The task monitoring of Advanced Scheduler depends on the result of your task. Make sure your task exits with the right exit code (sometimes referred to as a return status or exit status). A successful task returns a 0, while an unsuccessful one returns a non-zero value.
Although it is generally recommended to keep tasks lightweight and quick to execute, Advanced Scheduler can be used for longer-running tasks. Make sure to only have 1 task running by setting the execution interval higher than the task’s maximum execution time. In the event that a specific task does run longer than intended, you can force the task to exit by setting its trigger’s timeout value to the maximum allowed execution time. See tasks timing out to learn more.
Note that a task can run for up to 24 hours by settings its trigger’s timeout value to 86400 seconds.
You can only choose a dyno type within the dyno tier configured for your Heroku app. For example, you can’t configure your trigger to start a one-off dyno using
standard-2x when you use
eco for your formation dynos.
Heroku offers the following dyno tiers:
- Eco tier: An app that uses
ecodynos can only use
ecodynos for its one-off dynos.
- Basic tier: An app that uses
basicdynos can only use
basicdynos for its one-off dynos.
- Professional tier: An app that uses Professional-tier dynos (
performance) can use
performance-lfor its one-off dynos.
- Private tier: An app that uses Private-tier dynos can use
private-lfor its one-off dynos.
When a trigger is configured to use a type of dyno that is not available for your Heroku app, Advanced Scheduler will automatically fall back to Heroku’s default dyno type for your app.
Concurrent one-off dyno limits
The limit for your app’s concurrently running one-off dynos depends on several factors. See which limit applies to your situation.
Beware that when exceeding your app’s concurrent one-off dyno limit, the next task might not be executed.
To stay below Heroku’s concurrent one-off dyno limit, make sure to plan the execution of your tasks with care. Avoid a backlog of one-off dynos created when scheduled tasks are executed before running tasks are finished or time out.
Alternatively, you can contact Heroku to get your limit raised.
The Advanced Scheduler dashboard allows you to configure one-off and recurring triggers that execute different tasks.
You can access the dashboard via the CLI:
$ heroku addons:open advanced-scheduler
Opening advanced-scheduler for sharp-mountain-4005
or by visiting the Heroku Dashboard and selecting the application in question. Select Advanced Scheduler from the Add-ons menu.
The Advanced Scheduler Service API lets you programmatically automate, extend and combine Advanced Scheduler with other services. You can use the Service API to create and manage triggers. For details, see the Service API Reference.
To interact with the Service API, you will need an API token. This API token can be generated in the Advanced Scheduler dashboard.
Note that when generating an API token in the Advanced Scheduler dashboard, the
ADVANCED_SCHEDULER_API_TOKEN config var will be set on your Heroku application and cause it to restart.
Heroku CLI Plugin
To install the plugin:
$ heroku plugins:install advanced-scheduler
To start using the plugin:
$ heroku triggers --app example
=== 01234567-89ab-cdef-0123-456789abcdef (active): Monday morning newsletter
At 09:00 AM, only on Monday (UTC) w/ Standard-1X ⬢
$ node bin/send-newsletter.js
To consult the plugin documentation:
$ heroku triggers -h
List the Advanced Scheduler triggers for an app
$ heroku triggers...
-a, --app=app (required) app to run command against
-h, --help show CLI help
-j, --json output triggers in json format
$ heroku triggers -a example
triggers:activate Activate an Advanced Scheduler trigger for an app
triggers:create Create a new Advanced Scheduler trigger for an app
triggers:deactivate Deactivate an Advanced Scheduler trigger for an app
triggers:delete Permanently delete an Advanced Scheduler trigger for an app
triggers:update Update an Advanced Scheduler trigger for an app
By default, Advanced Scheduler monitors the executions of your scheduled tasks. For every trigger an email notification is sent on the first failed execution each day.
Note that the successful or failed execution of your task depends on the process exit code (sometimes referred to as a return status or exit status), so make sure your process is exiting properly. When the process exits with code 0, the execution is considered successful. Anything else is treated as a failed execution.
Whenever a one-off dyno fails to be provisioned, the process never actually runs and consequently there is no process exit code. Advanced Scheduler interprets this as a failed execution and sends a task failure alert notification with exit status
Alert Notification Subscribers
By default, the distribution for email notifications is to all app owners and collaborators for non-org accounts, and admins for people in a Heroku Enterprise org. Alternatively, reach out to firstname.lastname@example.org to add additional email addresses, such as for email-based PagerDuty integration.
Tasks timing out
When a task runs longer than its trigger’s timeout value, it will be forced to exit shortly after. This mechanism is intended to either prevent a backlog of one-off dynos or to ensure only 1 task of a specific trigger is running at a time. Always aim to make tasks finish by themselves. When a task does time out, make sure to figure out why and take action to prevent it from happening again.
Advanced Scheduler supports getting alert notifications on task timeout. Opt-in by reaching out to email@example.com.
Note that the default timeout value for new triggers is 1800 seconds or 30 minutes and the maximum timeout value is 86400 seconds or 24 hours.
Import Heroku Scheduler Jobs
Heroku Scheduler jobs can be imported into Advanced Scheduler using the Advanced Scheduler dashboard. Advanced Scheduler is totally compatible with Heroku Scheduler. All imported jobs will behave exactly the same after import.
To start importing your Heroku Scheduler jobs, head to the Advanced Scheduler dashboard and click the
Import Heroku Scheduler Jobs button in the Triggers section of the overview page.
All imported jobs will be inactive to avoid collisions with your existing Heroku Scheduler jobs.
Heroku API token
Advanced Scheduler uses a Heroku API token to access the Heroku Scheduler jobs on your Heroku app. The Heroku API token can be retrieved using the
heroku auth:token command, which outputs your current Heroku CLI authentication token and when it will expire. The token can be explicitly invalidated by running
heroku auth:logout. For more information check out the Heroku CLI documentation.
The provided Heroku API token will not be stored by Advanced Scheduler and will only be used to perform a single GET request to fetch your Heroku Scheduler jobs.
Heroku Scheduler Timeouts
All jobs run by Heroku Scheduler have a maximum runtime equal to the frequency of their execution. For example, jobs scheduled to run every 10 minutes will be terminated after approximately 10 minutes. To make sure imported jobs behave identically, Advanced Scheduler uses the same timeout values that Heroku Scheduler uses.
Advanced Scheduler and Container Registry
If you are using Advanced Scheduler and Container Registry as your deployment method, your task must be accessible from the
web image. There is no way to specify a non-web image for task execution.
Migrating between plans
Note that application owners should carefully manage the migration timing to ensure proper application function during the migration process.
heroku addons:upgrade command to migrate to a new plan.
$ heroku addons:upgrade advanced-scheduler:newplan
-----> Upgrading advanced-scheduler:newplan to sharp-mountain-4005... done, v18 ($49/mo)
Your plan has been updated to: advanced-scheduler:newplan
When downgrading to a plan that does not include access to the Advanced Scheduler Service API, the API token will be removed if applicable. This action will also remove the config var
ADVANCED_SCHEDULER_API_TOKEN and cause your Heroku application to restart.
Removing the add-on
You can remove Advanced Scheduler via the CLI:
This will destroy all associated data and cannot be undone!
$ heroku addons:destroy advanced-scheduler
-----> Removing advanced-scheduler from sharp-mountain-4005... done, v20 (free)
All Advanced Scheduler support and runtime issues should be submitted via one of the Heroku Support channels. Any non-support related issues or product feedback is welcome at firstname.lastname@example.org.