TimerTriggered Azure Webjob in different deployment slots

Kushwaha, Amit 1 Reputation point
2020-09-29T03:49:23.703+00:00

I have website on azure app service running in 5 different deployment slots(Dev, UAT, Staging, Prelive-1, Prelive-2), each of which containing a timer triggered azure webjob written in .NET Core 2.2(properly placed at the right place in App_Data). Each Webjob contains an appsettings.json file which has different URLs to hit.

The webjob runs in every 5 mins and hits the url from the appsettings, but when I check logs in azure insights(configured properly) I see only one of the deployment slot is running(90% of the times its the first deployment slot -- "DEV" one). I want to make all the webjob run in all the deployment slots.

They all have same storage and app insights configured.

One more problem Let say if I restart any deployment slot e.g. Prelive-1 then instantly the webjobs execute and after 5 mins its doesn't. I can only see the processing being done in DEV but not in others

Is there anyway to make them run all at once or one by one whatever but I just want to make all the deployments slots to run the webjob in every 5 mins.

Below is my Program.cs

public class Program
{
    public static async Task Main()
    {
        HostBuilder hostBuilder = new HostBuilder();
        hostBuilder.ConfigureWebJobs(builder =>
        {
            builder.AddAzureStorageCoreServices();
            builder.AddAzureStorage();
            builder.AddTimers();
        })
        .ConfigureAppConfiguration((hostContext, configurationBuilder) =>
        { 
            configurationBuilder.AddJsonFile($"appsettings.json", optional: false, reloadOnChange: false);
        })
        .ConfigureLogging((context, loggingBuilder) =>
        {
            string instrumentationKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
            if (!string.IsNullOrEmpty(instrumentationKey))
                loggingBuilder.AddApplicationInsightsWebJobs(logBuilder => logBuilder.InstrumentationKey = instrumentationKey);
        })
        .ConfigureServices(services =>
        {
            services.AddHttpClient();
        })
        .UseConsoleLifetime();



        using (var host = hostBuilder.Build())
        {
            await host.RunAsync();
        }
    }

Below is my Function.cs

public class Function1
    {
        const string CheckPendingFulfillmentsUrlConfigKey = "CheckPendingFulfillmentsUrl";
        const string WebJobTimerExpression = "0 */5 * * * *";

        readonly IHttpClientFactory httpClientFactory;
        readonly ILogger<Function1> logger;
        readonly string CheckPendingFulfillmentsUrl;

        public Function1(IHttpClientFactory HttpClientFactory, ILogger<Function1> Logger, IConfiguration config)
        {
            httpClientFactory = HttpClientFactory;
            logger = Logger;
            CheckPendingFulfillmentsUrl = config[CheckPendingFulfillmentsUrlConfigKey];
        }

        public async Task TriggerCheckPendingFulfillments([TimerTrigger(WebJobTimerExpression, RunOnStartup = false)] TimerInfo timerInfo, CancellationToken cancellationToken)
        {
            using (var httpClient = httpClientFactory.CreateClient())
            {
                if (!string.IsNullOrEmpty(CheckPendingFulfillmentsUrl))
                {
                    await httpClient.GetAsync(CheckPendingFulfillmentsUrl);
                    logger.LogInformation(string.Format(Properties.Resources.Info_CheckPendingFulfillmentsTriggered, CheckPendingFulfillmentsUrl));
                }
            }
        }
    }
Azure App Service
Azure App Service
Azure App Service is a service used to create and deploy scalable, mission-critical web apps.
7,909 questions
{count} votes

1 answer

Sort by: Most helpful
  1. ajkuma 26,721 Reputation points Microsoft Employee
    2020-11-03T04:22:56.663+00:00

    To benefit the community, updating the Answer (SO -from 'Repcak')

    This could be caused because you use the same storage for all those Webjobs.
    From the documentation:

    Behind the scenes, TimerTrigger uses the Singleton feature of the WebJobs SDK to ensure that only a single instance of your triggered function is running at any given time. When the JobHost starts up, for each of your TimerTrigger functions a blob lease (the Singleton Lock) is taken. This distrubuted lock ensures that only a single instance of your scheduled function is running at any time. If the blob for that function is not currently leased, the function will acquire the lease and start running on schedule immediately. If the blob lease cannot be acquired, it generally means that another instance of that function is running, so the function is not started in the current host. When this happens, the host will continue to periodically check to see if it can acquire the lease. This is done as a sort of "recovery" mode to ensure that when running steady state, if an instance goes down, another instance notice and pick up where the other left off.

    So there are two possibilities:

    • Having separated storage accounts: makes sense if you have a storage account per environment (dev/staging/prod)
    • Specifying the HosId property of the JobHostConfiguration class:
      var config = new JobHostConfiguration();
      config.HostId = "dev|staging|prod";
      
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.