Azure WebJobs: Scheduling and Scaling

Why Azure WebJobs?

Azure WebJobs are a great resource included in Azure App Service plans. They provide a way for you to run binaries or scripts that perform scheduled or event-triggered job processing for work that isn’t suited to be done during a normal web request. Have a monthly cleanup task for your website? Sending a big batch of emails? Need to process some user uploaded files? These are all great examples of things you can throw into a WebJob.

How/where do they run?

When a WebJob is executed it shares the same CPU context/resources as the app service it is hosted with. So if you write CPU intensive jobs they will impact the performance of the app service instance. Thankfully most common website maintenance tasks aren’t too CPU intensive so the performance impact is negligible. A nice thing about the shared process space is that WebJobs have access to your existing App Service’s application settings.

If you want something to run outside of the app service context, I would recommend looking at Azure Functions – the serverless on-demand compute option. When deciding between Azure Functions and WebJobs – consider the cost as well. You will need to pay for Azure Function usage based on resource consumption and executions. Whereas Azure WebJobs are free; they come with the app-service plan you are already paying for.

WebJobs can have no schedule (run on demand), they can be scheduled to execute on a timer, and they can run continuously.

Note: The always-on setting for app service instances should be enabled if you want reliable WebJob trigger/execution. Otherwise WebJobs won’t run when your app is unloaded for inactivity.

Continuous WebJobs

A common pattern for the continuous WebJob schedule is to process Azure Storage Queue messages. The function(s) inside a WebJob can be configured with a queue message trigger using the WebJobs SDK. This automatically executes the function when a new message arrives in the queue.

public static async Task ProcessQueueMessageAsync([QueueTrigger("EmailMessageQueue")] string message, TextWriter log)

In the example above, the queue name is hardcoded, but this can also be derived from an environment variable.

Scheduled WebJobs

For scheduled WebJobs you define the schedule in the settings.job file. The settings.job file is a JSON blob that sits in the root of your WebJob project. Set the properties for this file in Visual Studio to ‘Copy to output directory: Always’ so the file is deployed with your compiled binaries. Use a CRON expression to define the execution schedule.

[NoAutomaticTrigger]
public static async Task FindNotificationsToProcessAsync(TextWriter log)
{
  "schedule": "0 */15 * * * *"
}

When the trigger from settings.job is fired, the WebJob is executed and any functions you have defined can then be called to run. In the above example I used the NoAutomaticTrigger attribute on the function and then would call the function from program.cs in the WebJob.

WebJob Scaling

Since WebJobs are tied directly to App Service instances, they automatically scale with the number of App Service instances you have deployed. For example if you have 1 App Service instance for your web app, there will be just 1 instance of the WebJob. If you have multiple instances or multiple deployment slots, then you have increased the number of WebJob instances at a 1:1 ratio with App Service instances.

There is an additional level of scaling that can occur inside continuous queue triggered WebJob functions: the batch size. When the WebJob SDK grabs messages from the queue for processing, it grabs a chunk of messages at a time (defaults to 16). You can change this setting up or down based on your needs. It grabs these messages and processes them in parallel.

Since you can have many WebJobs deployed to a single app service instance, you can also consider breaking up a single large WebJob into multiple smaller WebJobs. Or spreading WebJobs across different Web Apps or Deployment Slots.

WebJob Singletons

There are plenty of reasons you wouldn’t want to a WebJob to scale up with your instance count. For this reason we have the is_singleton property in the settings.job file. Set this value to true and it will ensure the Web App only runs a single instance of your WebJob.

When designing a new WebJob, think about what would happen to the job should there by more than one instance running at the same time. Write your WebJob code with parallel execution in mind. Thankfully for queue message triggers there is some built-in safety:

If your web app runs on multiple instances, a continuous WebJob runs on each machine, and each machine will wait for triggers and attempt to run functions. The WebJobs SDK queue trigger automatically prevents a function from processing a queue message multiple times; functions do not have to be written to be idempotent

You can also set the batch size to 1 to avoid parallel execution on continuous queue triggered jobs.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s