1
votes

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.

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.

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();
        }
    }

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))
            {
                try
                {
                    Console.WriteLine("Calling the url " + CheckPendingFulfillmentsUrl);
                    await httpClient.GetAsync(CheckPendingFulfillmentsUrl);
                }
                catch(Exception Ex)
                {
                    Console.WriteLine("Error -- "+ Ex.Message);
                }
                logger.LogInformation(string.Format(Properties.Resources.Info_CheckPendingFulfillmentsTriggered, CheckPendingFulfillmentsUrl));
            }
        }
    }
}
2
Do all the Webjobs use the same storage account or does every environment have their own?Repcak
They all have same storageAmit Kushwaha
Could you please give an update if the problem was solved and if the communities comments helped you?Repcak

2 Answers

0
votes

When you create a new slot, you will get a new App services(slot).

enter image description here

This means that no matter how many slots you create, they will all be independent. The execution of webjob should be independent of each other, and they will all run.

This is my webapp production slot. enter image description here

Create a new slot which name is Test1. I believe the execution of webjob has nothing to do with TRAFFIC %.

And this my test1 slot.

enter image description here

Below is my Test1 running output.

enter image description here

TroubleShooting:

  1. Create a demo webjob like my test code.

enter image description here

  1. Modify Settings.job file.

enter image description here

  1. Check logs.

Test1 slot.

enter image description here

Production slot.

enter image description here

In conclusion:

  1. Each slot is equivalent to a brand new app services.

  2. The execution of webjob will not interfere with each other and will be executed. (Not disturbed by the percentage of traffic)

  3. The essence of slot should be used for switching between development environment and production environment for hot deployment.

  4. If you want to execute webjob alone or execute webjob together, you can execute it through the configuration file or reading the database value.

Example:

In the configuration file of webjob, the name of the current webjob is test1. The others are test2, test3.

Assuming that the time of the timetrigger is set to 1min, then a table named Table_ExecutionSchedule in the database can be read every minute. When the program reads a specific instruction such as test1-start, the program executes a specific business function. Otherwise skip this trigger condition.

0
votes

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";