public void Create_NoLeaseExists_LeaseCreated(
     IConcurrencyControlRepository<ElasticLease> repo,
     ElasticLease lease,
     ISystemContext ctx,
     TestIndexUtils contactIndex)
 {
     using (contactIndex)
     {
         var actual = repo.Create(lease, ctx);
         actual.Should().BeTrue();
     }
 }
 public void Create_LeaseAlreadyExists_FalseReturned(
     IConcurrencyControlRepository<ElasticLease> repo,
     ElasticLease lease,
     ISystemContext ctx,
     TestIndexUtils contactIndex)
 {
     using (contactIndex)
     {
         repo.Create(lease, ctx);
         var actual = repo.Create(lease, ctx);
         actual.Should().BeFalse();
     }
 }
 public void TryObtainLock_NoLeaseExists_LockObtained(
     ILeaseService service,
     ISystemContext ctx,
     ElasticLease lease,
     TestIndexUtils contactIndex)
 {
     using (contactIndex)
     {
         ElasticLease winner;
         service.TryObtainLock(lease, ctx, out winner).Should().BeTrue();
         lease.ShouldBeEquivalentTo(winner);
     }
 }
        public virtual bool TryObtainLock(
            ElasticLease challenger, 
            ISystemContext ctx, 
            out ElasticLease winner)
        {
            // NOTE: The approach here is to fail immediately if someone else already has the lock.  Else
            // we try and obtain the lock.  If in the very unlikely/unlucky scenario that a lock is obtained 
            // between the first check and then the grab, we try again (recurse).  The likely hood of many
            // recursions there for is very very unlikely, so there is currently no guard against that.

            var currentLease = this.leaseRepository.Get(challenger.ResourceId, ctx);

            // there is no current lease
            if (currentLease == null)
            {
                var esChallenger = challenger;
                if (this.leaseRepository.Create(esChallenger, ctx)) // 'create only' ES query so will fail if we were beaten to it
                {
                    winner = challenger;
                    return true;
                }
                else
                {
                    // someone just beat us to it, so have another go...
                    return this.TryObtainLock(challenger, ctx, out winner);
                }
            }

            // lease is already held, and not by the challenger...
            if (!currentLease.HasExpired(this.dateTime.UtcNow) && !currentLease.IsOwnedBy(challenger.Owner))
            {
                winner = currentLease;
                return false;    
            }

            // lease has either expired, or is held by the current challenger owner and needs extending...
            if (!currentLease.IsOwnedBy(challenger.Owner) ||
                (currentLease.IsOwnedBy(challenger.Owner) && currentLease.Expires >= challenger.Expires))
            {
                if (!this.leaseRepository.SaveOptimistically(challenger, ctx)) // ES (upsert) using optimistic concurrency, so will fail if we were beaten to it
                {
                    // someone just beat us to it, so have another go...
                    return this.TryObtainLock(challenger, ctx, out winner);
                }
            }

            winner = challenger;
            return true;
        }
        public void TryObtainLock_ActiveLeaseExistsBySameOwner_LockObtained(
            ILeaseService service,
            ISystemContext ctx,
            ElasticLease activeLease,
            TestIndexUtils contactIndex,
            IFixture fixture
            )
        {
            using (contactIndex)
            {
                service.TryObtainLock(activeLease, ctx, out activeLease);

                var activeLease2 = new ElasticLease(
                    activeLease.ResourceId,
                    DateTime.UtcNow + TimeSpan.FromHours(2),
                    activeLease.Owner,
                    activeLease.Version);

                ElasticLease winner;
                service.TryObtainLock(activeLease2, ctx, out winner);
                activeLease2.ShouldBeEquivalentTo(winner);
            }
        }
        public void TryObtainLock_InactiveLeaseExistsByDifferentOwner_LockObtained(
            ILeaseService service,
            ISystemContext ctx,
            ElasticLease owner1InactiveLease,
            TestIndexUtils contactIndex,
            IFixture fixture)
        {
            using (contactIndex)
            {
                service.TryObtainLock(owner1InactiveLease, ctx, out owner1InactiveLease).Should().BeTrue();

                var owner2ActiveLease = new ElasticLease(
                    owner1InactiveLease.ResourceId,
                    DateTime.UtcNow + TimeSpan.FromHours(2),
                    fixture.Create<ElasticLeaseOwner>(),
                    owner1InactiveLease.Version);

                ElasticLease winner;
                service.TryObtainLock(owner2ActiveLease, ctx, out winner).Should().BeTrue();
                owner2ActiveLease.ShouldBeEquivalentTo(winner);
            }
        }
        public void TryObtainLock_ActiveLeaseExistsByDifferentOwner_LockRefused(
            ILeaseService service,
            ISystemContext ctx,
            ElasticLease owner1ActiveLease,
            TestIndexUtils contactIndex,
            IFixture fixture)
        {
            using (contactIndex)
            {
                ElasticLease winner;
                service.TryObtainLock(owner1ActiveLease, ctx, out winner).Should().BeTrue();

                var owner2ActiveLease = new ElasticLease(
                    winner.ResourceId,
                    DateTime.UtcNow + TimeSpan.FromHours(1),
                    fixture.Create<ElasticLeaseOwner>(),
                    winner.Version);

                service.TryObtainLock(owner2ActiveLease, ctx, out winner).Should().BeFalse();
            }
        }
 public void Extend_LeaseExistsVersionNotChanged_LeaseExtended(
     IConcurrencyControlRepository<ElasticLease> repo,
     ElasticLease lease,
     ISystemContext ctx,
     TestIndexUtils contactIndex)
 {
     using (contactIndex)
     {
         repo.Create(lease, ctx);
         var extendedLease = new ElasticLease(
             lease.ResourceId,
             lease.Expires + TimeSpan.FromHours(2),
             lease.Owner,
             lease.Version);
         var actual = repo.SaveOptimistically(extendedLease, ctx);
         actual.Should().BeTrue();
     }
 }
 protected virtual LockAttemptStatus TryLockContact(
     Guid id,
     ElasticLease challengerLease,
     ISystemContext ctx,
     out ElasticLease newLease)
 {
     var status = this.contactLeaseService.TryObtainLock(challengerLease, ctx, out newLease) ?
                                    LockAttemptStatus.Success :
                                    LockAttemptStatus.AlreadyLocked;
     return status;
 }
        public void Extend_InactiveLeaseExistsButLeaseGrabbedByDifferentOwner_LeaseNotExtended(
            IConcurrencyControlRepository<ElasticLease> repo,
            ElasticLease lease,
            ISystemContext ctx,
            TestIndexUtils contactIndex,
            IFixture fixture)
        {
            using (contactIndex)
            {
                // expired lease exists with owner1.
                repo.Create(lease, ctx);

                // simulate owner2 taking the lease...
                var changedLease = new ElasticLease(
                    lease.ResourceId,
                    DateTime.UtcNow + TimeSpan.FromHours(5),
                    fixture.Create<ElasticLeaseOwner>(),
                    lease.Version);
                repo.SaveOptimistically(changedLease, ctx);

                // now owner 1 tries to extend what it thinks is it's lease...
                var actual = repo.SaveOptimistically(lease, ctx);
                actual.Should().BeFalse();
            }
        }
 public void Extend_NoLeaseExists_LeaseCreatedAndSuccessful(
     IConcurrencyControlRepository<ElasticLease> repo,
     ElasticLease lease,
     ISystemContext ctx,
     TestIndexUtils contactIndex)
 {
     using (contactIndex)
     {
         var actual = repo.SaveOptimistically(lease, ctx);
         actual.Should().BeTrue();
     }
 }
 public void Delete_LeaseExists_LeaseDeleted(
     IConcurrencyControlRepository<ElasticLease> repo,
     ElasticLease lease,
     ISystemContext ctx,
     TestIndexUtils contactIndex)
 {
     using (contactIndex)
     {
         repo.Create(lease, ctx);
         repo.Delete(lease.ResourceId, ctx);
         repo.Get(lease.ResourceId, ctx).Should().BeNull();
     }
 }
 public void Get_LeaseAlreadyExists_LeaseIsReturned(
     IConcurrencyControlRepository<ElasticLease> repo,
     ElasticLease lease,
     ISystemContext ctx,
     TestIndexUtils contactIndex)
 {
     using (contactIndex)
     {
         repo.Create(lease, ctx);
         var actual = repo.Get(lease.ResourceId, ctx);
         actual.Should().NotBeNull();
         actual.ShouldBeEquivalentTo(lease);
     }
 }
 public virtual bool Extend(ElasticLease lease, TimeSpan leaseDuration, ISystemContext ctx, out ElasticLease currentLease)
 {
     var newLease = new ElasticLease(lease.ResourceId, this.dateTime.UtcNow + leaseDuration, lease.Owner);
     return this.TryObtainLock(newLease, ctx, out currentLease);
 }
        public void Extend_InactiveLeaseExistsThenLeaseGrabbedBySameOwner_LeaseNotExtended(
            IConcurrencyControlRepository<ElasticLease> repo,
            ElasticLease lease,
            ISystemContext ctx,
            TestIndexUtils contactIndex,
            IFixture fixture)
        {
            using (contactIndex)
            {
                // expired lease exists with owner1.
                repo.Create(lease, ctx);

                // simulate owner1 (in a different thread/process I guess) taking the lease...
                var changedLease = new ElasticLease(
                    lease.ResourceId,
                    DateTime.UtcNow + TimeSpan.FromHours(5),
                    lease.Owner,
                    lease.Version);
                repo.SaveOptimistically(changedLease, ctx); // version will get incremented

                // now owner 1 tries to extend what it thinks is it's lease, this should eventually be allowed...
                var actual = repo.SaveOptimistically(lease, ctx);
                actual.Should().BeFalse();
            }
        }
        public void TryObtainLock_InactiveLeaseExistsBySameOwner_LockObtained(
            ILeaseService service,
            ISystemContext ctx,
            ElasticLease inactiveLease,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                // write inactive lease
                service.TryObtainLock(inactiveLease, ctx, out inactiveLease).Should().BeTrue();

                var activeLease = new ElasticLease(
                    inactiveLease.ResourceId,
                    DateTime.UtcNow + TimeSpan.FromHours(1),
                    inactiveLease.Owner,
                    inactiveLease.Version);

                ElasticLease winner;
                service.TryObtainLock(activeLease, ctx, out winner).Should().BeTrue();
                activeLease.ShouldBeEquivalentTo(winner);
            }
        }
        protected virtual LockAttemptStatus TryLockContact(
            Guid id, 
            ElasticLeaseOwner targetLeaseOwner, 
            TimeSpan leaseDuration, 
            ISystemContext ctx,
            out ElasticLease newLease)
        {
            // TODO: this is a bit crap
            var leaseExpiration = (this.dateTime.UtcNow + ElasticAnalyticsSettings.MaxAcceptedClockDeviation + leaseDuration);

            var challengerLease = new ElasticLease(id, leaseExpiration, targetLeaseOwner);

            return this.TryLockContact(id, challengerLease, ctx, out newLease);
        }