public static StateLogEntry 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 { decoder = new Biser.Decoder(extDecoder); if (decoder.IsNull) { return(null); } } StateLogEntry m = new StateLogEntry(); //!!!!!!!!!!!!!! change return type m.Term = decoder.GetULong(); m.Index = decoder.GetULong(); m.Data = decoder.GetByteArray(); m.IsCommitted = decoder.GetBool(); m.PreviousStateLogId = decoder.GetULong(); m.PreviousStateLogTerm = decoder.GetULong(); return(m); }
/// <summary> /// + /// Get Term by EntryLogIndex. Returns First element false if not found, Second - Term (if found). /// Must be called inside of operation lock. /// </summary> /// <param name="logId"></param> /// <returns>first element true if exists</returns> public StateLogEntry GetEntryByIndexTerm(ulong logEntryId, ulong logEntryTerm) { try { Tuple <ulong, StateLogEntry> sleTpl; if ( rn.entitySettings.DelayedPersistenceIsActive && sleCache.TryGetValue(logEntryId, out sleTpl) && sleTpl.Item1 == logEntryTerm ) { return(sleTpl.Item2); } using (var t = this.rn.db.GetTransaction()) { var row = t.Select <byte[], byte[]>(tblStateLogEntry, new byte[] { 1 }.ToBytes(logEntryId, logEntryTerm)); if (!row.Exists) { //foreach (var el in t.SelectForwardStartsWith<byte[], byte[]>(tblStateLogEntry, new byte[] { 1 })) //{ // Console.WriteLine($"{rn.NodeAddress.NodeAddressId}> GetEntryByIndexTerm-NULL: {el.Key.ToBytesString()}"); //} return(null); } return(StateLogEntry.BiserDecode(row.Value)); } } catch (Exception ex) { throw ex; } }
public LiteLog(RaftStateMachine rn, string workPath) { this.statemachine = rn; db = new LiteDatabase(workPath); stateTableName += "_" + rn.entitySettings.EntityName; var col = this.db.GetCollection <StateLogEntry>(this.stateTableName); var list = col.Query().ToList(); var toprow = list.OrderByDescending(s => s.Index).OrderByDescending(s => s.Term).FirstOrDefault(); StateLogEntry sle = null; if (toprow != null) { sle = toprow; StateLogId = sle.Index; StateLogTerm = sle.Term; PreviousStateLogId = sle.PreviousStateLogId; PreviousStateLogTerm = sle.PreviousStateLogTerm; tempPrevStateLogId = PreviousStateLogId; tempPrevStateLogTerm = PreviousStateLogTerm; tempStateLogId = StateLogId; tempStateLogTerm = StateLogTerm; rn.NodeTerm = sle.Term; } toprow = col.Query().ToList().OrderByDescending(s => s.Index).OrderByDescending(s => s.Term).FirstOrDefault(); if (toprow != null) { LastCommittedIndex = toprow.Index; LastCommittedIndexTerm = toprow.Term; } }
/// <summary> /// Is called from lock_operations /// Adds to silo table, until is moved to log table. /// This table can be cleared up on start /// returns concatenated term+index inserted identifier /// </summary> /// <param name="data"></param> /// <param name="externalID">if set up must be returned in OnCommitted to notify that command is executed</param> /// <returns></returns> public void AddStateLogEntryForDistribution(byte[] data, byte[] externalID = null) { /* * Only nodes of the current term can be distributed */ tempPrevStateLogId = tempStateLogId; tempPrevStateLogTerm = tempStateLogTerm; tempStateLogId++; tempStateLogTerm = rn.NodeTerm; StateLogEntry le = new StateLogEntry() { Index = tempStateLogId, Data = data, Term = tempStateLogTerm, PreviousStateLogId = tempPrevStateLogId, PreviousStateLogTerm = tempPrevStateLogTerm, ExternalID = externalID }; qDistribution.Add(le.Index, le); }
/// <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); }
/// <summary> /// + /// Must be called inside of operation lock. /// </summary> /// <param name="logEntryId"></param> /// <returns></returns> public StateLogEntry GetCommitedEntryByIndex(ulong logEntryId) { try { if (this.LastCommittedIndex < logEntryId) { return(null); } using (var t = this.db.GetTransaction()) { foreach (var el in t.SelectForwardFromTo <byte[], byte[]>(stateTableName, new byte[] { 1 }.ToBytes(logEntryId, ulong.MinValue), true, new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true, true)) { return(StateLogEntry.BiserDecode(el.Value)); } } return(null); } catch (Exception ex) { throw ex; } }
public static StateLogEntrySuggestion 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; } } StateLogEntrySuggestion m = new StateLogEntrySuggestion(); //!!!!!!!!!!!!!! change return type m.LeaderTerm = decoder.GetULong(); m.StateLogEntry = StateLogEntry.BiserDecode(extDecoder: decoder); m.IsCommitted = decoder.GetBool(); return(m); }
public StateLog(RaftNode rn) { this.rn = rn; //if (rn.nodeSettings.InMemoryEntity) // if (String.IsNullOrEmpty(dbreezePath) || rn.nodeSettings.InMemoryEntity) // db = new DBreezeEngine(new DBreezeConfiguration { Storage = DBreezeConfiguration.eStorage.MEMORY }); //else // db = new DBreezeEngine(dbreezePath); if (rn.entitySettings.EntityName != "default") { tblStateLogEntry += "_" + rn.entitySettings.EntityName; } if (rn.entitySettings.InMemoryEntity) { tblStateLogEntry = "mem_" + tblStateLogEntry; } using (var t = this.rn.db.GetTransaction()) { var row = t.SelectBackwardFromTo <byte[], byte[]>(tblStateLogEntry, new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true, new byte[] { 1 }.ToBytes(ulong.MinValue, ulong.MinValue), true) .FirstOrDefault(); StateLogEntry sle = null; if (row != null && row.Exists) { sle = StateLogEntry.BiserDecode(row.Value); StateLogId = sle.Index; StateLogTerm = sle.Term; PreviousStateLogId = sle.PreviousStateLogId; PreviousStateLogTerm = sle.PreviousStateLogTerm; tempPrevStateLogId = PreviousStateLogId; tempPrevStateLogTerm = PreviousStateLogTerm; tempStateLogId = StateLogId; tempStateLogTerm = StateLogTerm; rn.NodeTerm = sle.Term; } var rowTerm = t.Select <byte[], byte[]>(tblStateLogEntry, new byte[] { 2 }); if (rowTerm.Exists) { LastCommittedIndex = rowTerm.Value.Substring(0, 8).To_UInt64_BigEndian(); LastCommittedIndexTerm = rowTerm.Value.Substring(8, 8).To_UInt64_BigEndian(); } var rowBL = t.Select <byte[], ulong>(tblStateLogEntry, new byte[] { 3 }); if (rowBL.Exists) { LastBusinessLogicCommittedIndex = rowBL.Value; } } }
internal void Commited() { if (System.Threading.Interlocked.CompareExchange(ref inCommit, 1, 0) != 0) { return; } Task.Run(() => { StateLogEntry sle = null; while (true) { lock (lock_Operations) { if (this.NodeStateLog.LastCommittedIndex == this.NodeStateLog.LastBusinessLogicCommittedIndex) { System.Threading.Interlocked.Exchange(ref inCommit, 0); return; } else { sle = this.NodeStateLog.GetCommitedEntryByIndex(this.NodeStateLog.LastBusinessLogicCommittedIndex + 1); if (sle == null) { System.Threading.Interlocked.Exchange(ref inCommit, 0); return; } } } try { if (this.OnCommit(entitySettings.EntityName, sle.Index, sle.Data)) { //In case if business logic commit was successful lock (lock_Operations) { this.NodeStateLog.BusinessLogicIsApplied(sle.Index); } } else { System.Threading.Thread.Sleep(500); //repeating with the same id } } catch (Exception ex) { Log.Log(new WarningLogEntry() { Exception = ex, Method = "Raft.RaftNode.Commited" }); } //i++; } }); }
/// <summary> /// + /// Must be called inside of operation lock. /// </summary> /// <param name="logEntryId"></param> /// <returns></returns> public StateLogEntry GetCommitedEntryByIndex(ulong logEntryId) { try { if (this.LastCommittedIndex < logEntryId) { return(null); } Tuple <ulong, StateLogEntry> sleTpl; if ( rn.entitySettings.DelayedPersistenceIsActive && sleCache.TryGetValue(logEntryId, out sleTpl) ) { return(sleTpl.Item2); } if (rn.entitySettings.InMemoryEntity) { StateLogEntry isle = null; lock (inMem.Sync) { foreach (var el in inMem.SelectForwardFromTo(logEntryId, ulong.MinValue, true, ulong.MaxValue, ulong.MaxValue)) { if (el.Item3.FakeEntry) { continue; } return(el.Item3); } } return(isle); } using (var t = this.rn.db.GetTransaction()) { foreach (var el in t.SelectForwardFromTo <byte[], byte[]>(tblStateLogEntry, new byte[] { 1 }.ToBytes(logEntryId, ulong.MinValue), true, new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true, true)) { return(StateLogEntry.BiserDecode(el.Value)); } } return(null); } catch (Exception ex) { throw ex; } }
public StateLog(RaftStateMachine rn, DBreezeEngine dbEngine) { this.statemachine = rn; this.db = dbEngine; if (rn.entitySettings.EntityName != "default") { stateTableName += "_" + rn.entitySettings.EntityName; } using (var t = this.db.GetTransaction()) { var row = t.SelectBackwardFromTo <byte[], byte[]>(stateTableName, new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true, new byte[] { 1 }.ToBytes(ulong.MinValue, ulong.MinValue), true) .FirstOrDefault(); StateLogEntry sle = null; if (row != null && row.Exists) { sle = StateLogEntry.BiserDecode(row.Value); StateLogId = sle.Index; StateLogTerm = sle.Term; PreviousStateLogId = sle.PreviousStateLogId; PreviousStateLogTerm = sle.PreviousStateLogTerm; //tempPrevStateLogId = PreviousStateLogId; //tempPrevStateLogTerm = PreviousStateLogTerm; //tempStateLogId = StateLogId; //tempStateLogTerm = StateLogTerm; rn.NodeTerm = sle.Term; } var rowTerm = t.Select <byte[], byte[]>(stateTableName, new byte[] { 2 }); if (rowTerm.Exists) { LastCommittedIndex = rowTerm.Value.Substring(0, 8).To_UInt64_BigEndian(); LastCommittedIndexTerm = rowTerm.Value.Substring(8, 8).To_UInt64_BigEndian(); } var rowBL = t.Select <byte[], ulong>(stateTableName, new byte[] { 3 }); if (rowBL.Exists) { LastBusinessLogicCommittedIndex = rowBL.Value; } } }
/// <summary> /// + /// Get Term by EntryLogIndex. Returns First element false if not found, Second - Term (if found). /// Must be called inside of operation lock. /// </summary> /// <param name="logId"></param> /// <returns>first element true if exists</returns> public StateLogEntry GetEntryByIndexTerm(ulong logEntryId, ulong logEntryTerm) { try { Tuple <ulong, StateLogEntry> sleTpl; using (var t = this.db.GetTransaction()) { var row = t.Select <byte[], byte[]>(stateTableName, new byte[] { 1 }.ToBytes(logEntryId, logEntryTerm)); if (!row.Exists) { return(null); } return(StateLogEntry.BiserDecode(row.Value)); } } catch (Exception ex) { throw ex; } }
/// <summary> /// + /// Must be called inside of operation lock. /// </summary> /// <param name="logEntryId"></param> /// <returns></returns> public StateLogEntry GetCommitedEntryByIndex(ulong logEntryId) { try { if (this.LastCommittedIndex < logEntryId) { return(null); } Tuple <ulong, StateLogEntry> sleTpl; if ( rn.nodeSettings.DelayedPersistenceIsActive && sleCache.TryGetValue(logEntryId, out sleTpl) ) { return(sleTpl.Item2); } using (var t = db.GetTransaction()) { foreach (var el in t.SelectForwardFromTo <byte[], byte[]>(tblStateLogEntry, new byte[] { 1 }.ToBytes(logEntryId, ulong.MinValue), true, new byte[] { 1 }.ToBytes(ulong.MaxValue, ulong.MaxValue), true, true)) { return(StateLogEntry.BiserDecode(el.Value)); } } return(null); } catch (Exception ex) { throw ex; } }
/// <summary> /// Is called from lock_operations /// Adds to silo table, until is moved to log table. /// This table can be cleared up on start /// returns concatenated term+index inserted identifier /// </summary> /// <param name="data"></param> /// <returns></returns> public void AddStateLogEntryForDistribution(byte[] data)//, ulong redirectId=0) { /* * Only nodes of the current term can be distributed */ tempPrevStateLogId = tempStateLogId; tempPrevStateLogTerm = tempStateLogTerm; tempStateLogId++; tempStateLogTerm = rn.NodeTerm; StateLogEntry le = new StateLogEntry() { Index = tempStateLogId, Data = data, Term = tempStateLogTerm, PreviousStateLogId = tempPrevStateLogId, PreviousStateLogTerm = tempPrevStateLogTerm }; qDistribution.Add(le.Index, le); }
/// <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); }
/// <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); }