public static StateLogEntryApplied BiserDecode(byte[] enc = null, Biser.Decoder extDecoder = null) //!!!!!!!!!!!!!! change return type
        {
            Biser.Decoder decoder = null;
            if (extDecoder == null)
            {
                if (enc == null || enc.Length == 0)
                {
                    return(null);
                }
                decoder = new Biser.Decoder(enc);
                if (decoder.CheckNull())
                {
                    return(null);
                }
            }
            else
            {
                if (extDecoder.CheckNull())
                {
                    return(null);
                }
                else
                {
                    decoder = extDecoder;
                }
            }

            StateLogEntryApplied m = new StateLogEntryApplied();  //!!!!!!!!!!!!!! change return type

            m.StateLogEntryId   = decoder.GetULong();
            m.StateLogEntryTerm = decoder.GetULong();


            return(m);
        }
예제 #2
0
파일: RaftNode.cs 프로젝트: ww-it/Raft.Net
        /// <summary>
        /// Only for Follower
        ///  Is called from tryCatch and in lock
        /// </summary>
        /// <param name="address"></param>
        /// <param name="data"></param>
        void ParseStateLogEntrySuggestion(NodeAddress address, byte[] data)
        {
            if (this.NodeState != eNodeState.Follower)
            {
                return;
            }

            StateLogEntrySuggestion suggest = StateLogEntrySuggestion.BiserDecode(data); //data.DeserializeProtobuf<StateLogEntrySuggestion>();

            if (this.NodeTerm > suggest.LeaderTerm)                                      //Sending Leader is not Leader anymore
            {
                this.NodeStateLog.LeaderSynchronizationIsActive = false;
                return;
            }

            if (this.NodeTerm < suggest.LeaderTerm)
            {
                this.NodeTerm = suggest.LeaderTerm;
            }


            if (suggest.StateLogEntry.Index <= NodeStateLog.LastCommittedIndex) //Preventing same entry income, can happen if restoration was sent twice (while switch of leaders)
            {
                return;                                                         //breakpoint don't remove
            }
            //Checking if node can accept current suggestion
            if (suggest.StateLogEntry.PreviousStateLogId > 0)
            {
                var sle = this.NodeStateLog.GetEntryByIndexTerm(suggest.StateLogEntry.PreviousStateLogId, suggest.StateLogEntry.PreviousStateLogTerm);

                if (sle == null)
                {
                    //We don't have previous to this log and need new index request
                    //VerbosePrint($"{NodeAddress.NodeAddressId}>  in sync 1 ");
                    if (entitySettings.InMemoryEntity && entitySettings.InMemoryEntityStartSyncFromLatestEntity && this.NodeStateLog.LastAppliedIndex == 0)
                    {
                        //helps newly starting mode with specific InMemory parameters get only latest command for the entity
                    }
                    else
                    {
                        this.SyncronizeWithLeader();
                        return;
                    }
                }
            }

            //We can apply new Log Entry from the Leader and answer successfully
            this.NodeStateLog.AddToLogFollower(suggest);

            StateLogEntryApplied applied = new StateLogEntryApplied()
            {
                StateLogEntryId   = suggest.StateLogEntry.Index,
                StateLogEntryTerm = suggest.StateLogEntry.Term
                                    // RedirectId = suggest.StateLogEntry.RedirectId
            };

            //this.NodeStateLog.LeaderSynchronizationIsActive = false;

            this.Sender.SendTo(address, eRaftSignalType.StateLogEntryAccepted, applied.SerializeBiser(), this.NodeAddress, entitySettings.EntityName);
        }
예제 #3
0
        /// <summary>
        /// +
        /// Only Leader's proc.
        /// Accepts entry return true if Committed
        /// </summary>
        /// <param name="majorityNumber"></param>
        /// <param name="LogId"></param>
        /// <param name="TermId"></param>
        public bool CommitLogEntry(NodeRaftAddress address, uint majorityQuantity, StateLogEntryApplied applied)
        {
            //If we receive acceptance signals of already Committed entries, we just ignore them

            if (this.LastCommittedIndex < applied.StateLogEntryId && statemachine.NodeTerm == applied.StateLogEntryTerm)    //Setting LastCommittedId
            {
                //Saving committed entry (all previous are automatically committed)
                List <byte[]> lstCommited = new List <byte[]>();

                using (var t = this.db.GetTransaction())
                {
                    //Gathering all not commited entries that are bigger than latest committed index
                    t.ValuesLazyLoadingIsOn = false;
                    foreach (var el in t.SelectForwardFromTo <byte[], byte[]>(stateTableName,
                                                                              new byte[] { 1 }.ToBytes(this.LastCommittedIndex + 1, applied.StateLogEntryTerm), true,
                                                                              new byte[] { 1 }.ToBytes(ulong.MaxValue, applied.StateLogEntryTerm), true, true))
                    {
                        lstCommited.Add(StateLogEntry.BiserDecode(el.Value).Data);
                    }

                    t.Insert <byte[], byte[]>(stateTableName, new byte[] { 2 }, applied.StateLogEntryId.ToBytes(applied.StateLogEntryTerm));
                    t.Commit();
                    //qDistribution.Remove(applied.StateLogEntryId);
                }
                this.LastCommittedIndex     = applied.StateLogEntryId;
                this.LastCommittedIndexTerm = applied.StateLogEntryTerm;

                return(lstCommited.Count > 0);
            }

            return(false);
        }
예제 #4
0
        /// <summary>
        /// Only for Follower
        ///  Is called from tryCatch and in lock
        /// </summary>
        /// <param name="address"></param>
        /// <param name="data"></param>
        void ParseStateLogEntrySuggestion(NodeRaftAddress address, object data)
        {
            if (this.States.NodeState != eNodeState.Follower)
            {
                return;
            }

            StateLogEntrySuggestion suggest = data as StateLogEntrySuggestion;

            if (this.NodeTerm > suggest.LeaderTerm)  //Sending Leader is not Leader anymore
            {
                this.States.LeaderSynchronizationIsActive = false;
                return;
            }

            if (this.NodeTerm < suggest.LeaderTerm)
            {
                this.NodeTerm = suggest.LeaderTerm;
            }
            if (suggest.StateLogEntry == null || (suggest.StateLogEntry.Index <= NodeStateLog.LastCommittedIndex)) //Preventing same entry income, can happen if restoration was sent twice (while switch of leaders)
            {
                return;                                                                                            //breakpoint don't remove
            }
            //Checking if node can accept current suggestion
            if (suggest.StateLogEntry.PreviousStateLogId > 0)
            {
                var sle = this.NodeStateLog.GetEntryByIndexTerm(suggest.StateLogEntry.PreviousStateLogId, suggest.StateLogEntry.PreviousStateLogTerm);

                if (sle == null)
                {
                    this.SyncronizeWithLeader();
                    return;
                }
            }
            //We can apply new Log Entry from the Leader and answer successfully
            this.logHandler.AddLogEntryByFollower(suggest);

            StateLogEntryApplied applied = new StateLogEntryApplied()
            {
                StateLogEntryId   = suggest.StateLogEntry.Index,
                StateLogEntryTerm = suggest.StateLogEntry.Term
                                    // RedirectId = suggest.StateLogEntry.RedirectId
            };

            this.network.SendTo(address, eRaftSignalType.StateLogEntryAccepted, applied, this.NodeAddress, entitySettings.EntityName);
        }
예제 #5
0
파일: RaftNode.cs 프로젝트: ww-it/Raft.Net
        /// <summary>
        /// Leader receives accepted Log
        /// </summary>
        /// <param name="address"></param>
        /// <param name="data"></param>
        void ParseStateLogEntryAccepted(NodeAddress address, byte[] data)
        {
            if (this.NodeState != eNodeState.Leader)
            {
                return;
            }

            StateLogEntryApplied applied = StateLogEntryApplied.BiserDecode(data);

            var res = this.NodeStateLog.EntryIsAccepted(address, GetMajorityQuantity(), applied);

            if (res == StateLog.eEntryAcceptanceResult.Committed)
            {
                this.VerbosePrint($"{this.NodeAddress.NodeAddressId}> LogEntry {applied.StateLogEntryId} is COMMITTED (answer from {address.NodeAddressId})");

                RemoveLeaderLogResendTimer();


                //Force heartbeat, to make followers to get faster info about commited elements
                LeaderHeartbeat heartBeat = new LeaderHeartbeat()
                {
                    LeaderTerm                     = this.NodeTerm,
                    StateLogLatestIndex            = NodeStateLog.StateLogId,
                    StateLogLatestTerm             = NodeStateLog.StateLogTerm,
                    LastStateLogCommittedIndex     = this.NodeStateLog.LastCommittedIndex,
                    LastStateLogCommittedIndexTerm = this.NodeStateLog.LastCommittedIndexTerm
                };
                this.Sender.SendToAll(eRaftSignalType.LeaderHearthbeat, heartBeat.SerializeBiser(), this.NodeAddress, entitySettings.EntityName, true);
                //---------------------------------------

                //this.NodeStateLog.RemoveEntryFromDistribution(applied.StateLogEntryId, applied.StateLogEntryTerm);
                InLogEntrySend = false;

                ApplyLogEntry();
            }
        }
예제 #6
0
        /// <summary>
        /// +
        /// Only Leader's proc.
        /// Accepts entry return true if Committed
        /// </summary>
        /// <param name="majorityNumber"></param>
        /// <param name="LogId"></param>
        /// <param name="TermId"></param>
        public eEntryAcceptanceResult EntryIsAccepted(NodeAddress address, uint majorityQuantity, StateLogEntryApplied applied)
        {
            //If we receive acceptance signals of already Committed entries, we just ignore them
            if (applied.StateLogEntryId <= this.LastCommittedIndex)
            {
                return(eEntryAcceptanceResult.AlreadyAccepted);    //already accepted
            }
            if (applied.StateLogEntryId <= this.LastAppliedIndex)
            {
                return(eEntryAcceptanceResult.AlreadyAccepted);    //already accepted
            }
            StateLogEntryAcceptance acc = null;

            if (dStateLogEntryAcceptance.TryGetValue(applied.StateLogEntryId, out acc))
            {
                if (acc.Term != applied.StateLogEntryTerm)
                {
                    return(eEntryAcceptanceResult.NotAccepted);   //Came from wrong Leader probably
                }
                //acc.Quantity += 1;
                acc.acceptedEndPoints.Add(address.EndPointSID);
            }
            else
            {
                acc = new StateLogEntryAcceptance()
                {
                    //Quantity = 2,  //Leader + first incoming
                    Index = applied.StateLogEntryId,
                    Term  = applied.StateLogEntryTerm
                };

                acc.acceptedEndPoints.Add(address.EndPointSID);

                dStateLogEntryAcceptance[applied.StateLogEntryId] = acc;
            }


            if ((acc.acceptedEndPoints.Count + 1) >= majorityQuantity)
            {
                this.LastAppliedIndex = applied.StateLogEntryId;
                //Removing from Dictionary
                dStateLogEntryAcceptance.Remove(applied.StateLogEntryId);

                if (this.LastCommittedIndex < applied.StateLogEntryId && rn.NodeTerm == applied.StateLogEntryTerm)    //Setting LastCommittedId
                {
                    //Saving committed entry (all previous are automatically committed)
                    List <byte[]> lstCommited = new List <byte[]>();

                    using (var t = this.rn.db.GetTransaction())
                    {
                        //Gathering all not commited entries that are bigger than latest committed index
                        t.ValuesLazyLoadingIsOn = false;
                        foreach (var el in t.SelectForwardFromTo <byte[], byte[]>(tblStateLogEntry,
                                                                                  new byte[] { 1 }.ToBytes(this.LastCommittedIndex + 1, applied.StateLogEntryTerm), true,
                                                                                  new byte[] { 1 }.ToBytes(ulong.MaxValue, applied.StateLogEntryTerm), true, true))
                        {
                            lstCommited.Add(StateLogEntry.BiserDecode(el.Value).Data);
                        }


                        //Setting latest commited index
                        if (rn.entitySettings.DelayedPersistenceIsActive)
                        {
                            foreach (var iel in sleCache.Where(r => r.Key >= this.LastCommittedIndex + 1 && r.Value.Item1 == applied.StateLogEntryTerm))
                            {
                                lstCommited.Add(iel.Value.Item2.Data);
                            }

                            sleCacheIndex = applied.StateLogEntryId;
                            sleCacheTerm  = applied.StateLogEntryTerm;
                        }
                        else
                        {
                            t.Insert <byte[], byte[]>(tblStateLogEntry, new byte[] { 2 }, applied.StateLogEntryId.ToBytes(applied.StateLogEntryTerm));
                            t.Commit();
                        }

                        //Removing entry from command queue
                        //t.RemoveKey<byte[]>(tblAppendLogEntry, new byte[] { 1 }.ToBytes(applied.StateLogEntryTerm, applied.StateLogEntryId));
                        qDistribution.Remove(applied.StateLogEntryId);
                    }

                    this.LastCommittedIndex     = applied.StateLogEntryId;
                    this.LastCommittedIndexTerm = applied.StateLogEntryTerm;

                    if (lstCommited.Count > 0)
                    {
                        this.rn.Commited();
                    }
                    //this.rn.Commited(applied.StateLogEntryId);

                    return(eEntryAcceptanceResult.Committed);
                }
            }

            return(eEntryAcceptanceResult.Accepted);
        }
예제 #7
0
        /// <summary>
        /// +
        /// Only Leader's proc.
        /// Accepts entry return true if Committed
        /// </summary>
        /// <param name="majorityNumber"></param>
        /// <param name="LogId"></param>
        /// <param name="TermId"></param>
        public eEntryAcceptanceResult EntryIsAccepted(NodeRaftAddress address, uint majorityQuantity, StateLogEntryApplied applied)
        {
            //If we receive acceptance signals of already Committed entries, we just ignore them
            if (applied.StateLogEntryId <= this.LastCommittedIndex)
            {
                return(eEntryAcceptanceResult.AlreadyAccepted);    //already accepted
            }
            if (applied.StateLogEntryId <= this.LastAppliedIndex)
            {
                return(eEntryAcceptanceResult.AlreadyAccepted);    //already accepted
            }
            StateLogEntryAcceptance acc = null;

            if (dStateLogEntryAcceptance.TryGetValue(applied.StateLogEntryId, out acc))
            {
                if (acc.Term != applied.StateLogEntryTerm)
                {
                    return(eEntryAcceptanceResult.NotAccepted);   //Came from wrong Leader probably
                }
                acc.acceptedEndPoints.Add(address.EndPointSID);
            }
            else
            {
                acc = new StateLogEntryAcceptance()
                {
                    Index = applied.StateLogEntryId,
                    Term  = applied.StateLogEntryTerm
                };

                acc.acceptedEndPoints.Add(address.EndPointSID);

                dStateLogEntryAcceptance[applied.StateLogEntryId] = acc;
            }
            if ((acc.acceptedEndPoints.Count + 1) >= majorityQuantity)
            {
                this.LastAppliedIndex = applied.StateLogEntryId;
                //Removing from Dictionary
                dStateLogEntryAcceptance.Remove(applied.StateLogEntryId);

                if (this.LastCommittedIndex < applied.StateLogEntryId && statemachine.NodeTerm == applied.StateLogEntryTerm)    //Setting LastCommittedId
                {
                    //Saving committed entry (all previous are automatically committed)
                    List <byte[]> lstCommited = new List <byte[]>();

                    var col  = this.db.GetCollection <StateLogEntry>(stateTableName);
                    var list = col.Query().Where(s => s.Index >= this.LastCommittedIndex + 1 && s.Term == applied.StateLogEntryTerm).ToList();
                    foreach (var item in list)
                    {
                        lstCommited.Add(new byte[1]);
                        item.IsCommitted = true;
                        col.Update(item);
                        qDistribution.Remove(applied.StateLogEntryId);
                    }
                    //using (var t = this.db.GetTransaction())
                    //{
                    //    //Gathering all not commited entries that are bigger than latest committed index
                    //    t.ValuesLazyLoadingIsOn = false;
                    //    foreach (var el in t.SelectForwardFromTo<byte[], byte[]>(stateTableName,
                    //       new byte[] { 1 }.ToBytes(this.LastCommittedIndex + 1, applied.StateLogEntryTerm), true,
                    //       new byte[] { 1 }.ToBytes(ulong.MaxValue, applied.StateLogEntryTerm), true, true))
                    //    {
                    //        lstCommited.Add(StateLogEntry.BiserDecode(el.Value).Data);
                    //    }

                    //    t.Insert<byte[], byte[]>(stateTableName, new byte[] { 2 }, applied.StateLogEntryId.ToBytes(applied.StateLogEntryTerm));
                    //    t.Commit();
                    //    qDistribution.Remove(applied.StateLogEntryId);
                    //}
                    this.LastCommittedIndex     = applied.StateLogEntryId;
                    this.LastCommittedIndexTerm = applied.StateLogEntryTerm;

                    if (lstCommited.Count > 0)
                    {
                        this.statemachine.logHandler.Commited();
                    }
                    return(eEntryAcceptanceResult.Committed);
                }
            }

            return(eEntryAcceptanceResult.Accepted);
        }