public static StateLogEntryRequest 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; } } StateLogEntryRequest m = new StateLogEntryRequest(); //!!!!!!!!!!!!!! change return type m.StateLogEntryId = decoder.GetULong(); m.StateLogEntryTerm = decoder.GetULong(); return(m); }
/// <summary> /// Only for Leader. /// Follower requests new Log Entry Index from the Leader and Leader answers to the Follower /// </summary> /// <param name="address"></param> /// <param name="data"></param> void ParseStateLogEntryRequest(NodeAddress address, byte[] data) { if (this.NodeState != eNodeState.Leader) { return; } StateLogEntryRequest req = StateLogEntryRequest.BiserDecode(data);//.DeserializeProtobuf<StateLogEntryRequest>(); //Getting suggestion var suggestion = this.NodeStateLog.GetNextStateLogEntrySuggestionFromRequested(req); //VerbosePrint($"{NodeAddress.NodeAddressId} (Leader)> Request (I/T): {req.StateLogEntryId}/{req.StateLogEntryTerm} from {address.NodeAddressId};"); VerbosePrint($"{NodeAddress.NodeAddressId} (Leader)> Request (I): {req.StateLogEntryId} from {address.NodeAddressId};"); if (suggestion != null) { this.Sender.SendTo(address, eRaftSignalType.StateLogEntrySuggestion, suggestion.SerializeBiser(), this.NodeAddress, entitySettings.EntityName); } }
/// <summary> /// Only for Leader. /// Follower requests new Log Entry Index from the Leader and Leader answers to the Follower /// </summary> /// <param name="address"></param> /// <param name="data"></param> void ParseStateLogEntryRequest(NodeRaftAddress address, object data) { if (this.States.NodeState != eNodeState.Leader) { return; } StateLogEntryRequest req = data as StateLogEntryRequest; //Getting suggestion var suggestion = this.NodeStateLog.GetNextStateLogEntrySuggestion(req); if (GlobalConfig.Verbose) { Console.WriteLine($"create suggestion to req:{req.StateLogEntryId}" + suggestion.StateLogEntry.Index + " is commit:" + suggestion.StateLogEntry.IsCommitted); } //VerbosePrint($"{NodeAddress.NodeAddressId} (Leader)> Request (I): {req.StateLogEntryId} from {address.NodeAddressId};"); if (suggestion != null) { this.network.SendTo(address, eRaftSignalType.StateLogEntrySuggestion, suggestion, this.NodeAddress, entitySettings.EntityName); } }
/// <summary> /// Is called from tryCatch and lock. /// Synchronizes starting from last committed value /// </summary> /// <param name="stateLogEntryId"></param> internal void SyncronizeWithLeader(bool selfCall = false) { if (!selfCall) { if (IsLeaderSynchroTimerActive) { return; } NodeStateLog.RollbackToLastestCommit(); } States.LeaderSynchronizationIsActive = true; States.LeaderSynchronizationRequestWasSent = DateTime.UtcNow; StateLogEntryRequest req = null; req = new StateLogEntryRequest() { StateLogEntryId = NodeStateLog.LastCommittedIndex }; this.network.SendTo(this.LeaderNodeAddress, eRaftSignalType.StateLogEntryRequest, req, this.NodeAddress, entitySettings.EntityName); }
/// <summary> /// Is called from tryCatch and lock. /// Synchronizes starting from last committed value /// </summary> /// <param name="stateLogEntryId"></param> internal void SyncronizeWithLeader(bool selfCall = false) { if (!selfCall) { if (IsLeaderSynchroTimerActive) { return; } NodeStateLog.ClearStateLogStartingFromCommitted(); } NodeStateLog.LeaderSynchronizationIsActive = true; NodeStateLog.LeaderSynchronizationRequestWasSent = DateTime.UtcNow; StateLogEntryRequest req = null; if (entitySettings.InMemoryEntity && entitySettings.InMemoryEntityStartSyncFromLatestEntity) { req = new StateLogEntryRequest() { StateLogEntryId = this.LeaderHeartbeat.LastStateLogCommittedIndex == 0 ? 0 : this.LeaderHeartbeat.LastStateLogCommittedIndex - 1 }; } else { req = new StateLogEntryRequest() { StateLogEntryId = NodeStateLog.LastCommittedIndex }; } this.Sender.SendTo(this.LeaderNodeAddress, eRaftSignalType.StateLogEntryRequest, req.SerializeBiser(), this.NodeAddress, entitySettings.EntityName); }
/// <summary> /// + /// Can be null. /// Must be called inside of operation lock. /// </summary> /// <param name="logEntryId"></param> /// <param name="LeaderTerm"></param> /// <returns></returns> public StateLogEntrySuggestion GetNextStateLogEntrySuggestionFromRequested(StateLogEntryRequest req) { StateLogEntrySuggestion le = new StateLogEntrySuggestion() { LeaderTerm = rn.NodeTerm }; int cnt = 0; StateLogEntry sle = null; ulong prevId = 0; ulong prevTerm = 0; using (var t = this.rn.db.GetTransaction()) { if (req.StateLogEntryId == 0)// && req.StateLogEntryTerm == 0) { if (rn.entitySettings.DelayedPersistenceIsActive && sleCache.Count > 0) { sle = sleCache.OrderBy(r => r.Key).First().Value.Item2; } else { var trow = t.SelectForwardFromTo <byte[], byte[]>(tblStateLogEntry, new byte[] { 1 }.ToBytes(ulong.MinValue, ulong.MinValue), true, new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true).FirstOrDefault(); if (trow != null && trow.Exists) { sle = StateLogEntry.BiserDecode(trow.Value); } } //else //{ // //should not normally happen //} if (sle != null) { le.StateLogEntry = sle; if ( LastCommittedIndexTerm >= le.StateLogEntry.Term && LastCommittedIndex >= le.StateLogEntry.Index ) { le.IsCommitted = true; } cnt = 2; } } else { Tuple <ulong, StateLogEntry> sleTpl; bool reForward = true; if (rn.entitySettings.DelayedPersistenceIsActive && sleCache.TryGetValue(req.StateLogEntryId, out sleTpl) ) { cnt++; sle = sleTpl.Item2; prevId = sle.Index; prevTerm = sle.Term; if (sleCache.TryGetValue(req.StateLogEntryId + 1, out sleTpl)) { cnt++; sle = sleTpl.Item2; le.StateLogEntry = sle; } } else { reForward = false; foreach (var el in t.SelectForwardFromTo <byte[], byte[]>(tblStateLogEntry, //new byte[] { 1 }.ToBytes(req.StateLogEntryId, req.StateLogEntryTerm), true, new byte[] { 1 }.ToBytes(req.StateLogEntryId, ulong.MinValue), true, new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true).Take(2)) { cnt++; sle = StateLogEntry.BiserDecode(el.Value); if (cnt == 1) { prevId = sle.Index; prevTerm = sle.Term; } else { le.StateLogEntry = sle; } } } if (cnt < 2 && reForward) { ulong toAdd = (ulong)cnt; foreach (var el in t.SelectForwardFromTo <byte[], byte[]>(tblStateLogEntry, //new byte[] { 1 }.ToBytes(req.StateLogEntryId + toAdd, req.StateLogEntryTerm), true, new byte[] { 1 }.ToBytes(req.StateLogEntryId + toAdd, ulong.MinValue), true, new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true).Take(2)) { cnt++; sle = StateLogEntry.BiserDecode(el.Value); if (cnt == 1) { prevId = sle.Index; prevTerm = sle.Term; } else { le.StateLogEntry = sle; } } } if (cnt == 2) { le.StateLogEntry.PreviousStateLogId = prevId; le.StateLogEntry.PreviousStateLogTerm = prevTerm; if ( LastCommittedIndexTerm >= le.StateLogEntry.Term && LastCommittedIndex >= le.StateLogEntry.Index ) { le.IsCommitted = true; } } } } //if (first) if (cnt != 2) { return(null); } return(le); }
/// <summary> /// + /// Can be null. /// Must be called inside of operation lock. /// get local commit history for follower /// </summary> /// <param name="logEntryId"></param> /// <param name="LeaderTerm"></param> /// <returns></returns> public StateLogEntrySuggestion GetNextStateLogEntrySuggestionFromRequested(StateLogEntryRequest req) { StateLogEntrySuggestion le = new StateLogEntrySuggestion() { LeaderTerm = statemachine.NodeTerm }; int cnt = 0; StateLogEntry sle = null; ulong prevId = 0; ulong prevTerm = 0; var col = this.db.GetCollection <StateLogEntry>(stateTableName); //using (var t = this.db.GetTransaction()) { if (req.StateLogEntryId == 0) { var trow = col.Query().OrderBy(s => s.Term, 1).OrderBy(s => s.Term, 1).FirstOrDefault(); if (trow != null) { le.StateLogEntry = trow; if (LastCommittedIndexTerm >= le.StateLogEntry.Term && LastCommittedIndex >= le.StateLogEntry.Index ) { le.IsCommitted = true; } cnt = 2; } } else { var list = col.Query().Where(s => s.Index >= req.StateLogEntryId).ToList().OrderBy(s => s.Term).OrderBy(s => s.Term).ToList(); foreach (var item in list) { cnt++; sle = item; if (cnt == 1) { prevId = sle.Index; prevTerm = sle.Term; } else { le.StateLogEntry = sle; } } if (cnt < 2) { ulong toAdd = (ulong)cnt; list = col.Query().Where(s => s.Index >= req.StateLogEntryId + toAdd).ToList().OrderBy(s => s.Term).OrderBy(s => s.Index).ToList(); foreach (var item in list) { cnt++; sle = item; if (cnt == 1) { prevId = sle.Index; prevTerm = sle.Term; } else { le.StateLogEntry = sle; } } } } //if (req.StateLogEntryId == 0)// && req.StateLogEntryTerm == 0) //{ // var trow = t.SelectForwardFromTo<byte[], byte[]>(stateTableName, // new byte[] { 1 }.ToBytes(ulong.MinValue, ulong.MinValue), true, // new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true).FirstOrDefault(); // if (trow != null && trow.Exists) // sle = StateLogEntry.BiserDecode(trow.Value); // if (sle != null) // { // le.StateLogEntry = sle; // if ( // LastCommittedIndexTerm >= le.StateLogEntry.Term // && // LastCommittedIndex >= le.StateLogEntry.Index // ) // { // le.IsCommitted = true; // } // cnt = 2; // } //} //else //{ // Tuple<ulong, StateLogEntry> sleTpl; // bool reForward = true; // reForward = false; // foreach (var el in t.SelectForwardFromTo<byte[], byte[]>(stateTableName, // new byte[] { 1 }.ToBytes(req.StateLogEntryId, ulong.MinValue), true, // new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true).Take(2)) // { // cnt++; // sle = StateLogEntry.BiserDecode(el.Value); // if (cnt == 1) // { // prevId = sle.Index; // prevTerm = sle.Term; // } // else // { // le.StateLogEntry = sle; // } // } // if (cnt < 2 && reForward) // { // ulong toAdd = (ulong)cnt; // foreach (var el in t.SelectForwardFromTo<byte[], byte[]>(stateTableName, // //new byte[] { 1 }.ToBytes(req.StateLogEntryId + toAdd, req.StateLogEntryTerm), true, // new byte[] { 1 }.ToBytes(req.StateLogEntryId + toAdd, ulong.MinValue), true, // new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true).Take(2)) // { // cnt++; // sle = StateLogEntry.BiserDecode(el.Value); // if (cnt == 1) // { // prevId = sle.Index; // prevTerm = sle.Term; // } // else // { // le.StateLogEntry = sle; // } // } } if (cnt == 2) { le.StateLogEntry.PreviousStateLogId = prevId; le.StateLogEntry.PreviousStateLogTerm = prevTerm; if ( LastCommittedIndexTerm >= le.StateLogEntry.Term && LastCommittedIndex >= le.StateLogEntry.Index ) { le.IsCommitted = true; } } if (cnt != 2) { return(null); } return(le); }
/// <summary> /// + /// Can be null. /// Must be called inside of operation lock. /// get local commit history for follower /// </summary> /// <param name="logEntryId"></param> /// <param name="LeaderTerm"></param> /// <returns></returns> public StateLogEntrySuggestion GetNextStateLogEntrySuggestion(StateLogEntryRequest req) { StateLogEntrySuggestion le = new StateLogEntrySuggestion() { LeaderTerm = statemachine.NodeTerm }; int cnt = 0; StateLogEntry sle = null; ulong prevId = 0; ulong prevTerm = 0; using (var t = this.db.GetTransaction()) { if (req.StateLogEntryId == 0)// && req.StateLogEntryTerm == 0) { var trow = t.SelectForwardFromTo <byte[], byte[]>(stateTableName, new byte[] { 1 }.ToBytes(ulong.MinValue, ulong.MinValue), true, new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true).FirstOrDefault(); if (trow != null && trow.Exists) { sle = StateLogEntry.BiserDecode(trow.Value); } if (sle != null) { le.StateLogEntry = sle; if ( LastCommittedIndexTerm >= le.StateLogEntry.Term && LastCommittedIndex >= le.StateLogEntry.Index ) { le.IsCommitted = true; } cnt = 2; } } else { Tuple <ulong, StateLogEntry> sleTpl; bool reForward = true; reForward = false; foreach (var el in t.SelectForwardFromTo <byte[], byte[]>(stateTableName, new byte[] { 1 }.ToBytes(req.StateLogEntryId, ulong.MinValue), true, new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true).Take(2)) { cnt++; sle = StateLogEntry.BiserDecode(el.Value); if (cnt == 1) { prevId = sle.Index; prevTerm = sle.Term; } else { le.StateLogEntry = sle; } } if (cnt < 2 && reForward) { ulong toAdd = (ulong)cnt; foreach (var el in t.SelectForwardFromTo <byte[], byte[]>(stateTableName, //new byte[] { 1 }.ToBytes(req.StateLogEntryId + toAdd, req.StateLogEntryTerm), true, new byte[] { 1 }.ToBytes(req.StateLogEntryId + toAdd, ulong.MinValue), true, new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true).Take(2)) { cnt++; sle = StateLogEntry.BiserDecode(el.Value); if (cnt == 1) { prevId = sle.Index; prevTerm = sle.Term; } else { le.StateLogEntry = sle; } } } if (cnt == 2) { le.StateLogEntry.PreviousStateLogId = prevId; le.StateLogEntry.PreviousStateLogTerm = prevTerm; if ( LastCommittedIndexTerm >= le.StateLogEntry.Term && LastCommittedIndex >= le.StateLogEntry.Index ) { le.IsCommitted = true; } } } } if (cnt != 2) { return(null); } return(le); }