Skip to content

Commit f6f5fb3

Browse files
committed
Chance of getting 409 while renewing the primary host lock. Fixes #1864
1 parent b52bc63 commit f6f5fb3

File tree

2 files changed

+45
-5
lines changed

2 files changed

+45
-5
lines changed

src/WebJobs.Script/Host/PrimaryHostCoordinator.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ internal sealed class PrimaryHostCoordinator : IDisposable
3636

3737
private IDistributedLockManager _lockManager;
3838

39+
private static PrimaryHostCoordinator latestCreatedInstance;
40+
private static object syncLock = new object();
41+
3942
internal PrimaryHostCoordinator(IDistributedLockManager lockManager, TimeSpan leaseTimeout, string hostId, string instanceId, TraceWriter traceWriter,
4043
ILoggerFactory loggerFactory, TimeSpan? renewalInterval = null)
4144
{
@@ -100,13 +103,22 @@ public static PrimaryHostCoordinator Create(
100103
throw new ArgumentOutOfRangeException(nameof(leaseTimeout), $"The {nameof(leaseTimeout)} should be between 15 and 60 seconds");
101104
}
102105

103-
var manager = new PrimaryHostCoordinator(lockManager, leaseTimeout, hostId, instanceId, traceWriter, loggerFactory, renewalInterval);
104-
return manager;
106+
lock (syncLock)
107+
{
108+
// Dispose previously created instance to ensure releasing a lease before new instance is created.
109+
if (latestCreatedInstance != null)
110+
{
111+
latestCreatedInstance.Dispose();
112+
}
113+
114+
latestCreatedInstance = new PrimaryHostCoordinator(lockManager, leaseTimeout, hostId, instanceId, traceWriter, loggerFactory, renewalInterval);
115+
}
116+
return latestCreatedInstance;
105117
}
106118

107119
private void ProcessLeaseTimerTick(object state)
108120
{
109-
if (_processingLease)
121+
if (_processingLease && !_disposed)
110122
{
111123
return;
112124
}

test/WebJobs.Script.Tests.Integration/PrimaryHostCoordinatorTests.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ public async Task DifferentHosts_UsingSameStorageAccount_CanObtainLease()
229229
var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
230230

231231
var lockManager = CreateLockManager();
232-
using (var manager1 = PrimaryHostCoordinator.Create(lockManager, TimeSpan.FromSeconds(15), hostId1, instanceId, traceWriter, null))
233-
using (var manager2 = PrimaryHostCoordinator.Create(lockManager, TimeSpan.FromSeconds(15), hostId2, instanceId, traceWriter, null))
232+
using (var manager1 = new PrimaryHostCoordinator(lockManager, TimeSpan.FromSeconds(15), hostId1, instanceId, traceWriter, null))
233+
using (var manager2 = new PrimaryHostCoordinator(lockManager, TimeSpan.FromSeconds(15), hostId2, instanceId, traceWriter, null))
234234
{
235235
Task manager1Check = TestHelpers.Await(() => manager1.HasLease);
236236
Task manager2Check = TestHelpers.Await(() => manager2.HasLease);
@@ -241,6 +241,34 @@ public async Task DifferentHosts_UsingSameStorageAccount_CanObtainLease()
241241
await Task.WhenAll(ClearLeaseBlob(hostId1), ClearLeaseBlob(hostId2));
242242
}
243243

244+
[Fact]
245+
public async Task Create_LastCreatedInstanceAquiresLease()
246+
{
247+
string hostId = Guid.NewGuid().ToString();
248+
string instanceId = Guid.NewGuid().ToString();
249+
string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
250+
var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
251+
252+
var lockManager = CreateLockManager();
253+
using (var manager = PrimaryHostCoordinator.Create(lockManager, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter, null))
254+
{
255+
Task managerCheck = TestHelpers.Await(() => manager.HasLease);
256+
await Task.WhenAll(managerCheck);
257+
}
258+
259+
var manager1 = PrimaryHostCoordinator.Create(lockManager, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter, null);
260+
Task manager1Check = TestHelpers.Await(() => manager1.HasLease);
261+
await Task.WhenAll(manager1Check);
262+
263+
var manager2 = PrimaryHostCoordinator.Create(lockManager, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter, null);
264+
Task manager2Check = TestHelpers.Await(() => manager2.HasLease);
265+
await Task.WhenAll(manager2Check);
266+
Assert.True(traceWriter.Traces[traceWriter.Traces.Count() - 2].Message.Contains($"Host instance '{instanceId}' released lock lease"));
267+
Assert.True(traceWriter.Traces.Last().Message.Contains($"Host lock lease acquired by instance ID '{instanceId}'"));
268+
269+
await Task.WhenAll(ClearLeaseBlob(hostId));
270+
}
271+
244272
private static async Task<ICloudBlob> GetLockBlobAsync(string accountConnectionString, string hostId)
245273
{
246274
CloudStorageAccount account = CloudStorageAccount.Parse(accountConnectionString);

0 commit comments

Comments
 (0)