Example #1
0
        public async Task TraceOutputsMessagesWhenLeaseRenewalFails()
        {
            string hostId          = Guid.NewGuid().ToString();
            string instanceId      = Guid.NewGuid().ToString();
            var    traceWriter     = new TestTraceWriter(TraceLevel.Verbose);
            var    renewResetEvent = new ManualResetEventSlim();

            var blobMock = new Mock <ICloudBlob>();

            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny <TimeSpan>(), It.IsAny <string>()))
            .Returns(() => Task.FromResult(hostId));

            blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny <AccessCondition>()))
            .Returns(() => Task.FromException(new StorageException(new RequestResult {
                HttpStatusCode = 409
            }, "test", null)))
            .Callback(() => renewResetEvent.Set());

            using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter, null))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));
                await TestHelpers.Await(() => traceWriter.Traces.Count == 2, 5000, 500);
            }

            TraceEvent acquisitionEvent = traceWriter.Traces.First();

            Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message);
            Assert.Equal(TraceLevel.Info, acquisitionEvent.Level);

            TraceEvent renewalEvent = traceWriter.Traces.Skip(1).First();
            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.";

            Assert.True(Regex.IsMatch(renewalEvent.Message, pattern), $"Expected trace event {pattern} not found.");
            Assert.Equal(TraceLevel.Info, renewalEvent.Level);
        }
        public async Task TraceOutputsMessagesWhenLeaseRenewalFails()
        {
            string hostId          = Guid.NewGuid().ToString();
            string instanceId      = Guid.NewGuid().ToString();
            var    traceWriter     = new TestTraceWriter(TraceLevel.Verbose);
            var    renewResetEvent = new ManualResetEventSlim();

            var blobMock = new Mock <ICloudBlob>();

            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny <TimeSpan>(), It.IsAny <string>()))
            .Returns(() => Task.FromResult(hostId));

            blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny <AccessCondition>()))
            .Returns(() => Task.FromException(new StorageException(new RequestResult {
                HttpStatusCode = 409
            }, "test", null)))
            .Callback(() => renewResetEvent.Set());

            using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));
                // Make sure we have enough time to trace the renewal
                await TestHelpers.Await(() => traceWriter.Traces.Count == 2, 5000, 500);
            }

            TraceEvent acquisitionEvent = traceWriter.Traces.First();

            Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message);
            Assert.Equal(TraceLevel.Info, acquisitionEvent.Level);

            TraceEvent renewalEvent = traceWriter.Traces.Skip(1).First();

            Assert.Contains("Failed to renew host lock lease", renewalEvent.Message);
            Assert.Equal(TraceLevel.Info, renewalEvent.Level);
        }
        public async Task Dispose_ReleasesBlobLease()
        {
            string hostId           = Guid.NewGuid().ToString();
            string instanceId       = Guid.NewGuid().ToString();
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);

            var traceWriter = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose);

            using (var manager = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter))
            {
                await TestHelpers.Await(() => manager.HasLease);
            }

            ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId);

            string leaseId = null;

            try
            {
                // Acquire a lease on the host lock blob
                leaseId = await blob.AcquireLeaseAsync(TimeSpan.FromSeconds(15));

                await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = leaseId });
            }
            catch (StorageException exc) when(exc.RequestInformation.HttpStatusCode == 409)
            {
            }

            Assert.False(string.IsNullOrEmpty(leaseId), "Failed to acquire a blob lease. The lease was not properly released.");

            await ClearLeaseBlob(hostId);
        }
        public void AcquiringLease_WithServerError_LogsAndRetries()
        {
            string hostId          = Guid.NewGuid().ToString();
            string instanceId      = Guid.NewGuid().ToString();
            var    traceWriter     = new TestTraceWriter(TraceLevel.Verbose);
            var    renewResetEvent = new ManualResetEventSlim();

            var results = new Queue <Task <string> >();

            results.Enqueue(Task.FromException <string>(new StorageException(new RequestResult {
                HttpStatusCode = 500
            }, "test", null)));
            results.Enqueue(Task.FromResult(hostId));

            var blobMock = new Mock <ICloudBlob>();

            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny <TimeSpan>(), It.IsAny <string>()))
            .Returns(() => results.Dequeue());

            blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny <AccessCondition>()))
            .Returns(() => Task.Delay(1000))
            .Callback(() => renewResetEvent.Set());

            BlobLeaseManager manager;

            using (manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));
            }

            Assert.True(renewResetEvent.IsSet);
            Assert.True(manager.HasLease);
            Assert.True(traceWriter.Traces.Any(t => t.Message.Contains("Server error")));
        }
        public async Task TraceOutputsMessagesWhenLeaseIsAcquired()
        {
            string hostId          = Guid.NewGuid().ToString();
            string instanceId      = Guid.NewGuid().ToString();
            var    traceWriter     = new TestTraceWriter(TraceLevel.Verbose);
            var    renewResetEvent = new ManualResetEventSlim();

            var blobMock = new Mock <ICloudBlob>();

            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny <TimeSpan>(), It.IsAny <string>()))
            .Returns(() => Task.FromResult(hostId));

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

                // Make sure we have enough time to trace the renewal
                await TestHelpers.Await(() => traceWriter.Traces.Count == 1, 5000, 500);
            }

            TraceEvent acquisitionEvent = traceWriter.Traces.First();

            Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message);
            Assert.Equal(TraceLevel.Info, acquisitionEvent.Level);
        }
        public async Task HasLease_WhenLeaseIsAcquired_ReturnsTrue()
        {
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
            string hostId           = Guid.NewGuid().ToString();
            string instanceId       = Guid.NewGuid().ToString();
            var    traceWriter      = new TestTraceWriter(System.Diagnostics.TraceLevel.Verbose);

            using (var manager = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter))
            {
                await TestHelpers.Await(() => manager.HasLease);

                Assert.Equal(instanceId, manager.LeaseId);
            }

            await ClearLeaseBlob(hostId);
        }
        private static async Task <ICloudBlob> GetLockBlobAsync(string accountConnectionString, string hostId)
        {
            CloudStorageAccount account = CloudStorageAccount.Parse(accountConnectionString);
            CloudBlobClient     client  = account.CreateCloudBlobClient();

            var container = client.GetContainerReference(BlobLeaseManager.HostContainerName);

            await container.CreateIfNotExistsAsync();

            CloudBlockBlob blob = container.GetBlockBlobReference(BlobLeaseManager.GetBlobName(hostId));

            if (!await blob.ExistsAsync())
            {
                await blob.UploadFromStreamAsync(new MemoryStream());
            }

            return(blob);
        }
        public async Task DifferentHosts_UsingSameStorageAccount_CanObtainLease()
        {
            string hostId1          = Guid.NewGuid().ToString();
            string hostId2          = Guid.NewGuid().ToString();
            string instanceId       = Guid.NewGuid().ToString();
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
            var    traceWriter      = new TestTraceWriter(TraceLevel.Verbose);

            using (var manager1 = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId1, instanceId, traceWriter))
                using (var manager2 = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId2, instanceId, traceWriter))
                {
                    Task manager1Check = TestHelpers.Await(() => manager1.HasLease);
                    Task manager2Check = TestHelpers.Await(() => manager2.HasLease);

                    await Task.WhenAll(manager1Check, manager2Check);
                }

            await Task.WhenAll(ClearLeaseBlob(hostId1), ClearLeaseBlob(hostId2));
        }
        public async Task HasLeaseChanged_WhenLeaseIsLostAndStateChanges_IsFired()
        {
            string     hostId           = Guid.NewGuid().ToString();
            string     instanceId       = Guid.NewGuid().ToString();
            string     connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
            ICloudBlob blob             = await GetLockBlobAsync(connectionString, hostId);

            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var resetEvent  = new ManualResetEventSlim();

            BlobLeaseManager manager     = null;
            string           tempLeaseId = null;

            using (manager = new BlobLeaseManager(blob, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter, TimeSpan.FromSeconds(3)))
            {
                try
                {
                    await TestHelpers.Await(() => manager.HasLease);

                    manager.HasLeaseChanged += (s, a) => resetEvent.Set();

                    // Release the manager's lease and acquire one with a different id
                    await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = manager.LeaseId });

                    tempLeaseId = await blob.AcquireLeaseAsync(TimeSpan.FromSeconds(30), Guid.NewGuid().ToString());
                }
                finally
                {
                    if (tempLeaseId != null)
                    {
                        await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = tempLeaseId });
                    }
                }

                resetEvent.Wait(TimeSpan.FromSeconds(15));
            }

            Assert.True(resetEvent.IsSet);
            Assert.False(manager.HasLease, $"{nameof(BlobLeaseManager.HasLease)} was not correctly set to 'false' when lease lost.");

            await ClearLeaseBlob(hostId);
        }
        public async Task Renew_WhenBlobIsDeleted_RecreatesBlob()
        {
            string hostId           = Guid.NewGuid().ToString();
            string instanceId       = Guid.NewGuid().ToString();
            var    traceWriter      = new TestTraceWriter(TraceLevel.Verbose);
            var    renewResetEvent  = new ManualResetEventSlim();
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);

            ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId);

            var blobMock = new Mock <ICloudBlob>();

            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny <TimeSpan>(), It.IsAny <string>()))
            .Returns(() => Task.FromResult(hostId));

            blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny <AccessCondition>()))
            .Returns(() => Task.FromException <string>(new StorageException(new RequestResult {
                HttpStatusCode = 404
            }, "test", null)))
            .Callback(() => Task.Delay(1000).ContinueWith(t => renewResetEvent.Set()));

            blobMock.SetupGet(b => b.ServiceClient).Returns(blob.ServiceClient);

            // Delete the blob
            await blob.DeleteIfExistsAsync();

            using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter, TimeSpan.FromSeconds(3)))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));

                await TestHelpers.Await(() => manager.HasLease);
            }

            bool blobExists = await blob.ExistsAsync();

            Assert.True(renewResetEvent.IsSet);
            Assert.True(blobExists);

            await ClearLeaseBlob(hostId);
        }
        public async Task HasLeaseChanged_WhenLeaseIsAcquiredAndStateChanges_IsFired()
        {
            string hostId      = Guid.NewGuid().ToString();
            string instanceId  = Guid.NewGuid().ToString();
            var    traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var    resetEvent  = new ManualResetEventSlim();

            string     connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
            ICloudBlob blob             = await GetLockBlobAsync(connectionString, hostId);

            // Acquire a lease on the host lock blob
            string leaseId = await blob.AcquireLeaseAsync(TimeSpan.FromSeconds(15));

            BlobLeaseManager manager = null;

            try
            {
                manager = await BlobLeaseManager.CreateAsync(connectionString, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter);

                manager.HasLeaseChanged += (s, a) => resetEvent.Set();
            }
            finally
            {
                await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = leaseId });
            }

            resetEvent.Wait(TimeSpan.FromSeconds(15));
            bool   hasLease      = manager.HasLease;
            string actualLeaseId = manager.LeaseId;

            manager.Dispose();

            Assert.True(resetEvent.IsSet);
            Assert.True(hasLease, $"{nameof(BlobLeaseManager.HasLease)} was not correctly set to 'true' when lease was acquired.");
            Assert.Equal(instanceId, actualLeaseId);

            await ClearLeaseBlob(hostId);
        }
        public async Task HasLeaseChanged_WhenLeaseIsLostAndStateChanges_IsFired()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
            ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId);

            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var resetEvent = new ManualResetEventSlim();

            BlobLeaseManager manager = null;
            string tempLeaseId = null;

            using (manager = new BlobLeaseManager(blob, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter, TimeSpan.FromSeconds(3)))
            {
                try
                {
                    await TestHelpers.Await(() => manager.HasLease);

                    manager.HasLeaseChanged += (s, a) => resetEvent.Set();

                    // Release the manager's lease and acquire one with a different id
                    await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = manager.LeaseId });
                    tempLeaseId = await blob.AcquireLeaseAsync(TimeSpan.FromSeconds(30), Guid.NewGuid().ToString());
                }
                finally
                {
                    if (tempLeaseId != null)
                    {
                        await blob.ReleaseLeaseAsync(new AccessCondition { LeaseId = tempLeaseId });
                    }
                }

                resetEvent.Wait(TimeSpan.FromSeconds(15));
            }

            Assert.True(resetEvent.IsSet);
            Assert.False(manager.HasLease, $"{nameof(BlobLeaseManager.HasLease)} was not correctly set to 'false' when lease lost.");

            await ClearLeaseBlob(hostId);
        }
        public async Task TraceOutputsMessagesWhenLeaseRenewalFails()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var renewResetEvent = new ManualResetEventSlim();

            var blobMock = new Mock<ICloudBlob>();
            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>()))
                .Returns(() => Task.FromResult(hostId));

            blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny<AccessCondition>()))
                .Returns(() => Task.FromException(new StorageException(new RequestResult { HttpStatusCode = 409 }, "test", null)))
                .Callback(() => renewResetEvent.Set());

            using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));
                // Make sure we have enough time to trace the renewal
                await TestHelpers.Await(() => traceWriter.Traces.Count == 2, 5000, 500);
            }

            TraceEvent acquisitionEvent = traceWriter.Traces.First();
            Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message);
            Assert.Equal(TraceLevel.Info, acquisitionEvent.Level);

            TraceEvent renewalEvent = traceWriter.Traces.Skip(1).First();            
            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.";
            Assert.True(Regex.IsMatch(renewalEvent.Message, pattern), $"Expected trace event {pattern} not found.");            
            Assert.Equal(TraceLevel.Info, renewalEvent.Level);
        }
        public async Task TraceOutputsMessagesWhenLeaseIsAcquired()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var renewResetEvent = new ManualResetEventSlim();

            var blobMock = new Mock<ICloudBlob>();
            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>()))
                .Returns(() => Task.FromResult(hostId));            

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

                // Make sure we have enough time to trace the renewal
                await TestHelpers.Await(() => traceWriter.Traces.Count == 1, 5000, 500);
            }

            TraceEvent acquisitionEvent = traceWriter.Traces.First();
            Assert.Contains($"Host lock lease acquired by instance ID '{instanceId}'.", acquisitionEvent.Message);
            Assert.Equal(TraceLevel.Info, acquisitionEvent.Level);
        }      
        public async Task Renew_WhenBlobIsDeleted_RecreatesBlob()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var renewResetEvent = new ManualResetEventSlim();
            string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);

            ICloudBlob blob = await GetLockBlobAsync(connectionString, hostId);

            var blobMock = new Mock<ICloudBlob>();
            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>()))
                .Returns(() => Task.FromResult(hostId));

            blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny<AccessCondition>()))
                .Returns(() => Task.FromException<string>(new StorageException(new RequestResult { HttpStatusCode = 404 }, "test", null)))
                .Callback(() => Task.Delay(1000).ContinueWith(t => renewResetEvent.Set()));

            blobMock.SetupGet(b => b.ServiceClient).Returns(blob.ServiceClient);

            // Delete the blob
            await blob.DeleteIfExistsAsync();

            using (var manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(15), hostId, instanceId, traceWriter, TimeSpan.FromSeconds(3)))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));

                await TestHelpers.Await(() => manager.HasLease);
            }

            bool blobExists = await blob.ExistsAsync();

            Assert.True(renewResetEvent.IsSet);
            Assert.True(blobExists);

            await ClearLeaseBlob(hostId);
        }
        public void AcquiringLease_WithServerError_LogsAndRetries()
        {
            string hostId = Guid.NewGuid().ToString();
            string instanceId = Guid.NewGuid().ToString();
            var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
            var renewResetEvent = new ManualResetEventSlim();

            var results = new Queue<Task<string>>();
            results.Enqueue(Task.FromException<string>(new StorageException(new RequestResult { HttpStatusCode = 500 }, "test", null)));
            results.Enqueue(Task.FromResult(hostId));

            var blobMock = new Mock<ICloudBlob>();
            blobMock.Setup(b => b.AcquireLeaseAsync(It.IsAny<TimeSpan>(), It.IsAny<string>()))
                .Returns(() => results.Dequeue());

            blobMock.Setup(b => b.RenewLeaseAsync(It.IsAny<AccessCondition>()))
                .Returns(() => Task.Delay(1000))
                .Callback(() => renewResetEvent.Set());

            BlobLeaseManager manager;
            using (manager = new BlobLeaseManager(blobMock.Object, TimeSpan.FromSeconds(5), hostId, instanceId, traceWriter))
            {
                renewResetEvent.Wait(TimeSpan.FromSeconds(10));
            }

            Assert.True(renewResetEvent.IsSet);
            Assert.True(manager.HasLease);
            Assert.True(traceWriter.Traces.Any(t => t.Message.Contains("Server error")));
        }