Пример #1
0
        public RoundBasedRegister(IIntercomMessageHub intercomMessageHub,
                                  IBallotGenerator ballotGenerator,
                                  ISynodConfigurationProvider synodConfigProvider,
                                  LeaseConfiguration leaseConfig,
                                  ILogger logger)
        {
            this.logger = logger;
            this.synodConfigProvider = synodConfigProvider;
            this.leaseConfig         = leaseConfig;
            this.intercomMessageHub  = intercomMessageHub;
            readBallot  = ballotGenerator.Null();
            writeBallot = ballotGenerator.Null();

            listener = intercomMessageHub.Subscribe();

            listener.Where(m => m.Equals(ConsensusMessages.LeaseRead)).Subscribe(OnReadReceived);
            listener.Where(m => m.Equals(ConsensusMessages.LeaseWrite)).Subscribe(OnWriteReceived);

            ackReadStream   = listener.Where(m => m.Equals(ConsensusMessages.LeaseAckRead));
            nackReadStream  = listener.Where(m => m.Equals(ConsensusMessages.LeaseNackRead));
            ackWriteStream  = listener.Where(m => m.Equals(ConsensusMessages.LeaseAckWrite));
            nackWriteStream = listener.Where(m => m.Equals(ConsensusMessages.LeaseNackWrite));

            WaitBeforeNextLeaseIssued(leaseConfig);
        }
Пример #2
0
        private void OnWriteReceived(IMessage message)
        {
            var payload = message.GetPayload <LeaseWriteMessage>();

            var ballot = new Ballot(new DateTime(payload.Ballot.Timestamp, DateTimeKind.Utc),
                                    payload.Ballot.MessageNumber,
                                    payload.Ballot.Identity);
            IMessage response;

            if (Interlocked.Exchange(ref writeBallot, writeBallot) > ballot ||
                Interlocked.Exchange(ref readBallot, readBallot) > ballot)
            {
                LogNackWrite(ballot);

                response = Message.Create(new LeaseNackWriteMessage
                {
                    Ballot    = payload.Ballot,
                    SenderUri = synodConfigProvider.LocalNode.Uri.ToSocketAddress()
                });
            }
            else
            {
                LogAckWrite(ballot);

                Interlocked.Exchange(ref writeBallot, ballot);
                Interlocked.Exchange(ref lease, new Lease(payload.Lease.Identity, new DateTime(payload.Lease.ExpiresAt, DateTimeKind.Utc), payload.Lease.OwnerPayload));

                response = Message.Create(new LeaseAckWriteMessage
                {
                    Ballot    = payload.Ballot,
                    SenderUri = synodConfigProvider.LocalNode.Uri.ToSocketAddress()
                });
            }
            intercomMessageHub.Send(response);
        }
Пример #3
0
        public InstanceRoundBasedRegister(Instance instance,
                                          IIntercomMessageHub intercomMessageHub,
                                          IBallotGenerator ballotGenerator,
                                          ISynodConfigurationProvider synodConfigProvider,
                                          InstanceLeaseProviderConfiguration leaseConfig,
                                          ILogger logger)
        {
            this.instance            = instance;
            this.logger              = logger;
            this.synodConfigProvider = synodConfigProvider;
            this.leaseConfig         = leaseConfig;
            this.intercomMessageHub  = intercomMessageHub;
            readBallot  = ballotGenerator.Null();
            writeBallot = ballotGenerator.Null();
            lastIntercomeMessageTimestamp = DateTime.UtcNow.Ticks;

            listener = intercomMessageHub.Subscribe();

            listener.Where(IsLeaseRead)
            .Subscribe(OnReadReceived);
            listener.Where(IsWriteLease)
            .Subscribe(OnWriteReceived);

            ackReadStream   = listener.Where(IsLeaseAckReadMessage);
            nackReadStream  = listener.Where(IsLeaseNackReadMessage);
            ackWriteStream  = listener.Where(IsLeaseAckWriteMessage);
            nackWriteStream = listener.Where(IsLeaseNackWriteMessage);

            logger.Info($"{instance.Identity.GetAnyString()}-InstanceRoundBasedRegister created");
        }
Пример #4
0
        private IMessage CreateLeaseAckReadMessage(LeaseReadMessage payload)
        {
            Lease lastKnownLease = null;

            Interlocked.Exchange(ref lastKnownLease, lease);
            Ballot lastKnownWriteBallot = null;

            Interlocked.Exchange(ref lastKnownWriteBallot, writeBallot);

            return(Message.Create(new LeaseAckReadMessage
            {
                Ballot = payload.Ballot,
                KnownWriteBallot = new Messages.Ballot
                {
                    Identity = lastKnownWriteBallot.Identity,
                    Timestamp = lastKnownWriteBallot.Timestamp.Ticks,
                    MessageNumber = lastKnownWriteBallot.MessageNumber
                },
                Lease = (lastKnownLease != null)
                                                  ? new Messages.Lease
                {
                    Identity = lastKnownLease.OwnerIdentity,
                    ExpiresAt = lastKnownLease.ExpiresAt.Ticks,
                    OwnerPayload = lastKnownLease.OwnerPayload
                }
                                                  : null,
                SenderUri = synodConfigProvider.LocalNode.Uri.ToSocketAddress()
            }));
        }
Пример #5
0
        private Lease AсquireOrLearnLease(Ballot ballot, DateTime now)
        {
            var read = register.Read(ballot);

            if (read.TxOutcome == TxOutcome.Commit)
            {
                var lease = read.Lease;
                if (LeaseIsNotSafelyExpired(lease, now))
                {
                    LogStartSleep();
                    config.ClockDrift.Sleep();
                    LogAwake();

                    // TODO: Add recursion exit condition
                    return(AсquireOrLearnLease(ballotGenerator.New(localNode.SocketIdentity), DateTime.UtcNow));
                }

                if (LeaseNullOrExpired(lease, now) || IsLeaseOwner(lease))
                {
                    LogLeaseProlonged(lease);
                    lease = new Lease(localNode.SocketIdentity, now + config.MaxLeaseTimeSpan, Interlocked.Exchange(ref ownerPayload, ownerPayload));
                }

                var write = register.Write(ballot, lease);
                if (write.TxOutcome == TxOutcome.Commit)
                {
                    return(lease);
                }
            }

            return(null);
        }
Пример #6
0
        public LeaseTxResult Write(Ballot ballot, Lease lease)
        {
            var ackFilter  = new LeaderElectionMessageFilter(ballot, m => m.GetPayload <LeaseAckWriteMessage>(), synodConfigProvider);
            var nackFilter = new LeaderElectionMessageFilter(ballot, m => m.GetPayload <LeaseNackWriteMessage>(), synodConfigProvider);

            var awaitableAckFilter  = new AwaitableMessageStreamFilter(ackFilter.Match, m => m.GetPayload <LeaseAckWriteMessage>(), GetQuorum());
            var awaitableNackFilter = new AwaitableMessageStreamFilter(nackFilter.Match, m => m.GetPayload <LeaseNackWriteMessage>(), GetQuorum());

            using (ackWriteStream.Subscribe(awaitableAckFilter))
            {
                using (nackWriteStream.Subscribe(awaitableNackFilter))
                {
                    intercomMessageHub.Send(CreateWriteMessage(ballot, lease));

                    var index = WaitHandle.WaitAny(new[] { awaitableAckFilter.Filtered, awaitableNackFilter.Filtered },
                                                   leaseConfig.NodeResponseTimeout);

                    if (ReadNotAcknowledged(index))
                    {
                        return(new LeaseTxResult {
                            TxOutcome = TxOutcome.Abort
                        });
                    }

                    return(new LeaseTxResult
                    {
                        TxOutcome = TxOutcome.Commit,
                        // NOTE: needed???
                        Lease = lease
                    });
                }
            }
        }
Пример #7
0
        private void OnReadReceived(IMessage message)
        {
            Interlocked.Exchange(ref lastIntercomeMessageTimestamp, DateTime.UtcNow.Ticks);

            var payload = message.GetPayload <LeaseReadMessage>();

            var ballot = new Ballot(new DateTime(payload.Ballot.Timestamp, DateTimeKind.Utc),
                                    payload.Ballot.MessageNumber,
                                    payload.Ballot.Identity);

            IMessage response;

            if (Interlocked.Exchange(ref writeBallot, writeBallot) >= ballot || Interlocked.Exchange(ref readBallot, readBallot) >= ballot)
            {
                LogNackRead(ballot);

                response = Message.Create(new LeaseNackReadMessage
                {
                    Ballot    = payload.Ballot,
                    SenderUri = synodConfigProvider.LocalNode.Uri.ToSocketAddress()
                });
            }
            else
            {
                LogAckRead(ballot);

                Interlocked.Exchange(ref readBallot, ballot);

                response = CreateLeaseAckReadMessage(payload);
            }

            intercomMessageHub.Send(response);
        }
Пример #8
0
 private void LogAckWrite(Ballot ballot)
 {
     if (writeBallot <= ballot)
     {
         logger.Debug($"process {synodConfig.LocalNode.Uri.AbsoluteUri} " +
                      "ACK_WRITE ==WB== " +
                      $"{writeBallot.Timestamp.ToString(format)}-" +
                      $"{writeBallot.MessageNumber}-" +
                      $"{writeBallot.Identity.GetAnyString()} " +
                      "<= " +
                      $"{ballot.Timestamp.ToString(format)}-" +
                      $"{ballot.MessageNumber}-" +
                      $"{ballot.Identity.GetAnyString()}");
     }
     if (readBallot <= ballot)
     {
         logger.Debug($"process {synodConfig.LocalNode.Uri.AbsoluteUri} " +
                      "ACK_WRITE ==RB== " +
                      $"{readBallot.Timestamp.ToString(format)}-" +
                      $"{readBallot.MessageNumber}-" +
                      $"{readBallot.Identity.GetAnyString()} " +
                      "<= " +
                      $"{ballot.Timestamp.ToString(format)}-" +
                      $"{ballot.MessageNumber}-" +
                      $"{ballot.Identity.GetAnyString()}");
     }
 }
Пример #9
0
 private void LogNackRead(Ballot ballot)
 {
     if (writeBallot >= ballot)
     {
         logger.Debug($"process {synodConfigProvider.LocalNode.Uri.AbsoluteUri} " +
                      "NACK_READ ==WB== " +
                      $"{writeBallot.Timestamp.ToString(format)}-" +
                      $"{writeBallot.MessageNumber}-" +
                      $"{writeBallot.Identity.GetAnyString()} " +
                      ">= " +
                      $"{ballot.Timestamp.ToString(format)}-" +
                      $"{ballot.MessageNumber}-" +
                      $"{ballot.Identity.GetAnyString()}");
     }
     if (readBallot >= ballot)
     {
         logger.Debug($"process {synodConfigProvider.LocalNode.Uri.AbsoluteUri} " +
                      "NACK_READ ==RB== " +
                      $"{readBallot.Timestamp.ToString(format)}-" +
                      $"{readBallot.MessageNumber}-" +
                      $"{readBallot.Identity.GetAnyString()} " +
                      ">= " +
                      $"{ballot.Timestamp.ToString(format)}-" +
                      $"{ballot.MessageNumber}-" +
                      $"{ballot.Identity.GetAnyString()}");
     }
 }
 public LeaderElectionMessageFilter(Ballot ballot,
                                    Func <IMessage, ILeaseMessage> payload,
                                    ISynodConfiguration synodConfig)
 {
     this.ballot      = ballot;
     this.synodConfig = synodConfig;
     this.payload     = payload;
 }
Пример #11
0
 private IMessage CreateReadMessage(Ballot ballot)
 => Message.Create(new LeaseReadMessage
 {
     Ballot = new Messages.Ballot
     {
         Identity      = ballot.Identity,
         Timestamp     = ballot.Timestamp.Ticks,
         MessageNumber = ballot.MessageNumber
     }
 });
Пример #12
0
 private IMessage CreateWriteMessage(Ballot ballot, Lease lease)
 => Message.Create(new LeaseWriteMessage
 {
     Ballot = new Messages.Ballot
     {
         Identity      = ballot.Identity,
         Timestamp     = ballot.Timestamp.Ticks,
         MessageNumber = ballot.MessageNumber
     },
     Lease = new Messages.Lease
     {
         Identity     = lease.OwnerIdentity,
         ExpiresAt    = lease.ExpiresAt.Ticks,
         OwnerPayload = lease.OwnerPayload
     }
 });
Пример #13
0
        public LeaseTxResult Read(Ballot ballot)
        {
            var ackFilter = new LeaderElectionMessageFilter(ballot,
                                                            m => m.GetPayload <LeaseAckReadMessage>(),
                                                            synodConfigProvider);
            var nackFilter = new LeaderElectionMessageFilter(ballot,
                                                             m => m.GetPayload <LeaseNackReadMessage>(),
                                                             synodConfigProvider);

            var awaitableAckFilter = new AwaitableMessageStreamFilter(ackFilter.Match,
                                                                      m => m.GetPayload <LeaseAckReadMessage>(),
                                                                      GetQuorum());
            var awaitableNackFilter = new AwaitableMessageStreamFilter(nackFilter.Match,
                                                                       m => m.GetPayload <LeaseNackReadMessage>(),
                                                                       GetQuorum());

            using (ackReadStream.Subscribe(awaitableAckFilter))
            {
                using (nackReadStream.Subscribe(awaitableNackFilter))
                {
                    var message = CreateReadMessage(ballot);
                    intercomMessageHub.Send(message);

                    var index = WaitHandle.WaitAny(new[] { awaitableAckFilter.Filtered, awaitableNackFilter.Filtered },
                                                   leaseConfig.NodeResponseTimeout);

                    if (ReadNotAcknowledged(index))
                    {
                        return(new LeaseTxResult {
                            TxOutcome = TxOutcome.Abort
                        });
                    }

                    var lease = awaitableAckFilter
                                .MessageStream
                                .Select(m => m.GetPayload <LeaseAckReadMessage>())
                                .Max(p => CreateLastWrittenLease(p))
                                .Lease;

                    return(new LeaseTxResult
                    {
                        TxOutcome = TxOutcome.Commit,
                        Lease = lease
                    });
                }
            }
        }
Пример #14
0
 public LastWrittenLease(Ballot writeBallot, Lease lease)
 {
     this.writeBallot = writeBallot;
     Lease            = lease;
 }