Skip to content

Commit 20e6fba

Browse files
committed
reducing host lease logs; adding debugging details on failure
1 parent 67fcd7b commit 20e6fba

File tree

2 files changed

+28
-41
lines changed

2 files changed

+28
-41
lines changed

src/WebJobs.Script/Host/BlobLeaseManager.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Globalization;
56
using System.IO;
67
using System.Threading;
78
using System.Threading.Tasks;
@@ -27,6 +28,8 @@ internal sealed class BlobLeaseManager : IDisposable
2728
private string _leaseId;
2829
private bool _disposed;
2930
private bool _processingLease;
31+
private DateTime _lastRenewal;
32+
private TimeSpan _lastRenewalLatency;
3033

3134
internal BlobLeaseManager(ICloudBlob lockBlob, TimeSpan leaseTimeout, string hostId, string instanceId, TraceWriter traceWriter, TimeSpan? renewalInterval = null)
3235
{
@@ -115,25 +118,40 @@ private async Task AcquireOrRenewLeaseAsync()
115118
{
116119
try
117120
{
121+
DateTime requestStart = DateTime.UtcNow;
118122
if (HasLease)
119123
{
120124
await _lockBlob.RenewLeaseAsync(new AccessCondition { LeaseId = LeaseId });
121-
_traceWriter.Verbose("Host lock lease renewed.");
125+
_lastRenewal = DateTime.UtcNow;
126+
_lastRenewalLatency = _lastRenewal - requestStart;
122127
}
123128
else
124129
{
125130
LeaseId = await _lockBlob.AcquireLeaseAsync(_leaseTimeout, _instanceId);
131+
_lastRenewal = DateTime.UtcNow;
132+
_lastRenewalLatency = _lastRenewal - requestStart;
133+
126134
_traceWriter.Info($"Host lock lease acquired by instance ID '{_instanceId}'.");
127135

128136
// We've successfully acquired the lease, change the timer to use our renewal interval
129137
SetTimerInterval(_renewalInterval);
130138
}
131139
}
132140
catch (StorageException exc)
133-
{
141+
{
134142
if (exc.RequestInformation.HttpStatusCode == 409)
135143
{
136-
ProcessLeaseError("Another host has an active lease.");
144+
// If we did not have the lease already, a 409 indicates that another host had it. This is
145+
// normal and does not warrant any logging.
146+
147+
if (HasLease)
148+
{
149+
// The lease was 'stolen'. Log details for debugging.
150+
string lastRenewalFormatted = _lastRenewal.ToString("yyyy-MM-ddTHH:mm:ss.FFFZ", CultureInfo.InvariantCulture);
151+
int millisecondsSinceLastSuccess = (int)(DateTime.UtcNow - _lastRenewal).TotalMilliseconds;
152+
int lastRenewalMilliseconds = (int)_lastRenewalLatency.TotalMilliseconds;
153+
ProcessLeaseError($"Another host has acquired the lease. The last successful renewal completed at {lastRenewalFormatted} ({millisecondsSinceLastSuccess} milliseconds ago) with a duration of {lastRenewalMilliseconds} milliseconds.");
154+
}
137155
}
138156
else if (exc.RequestInformation.HttpStatusCode >= 500)
139157
{

test/WebJobs.Script.Tests/BlobLeaseManagerTests.cs

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Diagnostics;
77
using System.IO;
88
using System.Linq;
9+
using System.Text.RegularExpressions;
910
using System.Threading;
1011
using System.Threading.Tasks;
1112
using Microsoft.Azure.WebJobs.Host;
@@ -235,53 +236,20 @@ public async Task TraceOutputsMessagesWhenLeaseIsAcquired()
235236

236237
var blobMock = new Mock<ICloudBlob>();
237238
blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>()))
238-
.Returns(() => Task.FromResult(hostId));
239-
240-
blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny<AccessCondition>()))
241-
.Returns(() => Task.Delay(10).ContinueWith(t => renewResetEvent.Set()));
239+
.Returns(() => Task.FromResult(hostId));
242240

243241
using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter))
244242
{
245243
renewResetEvent.Wait(TimeSpan.FromSeconds(10));
246244

247245
// Make sure we have enough time to trace the renewal
248-
await TestHelpers.Await(() => traceWriter.Traces.Count == 2, 5000, 500);
246+
await TestHelpers.Await(() => traceWriter.Traces.Count == 1, 5000, 500);
249247
}
250248

251249
TraceEvent acquisitionEvent = traceWriter.Traces.First();
252250
Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message);
253251
Assert.Equal(TraceLevel.Info, acquisitionEvent.Level);
254-
255-
TraceEvent renewalEvent = traceWriter.Traces.Skip(1).First();
256-
Assert.Contains("Host lock lease renewed.", renewalEvent.Message);
257-
Assert.Equal(TraceLevel.Verbose, renewalEvent.Level);
258-
}
259-
260-
[Fact]
261-
public async Task TraceOutputsMessagesWhenLeaseAcquisitionFails()
262-
{
263-
string hostId = Guid.NewGuid().ToString();
264-
string instanceId = Guid.NewGuid().ToString();
265-
var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
266-
var acquisitionResetEvent = new ManualResetEventSlim();
267-
268-
var blobMock = new Mock<ICloudBlob>();
269-
blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>()))
270-
.Returns(() => Task.FromException<string>(new StorageException(new RequestResult { HttpStatusCode = 409 }, "test", null)))
271-
.Callback(() => acquisitionResetEvent.Set());
272-
273-
using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(10), hostId, instanceId, traceWriter))
274-
{
275-
acquisitionResetEvent.Wait(TimeSpan.FromSeconds(5));
276-
}
277-
278-
// Make sure we have enough time to trace the renewal
279-
await TestHelpers.Await(() => traceWriter.Traces.Count == 1, 5000, 500);
280-
281-
TraceEvent acquisitionEvent = traceWriter.Traces.First();
282-
Assert.Contains($"Host instance '{instanceId}' failed to acquire host lock lease: Another host has an active lease.", acquisitionEvent.Message);
283-
Assert.Equal(TraceLevel.Verbose, acquisitionEvent.Level);
284-
}
252+
}
285253

286254
[Fact]
287255
public async Task TraceOutputsMessagesWhenLeaseRenewalFails()
@@ -310,8 +278,9 @@ public async Task TraceOutputsMessagesWhenLeaseRenewalFails()
310278
Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message);
311279
Assert.Equal(TraceLevel.Info, acquisitionEvent.Level);
312280

313-
TraceEvent renewalEvent = traceWriter.Traces.Skip(1).First();
314-
Assert.Contains("Failed to renew host lock lease", renewalEvent.Message);
281+
TraceEvent renewalEvent = traceWriter.Traces.Skip(1).First();
282+
string pattern = @"Failed to renew host lock lease: Another host has acquired the lease. The last successful renewal completed at (.+) \([0-9]+ milliseconds ago\) with a duration of [0-9]+ milliseconds.";
283+
Assert.True(Regex.IsMatch(renewalEvent.Message, pattern), $"Expected trace event {pattern} not found.");
315284
Assert.Equal(TraceLevel.Info, renewalEvent.Level);
316285
}
317286

0 commit comments

Comments
 (0)