简体   繁体   中英

Singleton Lock for Azure Webjob's TimerTrigger accross multiple regions

I have a timer triggered webjob deployed across multiple regions and it is getting triggering concurrently from all regions on given scheduled time, how do I make sure that only one instance of the job runs at a time. I tried applying Singleton attribute and "is_singleton": true but still it is triggering from all regions.

Is there any other way to achieve this. This link says that Singleton attribute no longer works for this purpose and also I don't see any lock file created in the azure blob storage. If its true how do we implement this to make sure only one region is triggered from multiple regions. Or If there is any other inbuilt way of achieving this with webjob sdk that would be really helpful to me

My program.cs

var builder = new HostBuilder();
builder
    .ConfigureWebJobs((context, b) =>
    {
        b.AddAzureStorageCoreServices();
    });
var host = builder.Build();
using (host)
{
    var jobHost = host.Services.GetService(typeof(IJobHost)) as JobHost;
    await host.StartAsync().ConfigureAwait(false);
    await jobHost.CallAsync("Run").ConfigureAwait(false);
    await host.StopAsync().ConfigureAwait(false);
}

Function.cs

[Singleton]
[NoAutomaticTrigger]
public async Task Run()
{
}

settings.job

{
  "schedule": "0 */5 * * * *",
  "is_singleton": true
}

nuget package

<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="4.0.1" />

So it sounds like you want each region to run, but just not at the same time.

One simple solution would be to stagger them so they don't all run at the same time but you give each one a slightly different cron time, such as 10m apart from each other.

But supposing you couldn't predict how long they will run or you essentially want them to run much faster then the minimum 10m apart, and supposing they could all access a database, such a Azure Cosmos Mongo, then you could add a table and some simple logic which would essentially model a "locking" pattern.

In mongodb you can use the findOneAndUpdate function to do an atomic update on a well known document which will allow only one process to "lock" a document at a time.

The database in this case contains a collection (aka table) with a single document (aka row) that looks like this:

interface ILock {
  state: 'UNLOCKED' | 'LOCKED';
  lockedBy: null | string;
  lockedAt: null | Date;
}

psuedo code

while (true)
{
  // todo: add some kind of timeout here so it doesn't run forever.

  // `findOneAndUpdate` is atomic, if multiple processes
  // attempt to do this modification simultaneously
  // only one will succeed, the others will get an `undefined`
  // result to indicate the document was not found.
  var lock = await this.db.locks.findOneAndUpdate(
    { state: 'UNLOCKED' },
    {
      $set: {
        state: 'LOCKED',
        lockedBy: this.jobId,
        lockedAt: new Date()
      }
    }
  )

  if (lock) {
    try {
      // you have the lock, do your thing...
      await DoWork();
      // you are done, exit the loop.
      return;
    } finally {
      // don't forget to unlock!
      await this.db.locks.findOneAndUpdate(
        { state: 'LOCKED' },
        {
          $set: {
            state: 'UNLOCKED',
          }
        }
      )
    }
  } else {
    // you are not the one neo, take the blue pill...
    await sleep(3000)
  }
  
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM