If I run my code (simplified below) with two different instances locally (dotnet run or VS for the first and dotnet run --no-build for the second) and restart my db a couple of times odds are quite high that eventually an instance will just keep going without the lock (in the while-loop). It could be as few restarts as 3-4 restarts. My understanding where that when you access handle.HandleLostToken.IsCancellationRequested it will make sure that the lock still exists? Or is that not working together with CancellationTokenSource.CreateLinkedTokenSource? I just tried without CancellationTokenSource.CreateLinkedTokenSource and get the same result. Not sure what is going on...
usingMedallion.Threading;namespaceSample;publicclassTestBackgroundService:BackgroundService{privatereadonlyIServiceScopeFactory_serviceScopeFactory;privatereadonlyILogger<TestBackgroundService>_logger;publicTestBackgroundService(IServiceScopeFactoryserviceScopeFactory,ILogger<TestBackgroundService>logger){_serviceScopeFactory=serviceScopeFactory;_logger=logger;}protectedoverrideasyncTaskExecuteAsync(CancellationTokenstoppingToken){while(!stoppingToken.IsCancellationRequested){try{awaitusingvarscope=_serviceScopeFactory.CreateAsyncScope();vardistributedLockProvider=scope.ServiceProvider.GetRequiredService<IDistributedLockProvider>();_logger.LogInformation("Will try to acquire lock.");awaitusingvarhandle=awaitdistributedLockProvider.AcquireLockAsync("a1416b2940b34bbb9189caaa13f11b1a",cancellationToken:stoppingToken);_logger.LogInformation("Acquired lock.");handle.HandleLostToken.Register(()=>_logger.LogError("Lost lock for job {Job Name}.",nameof(TestBackgroundService)));varstoppingCts=CancellationTokenSource.CreateLinkedTokenSource(stoppingToken,handle.HandleLostToken);if(stoppingCts.Token.IsCancellationRequested){return;}while(!stoppingCts.IsCancellationRequested)// This evaluates to true sometimes even if the database has been restarted{_logger.LogInformation("Doing stuff.");try{awaitTask.Delay(TimeSpan.FromSeconds(30),stoppingCts.Token);}catch(TaskCanceledException){}}}catch(Exceptionex){_logger.LogError(ex,"Something went wrong.");}}}}
Update: I can replicate it with just one instance (even if the log gets a bit tricker to follow then):
usingMedallion.Threading;namespaceSamples;publicsealedclassJob1:JobBase{publicJob1(IServiceScopeFactoryserviceScopeFactory,ILogger<Job1>logger):base(serviceScopeFactory,logger){}}publicsealedclassJob2:JobBase{publicJob2(IServiceScopeFactoryserviceScopeFactory,ILogger<Job2>logger):base(serviceScopeFactory,logger){}}publicabstractclassJobBase:BackgroundService{privatereadonlyIServiceScopeFactory_serviceScopeFactory;privatereadonlyILogger_logger;publicJobBase(IServiceScopeFactoryserviceScopeFactory,ILoggerlogger){_serviceScopeFactory=serviceScopeFactory;_logger=logger;}protectedoverrideasyncTaskExecuteAsync(CancellationTokenstoppingToken){while(!stoppingToken.IsCancellationRequested){try{awaitusingvarscope=_serviceScopeFactory.CreateAsyncScope();vardistributedLockProvider=scope.ServiceProvider.GetRequiredService<IDistributedLockProvider>();_logger.LogInformation("Will try to acquire lock.");awaitusingvarhandle=awaitdistributedLockProvider.AcquireLockAsync("a1416b2940b34bbb9189caaa13f11b1a",cancellationToken:stoppingToken);_logger.LogInformation("Acquired {CancelableDescription} lock.",handle.HandleLostToken.CanBeCanceled?"cancelable":"uncancelable");awaitusingvar_=handle.HandleLostToken.Register(()=>_logger.LogError("Lost lock."));usingvarstoppingCts=CancellationTokenSource.CreateLinkedTokenSource(stoppingToken,handle.HandleLostToken);if(stoppingCts.Token.IsCancellationRequested){return;}while(!stoppingCts.IsCancellationRequested)// This evaluates to true sometimes even if the database has been restarted{_logger.LogInformation("Doing stuff.");try{awaitTask.Delay(TimeSpan.FromSeconds(30),stoppingCts.Token);}catch(TaskCanceledException){}if(!stoppingToken.IsCancellationRequested){_logger.LogInformation("Cancellation is not requested.");}}}catch(Exceptionexception){_logger.LogError("Exception {Exception} thrown.",exception.GetType());}}}}
If I run my code (simplified below) with two different instances locally (
dotnet runor VS for the first anddotnet run --no-buildfor the second) and restart my db a couple of times odds are quite high that eventually an instance will just keep going without the lock (in the while-loop). It could be as few restarts as 3-4 restarts. My understanding where that when you accesshandle.HandleLostToken.IsCancellationRequestedit will make sure that the lock still exists?Or is that not working together withI just tried withoutCancellationTokenSource.CreateLinkedTokenSource?CancellationTokenSource.CreateLinkedTokenSourceand get the same result. Not sure what is going on...Update: I can replicate it with just one instance (even if the log gets a bit tricker to follow then):