示例#1
0
            internal VotingState(IRaftClusterMember voter, long term, IAuditTrail <IRaftLogEntry> auditTrail, CancellationToken token)
            {
                Voter = voter;

                // ensure parallel requesting of votes
                Task = System.Threading.Tasks.Task.Run(() => VoteAsync(voter, term, auditTrail, token));
            }
示例#2
0
        //true if at least one entry from current term is stored on this node; otherwise, false
        private static async Task <Result <bool> > AppendEntriesAsync(IRaftClusterMember member, long commitIndex,
                                                                      long term,
                                                                      IAuditTrail <ILogEntry> transactionLog, ILogger logger, CancellationToken token)
        {
            var currentIndex = transactionLog.GetLastIndex(false);

            logger.ReplicationStarted(member.Endpoint, currentIndex);
            var precedingIndex = Math.Max(0, member.NextIndex - 1);
            var precedingTerm  = (await transactionLog.GetEntryAsync(precedingIndex).ConfigureAwait(false) ??
                                  transactionLog.First).Term;
            var entries = currentIndex >= member.NextIndex
                ? await transactionLog.GetEntriesAsync(member.NextIndex).ConfigureAwait(false)
                : Array.Empty <ILogEntry>();

            logger.ReplicaSize(member.Endpoint, entries.Count, precedingIndex, precedingTerm);
            //trying to replicate
            var result = await member
                         .AppendEntriesAsync(term, entries, precedingIndex, precedingTerm, commitIndex, token)
                         .ConfigureAwait(false);

            if (result.Value)
            {
                logger.ReplicationSuccessful(member.Endpoint, member.NextIndex);
                member.NextIndex.VolatileWrite(currentIndex + 1);
                //checks whether the at least one entry from current term is stored on this node
                result = result.SetValue(entries.Any(entry => entry.Term == term));
            }
            else
            {
                logger.ReplicationFailed(member.Endpoint, member.NextIndex.DecrementAndGet());
            }

            return(result);
        }
示例#3
0
 internal static async Task WaitForCommitAsync(IAuditTrail log, IAsyncEvent commitEvent, long index, TimeSpan timeout, CancellationToken token)
 {
     for (var timeoutMeasurement = new Timeout(timeout); log.GetLastIndex(true) < index; await commitEvent.Wait(timeout, token).ConfigureAwait(false))
     {
         timeoutMeasurement.ThrowIfExpired(out timeout);
     }
 }
示例#4
0
 internal ValueTask <Result <bool> > Start(IAuditTrail <IRaftLogEntry> auditTrail)
 {
     logger.ReplicationStarted(member.EndPoint, currentIndex);
     return(currentIndex >= member.NextIndex ?
            auditTrail.ReadAsync(this, member.NextIndex, token) :
            ReadAsync <EmptyLogEntry, EmptyLogEntry[]>(Array.Empty <EmptyLogEntry>(), null, token));
 }
		public LdmlVersion0MigrationStrategy(Action<int, IEnumerable<LdmlMigrationInfo>> migrationHandler, IAuditTrail auditLog, int fromVersion) :
			base(fromVersion, 2)
		{
			_migrationInfo = new List<LdmlMigrationInfo>();
			_writingSystemsV1 = new Dictionary<string, WritingSystemDefinitionV1>();
			_migrationHandler = migrationHandler;
			_auditLog = auditLog;
		}
		public LdmlVersion2MigrationStrategy(Action<int, IEnumerable<LdmlMigrationInfo>> migrationHandler, IAuditTrail auditLog) :
			base(2, 3)
		{
			_migrationInfo = new List<LdmlMigrationInfo>();
			_staging = new Dictionary<string, Staging>();
			_migrationHandler = migrationHandler;
			_auditLog = auditLog;
		}
 public LdmlVersion2MigrationStrategy(Action <int, IEnumerable <LdmlMigrationInfo> > migrationHandler, IAuditTrail auditLog) :
     base(2, 3)
 {
     _migrationInfo    = new List <LdmlMigrationInfo>();
     _staging          = new Dictionary <string, Staging>();
     _migrationHandler = migrationHandler;
     _auditLog         = auditLog;
 }
示例#8
0
        internal static async ValueTask <bool> IsUpToDateAsync(this IAuditTrail <ILogEntry> auditTrail, long index, long term)
        {
            var localIndex = auditTrail.GetLastIndex(false);
            var localTerm  = (await auditTrail.GetEntryAsync(localIndex).ConfigureAwait(false) ?? auditTrail.First)
                             .Term;

            return(index >= localIndex && term >= localTerm);
        }
示例#9
0
        internal static async ValueTask <LogEntry> GetEntryAsync <LogEntry>(this IAuditTrail <LogEntry> auditTrail, long index)
            where LogEntry : class, IMessage

        {
            var entries = await auditTrail.GetEntriesAsync(index, index).ConfigureAwait(false);

            return(entries.Count > 0 ? entries[0] : null);
        }
 public LdmlVersion0MigrationStrategy(Action <int, IEnumerable <LdmlMigrationInfo> > migrationHandler, IAuditTrail auditLog, int fromVersion) :
     base(fromVersion, 2)
 {
     _migrationInfo    = new List <LdmlMigrationInfo>();
     _writingSystemsV1 = new Dictionary <string, WritingSystemDefinitionV1>();
     _migrationHandler = migrationHandler;
     _auditLog         = auditLog;
 }
示例#11
0
 private async Task DoHeartbeats(TimeSpan period, IAuditTrail <IRaftLogEntry> auditTrail, CancellationToken token)
 {
     using var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(token, timerCancellation.Token);
     for (token = cancellationSource.Token; await DoHeartbeats(auditTrail, token).ConfigureAwait(false); await forcedReplication.WaitAsync(period, token).ConfigureAwait(false))
     {
         DrainReplicationQueue();
     }
 }
示例#12
0
 /// <summary>
 /// Starts cluster synchronization.
 /// </summary>
 /// <param name="period">Time period of Heartbeats.</param>
 /// <param name="transactionLog">Transaction log.</param>
 /// <param name="token">The toke that can be used to cancel the operation.</param>
 internal LeaderState StartLeading(TimeSpan period, IAuditTrail <IRaftLogEntry> transactionLog, CancellationToken token)
 {
     foreach (var member in stateMachine.Members)
     {
         member.NextIndex = transactionLog.GetLastIndex(false) + 1;
     }
     heartbeatTask = DoHeartbeats(period, transactionLog, token);
     return(this);
 }
示例#13
0
        private static async void OnCommitted(IAuditTrail <ILogEntry> sender, long startIndex, long count)
        {
            foreach (var entry in await sender.GetEntriesAsync(startIndex, startIndex + count).ConfigureAwait(false))
            {
                var content = await entry.ReadAsTextAsync().ConfigureAwait(false);

                Console.WriteLine($"Message '{content}' is committed at term {entry.Term}");
            }
        }
示例#14
0
            internal ValueTask <Result <bool> > Start(IAuditTrail <IRaftLogEntry> auditTrail)
            {
                var currentIndex = this.currentIndex = auditTrail.GetLastIndex(false);

                logger.ReplicationStarted(member.Endpoint, currentIndex);
                return(currentIndex >= member.NextIndex ?
                       auditTrail.ReadAsync <Replicator, Result <bool> >(this, member.NextIndex, token) :
                       ReadAsync <IRaftLogEntry, IRaftLogEntry[]>(Array.Empty <IRaftLogEntry>(), null, token));
            }
		public LdmlVersion0MigrationStrategy(
			MigrationHandler migrationHandler,
			IAuditTrail auditLog,
			int fromVersion,
			WritingSystemCompatibility roundtripBogusFlex70PrivateUse) :
			this(migrationHandler, auditLog, fromVersion)
		{
			_roundTripBogusFlex70PrivateUse = roundtripBogusFlex70PrivateUse;
		}
 public LdmlVersion0MigrationStrategy(MigrationHandler migrationHandler, IAuditTrail auditLog, int fromVersion) :
     base(fromVersion, 2)
 {
     Guard.AgainstNull(migrationHandler, "migrationCallback must be set");
     _migrationInfo    = new List <MigrationInfo>();
     _writingSystemsV1 = new Dictionary <string, WritingSystemDefinitionV1>();
     _migrationHandler = migrationHandler;
     _auditLog         = auditLog;
 }
 public LdmlVersion0MigrationStrategy(
     MigrationHandler migrationHandler,
     IAuditTrail auditLog,
     int fromVersion,
     WritingSystemCompatibility roundtripBogusFlex70PrivateUse) :
     this(migrationHandler, auditLog, fromVersion)
 {
     _roundTripBogusFlex70PrivateUse = roundtripBogusFlex70PrivateUse;
 }
		public LdmlVersion0MigrationStrategy(MigrationHandler migrationHandler, IAuditTrail auditLog, int fromVersion) :
			base(fromVersion, 2)
		{
			Guard.AgainstNull(migrationHandler, "migrationCallback must be set");
			_migrationInfo = new List<MigrationInfo>();
			_writingSystemsV1 = new Dictionary<string, WritingSystemDefinitionV1>();
			_migrationHandler = migrationHandler;
			_auditLog = auditLog;
		}
示例#19
0
        private async Task <bool> DoHeartbeats(IAuditTrail <ILogEntry> transactionLog)
        {
            ICollection <Task <Result <MemberHealthStatus> > > tasks = new LinkedList <Task <Result <MemberHealthStatus> > >();
            //send heartbeat in parallel
            var commitIndex = transactionLog.GetLastIndex(true);

            foreach (var member in stateMachine.Members)
            {
                if (member.IsRemote)
                {
                    tasks.Add(AppendEntriesAsync(member, commitIndex, currentTerm, transactionLog, stateMachine.Logger, timerCancellation.Token)
                              .ContinueWith(HealthStatusContinuation, default, TaskContinuationOptions.ExecuteSynchronously,
示例#20
0
        /// <summary>
        /// Add record to AuditTrail. Insert or Update action on a Table
        /// </summary>
        /// <param name="entity">Entity which was modified</param>
        /// <param name="operation">Insert or Update operation</param>
        /// <param name="audit">Audit Trail object of type IAuditTrail</param>
        /// <returns>true if success, False if fail</returns>
        public bool Add(Entity entity, RecordOperationEnum operation, IAuditTrail audit)
        {
            CreateTableIfNotExist();

            audit.OperationType = operation;
            audit.RecordId      = entityTableInfo.GetKeyId(entity).ToString();
            if (!entityTableInfo.NoVersionNo)
            {
                audit.RecordVersionNo = (operation == RecordOperationEnum.Insert ? 1 : entityTableInfo.GetVersionNo(entity)); //always 1 for new insert
            }
            audit.TableName = entityTableInfo.Name;

            return(AddAuditTrail((AuditTrailKeyValue)audit));
        }
示例#21
0
        /// <summary>
        /// Starts voting asynchronously.
        /// </summary>
        /// <param name="timeout">Candidate state timeout.</param>
        /// <param name="auditTrail">The local transaction log.</param>
        internal CandidateState StartVoting(int timeout, IAuditTrail <IRaftLogEntry> auditTrail)
        {
            stateMachine.Logger.VotingStarted(timeout);
            ICollection <VotingState> voters = new LinkedList <VotingState>();

            votingCancellation.CancelAfter(timeout);
            //start voting in parallel
            foreach (var member in stateMachine.Members)
            {
                voters.Add(new VotingState(member, Term, auditTrail, votingCancellation.Token));
            }
            votingTask = EndVoting(voters);
            return(this);
        }
示例#22
0
        /// <summary>
        /// Add record to AuditTrail. Insert or Update action on a Table
        /// </summary>
        /// <param name="entity">Entity which was modified</param>
        /// <param name="operation">Insert or Update operation</param>
        /// <param name="audit">Audit Trail object of type IAuditTrail</param>
        /// <returns>true if success, False if fail</returns>
        public bool Add(Entity entity, RecordOperationEnum operation, IAuditTrail audit)
        {
            CreateTableIfNotExist();

            AuditTrail auditTrail = (AuditTrail)audit;

            auditTrail.OperationType = operation;
            //Remove EntityBase 12-Apr-19
            auditTrail.RecordId = entityTableInfo.GetKeyId(entity).ToString();
            //Remove EntityBase 12-Apr-19
            if (!entityTableInfo.NoVersionNo)
            {
                auditTrail.RecordVersionNo = (operation == RecordOperationEnum.Insert ? 1 : entityTableInfo.GetVersionNo(entity)); //always 1 for new insert
            }
            auditTrail.TableName    = entityTableInfo.Name;
            auditTrail.Details      = auditTrail.GenerateString();
            auditTrail.AuditTrailId = (long)Add(auditTrail);

            return(true);
        }
 // TODO: Replace with required init properties in the next version of C#
 internal Replicator(
     IAuditTrail <IRaftLogEntry> auditTrail,
     IRaftClusterMember member,
     long commitIndex,
     long currentIndex,
     long term,
     long precedingIndex,
     long precedingTerm,
     ILogger logger,
     CancellationToken token)
 {
     this.auditTrail     = auditTrail;
     this.member         = member;
     this.precedingIndex = precedingIndex;
     this.precedingTerm  = precedingTerm;
     this.commitIndex    = commitIndex;
     this.currentIndex   = currentIndex;
     this.term           = term;
     this.logger         = logger;
     this.token          = token;
 }
示例#24
0
 public PsaUserService(
     IContextService <IPsaContext> arg0,
     IUserValidator arg1,
     IUniqueUserService arg2,
     IOrganizationUserRepository arg3,
     IUserRepository arg4,
     IUserAuthorization arg5,
     IUserMemberCopyService arg6,
     IFeatureService arg7,
     IAuditTrail <User> arg8,
     IUserSeatService arg9,
     IUserEmailBuilder arg10,
     IMailClient arg11,
     IMasterUserRepository arg12,
     IUserDefaultsService arg13,
     IOrganizationService arg14,
     IExternallyOwnedOrganizationService arg15,
     IUserAnonymizer arg16
     )
 {
     field0  = arg0;
     field1  = arg1;
     field2  = arg2;
     field3  = arg3;
     field4  = arg4;
     field5  = arg5;
     field6  = arg6;
     field7  = arg7;
     field8  = arg8;
     field9  = arg9;
     field10 = arg10;
     field11 = arg11;
     field12 = arg12;
     field13 = arg13;
     field14 = arg14;
     field15 = arg15;
     field16 = arg16;
 }
示例#25
0
        private async Task <bool> DoHeartbeats(IAuditTrail <IRaftLogEntry> auditTrail, CancellationToken token)
        {
            var timeStamp = Timestamp.Current;
            var tasks     = new LinkedList <ValueTask <Result <bool> > >();

            long commitIndex = auditTrail.GetLastIndex(true), currentIndex = auditTrail.GetLastIndex(false);
            var  term = currentTerm;

            //send heartbeat in parallel
            foreach (var member in stateMachine.Members)
            {
                if (member.IsRemote)
                {
                    long precedingIndex = Math.Max(0, member.NextIndex - 1), precedingTerm = await auditTrail.GetTermAsync(precedingIndex, token);

                    tasks.AddLast(new Replicator(member, commitIndex, currentIndex, term, precedingIndex, precedingTerm, stateMachine.Logger, token).Start(auditTrail));
                }
            }
            var quorum       = 1; //because we know that the entry is replicated in this node
            var commitQuorum = 1;

            for (var task = tasks.First; task != null; task.Value = default, task = task.Next)
            {
                try
                {
                    var result = await task.Value.ConfigureAwait(false);

                    term          = Math.Max(term, result.Term);
                    quorum       += 1;
                    commitQuorum += result.Value ? 1 : -1;
                }
                catch (MemberUnavailableException)
                {
                    quorum       -= 1;
                    commitQuorum -= 1;
                }
            }
示例#26
0
        internal virtual void CreateAddCommand(IDbCommand cmd, object entity, IAuditTrail audit = null, string columnNames = null, bool doNotAppendCommonFields = false, bool overrideCreatedUpdatedOn = false)
        {
            TableAttribute tableInfo = EntityCache.Get(entity.GetType());

            if (tableInfo.NeedsHistory && tableInfo.IsCreatedByEmpty(entity))
            {
                throw new MissingFieldException("CreatedBy is required when Audit Trail is enabled");
            }

            List <string> columns = new List <string>();

            if (!string.IsNullOrEmpty(columnNames))
            {
                columns.AddRange(columnNames.Split(','));
            }
            else
            {
                columns.AddRange(tableInfo.DefaultInsertColumns); //Get columns from Entity attributes loaded in TableInfo
            }
            bool isPrimaryKeyEmpty = false;

            foreach (ColumnAttribute pkCol in tableInfo.Columns.Where(p => p.Value.IsPrimaryKey).Select(p => p.Value))
            {
                if (pkCol.PrimaryKeyInfo.IsIdentity && tableInfo.IsKeyIdEmpty(entity, pkCol))
                {
                    isPrimaryKeyEmpty = true;
                    //if identity remove keyfield if added in field list
                    columns.Remove(pkCol.Name);
                }
                else if (pkCol.Property.PropertyType == typeof(Guid) && tableInfo.IsKeyIdEmpty(entity, pkCol))
                {
                    isPrimaryKeyEmpty = true;
                    //if not identity and key not generated, generate before save
                    tableInfo.SetKeyId(entity, pkCol, Guid.NewGuid());
                }
            }

            #region append common columns

            if (!doNotAppendCommonFields)
            {
                if (!tableInfo.NoIsActive)
                {
                    if (!columns.Contains(Config.ISACTIVE_COLUMN.Name))
                    {
                        columns.Add(Config.ISACTIVE_COLUMN.Name);
                    }

                    bool isActive = tableInfo.GetIsActive(entity) ?? true;
                    //when IsActive is not set then true for insert
                    cmd.AddInParameter("@" + Config.ISACTIVE_COLUMN.Name, Config.ISACTIVE_COLUMN.ColumnDbType, isActive);

                    if (tableInfo.NeedsHistory)
                    {
                        audit.AppendDetail(Config.ISACTIVE_COLUMN.Name, isActive, DbType.Boolean, null);
                    }

                    tableInfo.SetIsActive(entity, isActive); //Set IsActive value
                }

                if (!tableInfo.NoVersionNo)
                {
                    int versionNo = tableInfo.GetVersionNo(entity) ?? 1;  //set defualt versionno 1 for Insert
                    if (versionNo == 0)
                    {
                        versionNo = 1;                 //set defualt versionno 1 for Insert even if its zero or null
                    }
                    if (!columns.Contains(Config.VERSIONNO_COLUMN.Name))
                    {
                        columns.Add(Config.VERSIONNO_COLUMN.Name);
                    }

                    cmd.AddInParameter("@" + Config.VERSIONNO_COLUMN.Name, Config.VERSIONNO_COLUMN.ColumnDbType, versionNo);

                    tableInfo.SetVersionNo(entity, versionNo); //Set VersionNo value
                }

                if (!tableInfo.NoCreatedBy)
                {
                    if (!columns.Contains(Config.CREATEDBY_COLUMN.Name))
                    {
                        columns.Add(Config.CREATEDBY_COLUMN.Name);
                    }

                    cmd.AddInParameter("@" + Config.CREATEDBY_COLUMN.Name, Config.CREATEDBY_COLUMN.ColumnDbType, tableInfo.GetCreatedBy(entity));
                }

                if (!tableInfo.NoCreatedOn & !columns.Contains(Config.CREATEDON_COLUMN.Name))
                {
                    columns.Add(Config.CREATEDON_COLUMN.Name);
                }

                if (!tableInfo.NoUpdatedBy)
                {
                    if (!columns.Contains(Config.UPDATEDBY_COLUMN.Name))
                    {
                        columns.Add(Config.UPDATEDBY_COLUMN.Name);
                    }

                    cmd.AddInParameter("@" + Config.UPDATEDBY_COLUMN.Name, Config.UPDATEDBY_COLUMN.ColumnDbType, tableInfo.GetCreatedBy(entity));
                }

                if (!tableInfo.NoUpdatedOn & !columns.Contains(Config.UPDATEDON_COLUMN.Name))
                {
                    columns.Add(Config.UPDATEDON_COLUMN.Name);
                }
            }

            #endregion

            //append @ before each fields to add as parameter
            List <string> parameters = columns.Select(c => "@" + c).ToList();

            int pIndex = parameters.FindIndex(c => c == "@" + Config.CREATEDON_COLUMN.Name);
            if (pIndex >= 0)
            {
                var createdOn = Helper.GetDateTimeOrDatabaseDateTimeSQL(tableInfo.GetCreatedOn(entity), this, overrideCreatedUpdatedOn);
                if (createdOn is string)
                {
                    parameters[pIndex] = (string)createdOn;
                }
                else
                {
                    cmd.AddInParameter(parameters[pIndex], Config.CREATEDON_COLUMN.ColumnDbType, createdOn);
                }
                //parameters[pIndex] = (string)Helper.GetDateTimeOrDatabaseDateTimeSQL(tableInfo.GetCreatedOn(entity), this, overrideCreatedUpdatedOn);
            }

            pIndex = parameters.FindIndex(c => c == "@" + Config.UPDATEDON_COLUMN.Name);
            if (pIndex >= 0)
            {
                var updatedOn = Helper.GetDateTimeOrDatabaseDateTimeSQL(tableInfo.GetUpdatedOn(entity), this, overrideCreatedUpdatedOn);
                if (updatedOn is string)
                {
                    parameters[pIndex] = (string)updatedOn;
                }
                else
                {
                    cmd.AddInParameter(parameters[pIndex], Config.CREATEDON_COLUMN.ColumnDbType, updatedOn);
                }
                //parameters[pIndex] = (string)Helper.GetDateTimeOrDatabaseDateTimeSQL(tableInfo.GetUpdatedOn(entity), this, overrideCreatedUpdatedOn);
            }

            StringBuilder cmdText = new StringBuilder();
            cmdText.Append($"INSERT INTO {tableInfo.FullName} ({string.Join(",", columns)}) VALUES({string.Join(",", parameters)});");

            if (tableInfo.IsKeyIdentity() && isPrimaryKeyEmpty)
            {
                //add query to get inserted id
                cmdText.Append(LASTINSERTEDROWIDSQL);
            }

            //remove common columns and parameters already added above
            columns.RemoveAll(c => c == Config.CREATEDON_COLUMN.Name || c == Config.CREATEDBY_COLUMN.Name ||
                              c == Config.UPDATEDON_COLUMN.Name || c == Config.UPDATEDBY_COLUMN.Name ||
                              c == Config.VERSIONNO_COLUMN.Name || c == Config.ISACTIVE_COLUMN.Name);

            cmd.CommandType = CommandType.Text;
            cmd.CommandText = cmdText.ToString();

            for (int i = 0; i < columns.Count(); i++)
            {
                tableInfo.Columns.TryGetValue(columns[i], out ColumnAttribute columnInfo); //find column attribute

                DbType dbType      = DbType.Object;
                object columnValue = null;

                if (columnInfo != null && columnInfo.GetMethod != null)
                {
                    dbType      = columnInfo.ColumnDbType;
                    columnValue = columnInfo.GetAction(entity);

                    if (tableInfo.NeedsHistory)
                    {
                        audit.AppendDetail(columns[i], columnValue, dbType, null);
                    }
                }
                cmd.AddInParameter("@" + columns[i], dbType, columnValue);
            }
        }
示例#27
0
 internal static async Task <bool> ContainsAsync(this IAuditTrail <IRaftLogEntry> auditTrail, long index, long term, CancellationToken token)
 => index <= auditTrail.GetLastIndex(false) && term == await auditTrail.GetTermAsync(index, token).ConfigureAwait(false);
示例#28
0
        internal static async Task <bool> IsUpToDateAsync(this IAuditTrail <IRaftLogEntry> auditTrail, long index, long term, CancellationToken token)
        {
            var localIndex = auditTrail.GetLastIndex(false);

            return(index >= localIndex && term >= await auditTrail.GetTermAsync(localIndex, token).ConfigureAwait(false));
        }
示例#29
0
 internal static ValueTask <long> GetTermAsync(this IAuditTrail <IRaftLogEntry> auditTrail, long index, CancellationToken token)
 => auditTrail.ReadAsync <TermReader, long>(new TermReader(), index, index, token);
示例#30
0
 internal static async ValueTask <bool> ContainsAsync(this IAuditTrail <ILogEntry> auditTrail, long index,
                                                      long term)
 => term == (await auditTrail.GetEntryAsync(index).ConfigureAwait(false) ?? auditTrail.First).Term;
示例#31
0
        private async Task <bool> DoHeartbeats(IAuditTrail <IRaftLogEntry> auditTrail, CancellationToken token)
        {
            var timeStamp = Timestamp.Current;
            var tasks     = new AsyncResultSet();

            long commitIndex       = auditTrail.GetLastIndex(true),
                 currentIndex      = auditTrail.GetLastIndex(false),
                 term              = currentTerm,
                 minPrecedingIndex = 0L;

            // send heartbeat in parallel
            foreach (var member in stateMachine.Members)
            {
                if (member.IsRemote)
                {
                    long precedingIndex = Math.Max(0, member.NextIndex - 1), precedingTerm;
                    minPrecedingIndex = Math.Min(minPrecedingIndex, precedingIndex);

                    // try to get term from the cache to avoid touching audit trail for each member
                    if (!precedingTermCache.TryGetValue(precedingIndex, out precedingTerm))
                    {
                        precedingTermCache.Add(precedingIndex, precedingTerm = await auditTrail.GetTermAsync(precedingIndex, token).ConfigureAwait(false));
                    }

                    tasks.AddLast(new Replicator(member, commitIndex, currentIndex, term, precedingIndex, precedingTerm, stateMachine.Logger, token).Start(auditTrail));
                }
            }

            // clear cache
            if (precedingTermCache.Count > MaxTermCacheSize)
            {
                precedingTermCache.Clear();
            }
            else
            {
                precedingTermCache.RemoveHead(minPrecedingIndex);
            }

            int quorum = 1, commitQuorum = 1; // because we know that the entry is replicated in this node

#if NETSTANDARD2_1
            for (var task = tasks.First; task is not null; task.Value = default, task = task.Next)
#else
            for (var task = tasks.First; task is not null; task.ValueRef = default, task = task.Next)
#endif
            {
                try
                {
#if NETSTANDARD2_1
                    var result = await task.Value.ConfigureAwait(false);
#else
                    var result = await task.ValueRef.ConfigureAwait(false);
#endif
                    term          = Math.Max(term, result.Term);
                    quorum       += 1;
                    commitQuorum += result.Value ? 1 : -1;
                }
                catch (MemberUnavailableException)
                {
                    quorum       -= 1;
                    commitQuorum -= 1;
                }
                catch (OperationCanceledException)
                {
                    // leading was canceled
                    tasks.Clear();
                    Metrics?.ReportBroadcastTime(timeStamp.Elapsed);
                    return(false);
                }
                catch (Exception e)
                {
                    stateMachine.Logger.LogError(e, ExceptionMessages.UnexpectedError);
                }
            }

            tasks.Clear();
            Metrics?.ReportBroadcastTime(timeStamp.Elapsed);

            // majority of nodes accept entries with a least one entry from the current term
            if (commitQuorum > 0)
            {
                var count = await auditTrail.CommitAsync(currentIndex, token).ConfigureAwait(false); // commit all entries started from first uncommitted index to the end

                stateMachine.Logger.CommitSuccessful(commitIndex + 1, count);
                goto check_term;
            }

            stateMachine.Logger.CommitFailed(quorum, commitIndex);

            // majority of nodes replicated, continue leading if current term is not changed
            if (quorum <= 0 && !allowPartitioning)
            {
                goto stop_leading;
            }

check_term:
            if (term <= currentTerm)
            {
                return(true);
            }

            // it is partitioned network with absolute majority, not possible to have more than one leader
stop_leading:
            stateMachine.MoveToFollowerState(false, term);
            return(false);
        }
            private static async Task <Result <VotingResult> > VoteAsync(IRaftClusterMember voter, long term, IAuditTrail <IRaftLogEntry> auditTrail, CancellationToken token)
            {
                var lastIndex = auditTrail.GetLastIndex(false);
                var lastTerm  = await auditTrail.GetTermAsync(lastIndex, token).ConfigureAwait(false);

                VotingResult result;

                try
                {
                    var response = await voter.VoteAsync(term, lastIndex, lastTerm, token).ConfigureAwait(false);

                    term   = response.Term;
                    result = response.Value ? VotingResult.Granted : VotingResult.Rejected;
                }
                catch (OperationCanceledException)
                {
                    result = VotingResult.Canceled;
                }
                catch (MemberUnavailableException)
                {
                    result = VotingResult.NotAvailable;
                    term   = -1L;
                }

                return(new Result <VotingResult>(term, result));
            }
 internal VotingState(IRaftClusterMember voter, long term, IAuditTrail <IRaftLogEntry> auditTrail, CancellationToken token)
 {
     Voter = voter;
     Task  = VoteAsync(voter, term, auditTrail, token);
 }
示例#34
0
        internal virtual bool CreateUpdateCommand(IDbCommand cmd, object entity, object oldEntity, IAuditTrail audit = null, string columnNames = null, bool doNotAppendCommonFields = false, bool overrideCreatedUpdatedOn = false)
        {
            bool isUpdateNeeded = false;

            TableAttribute tableInfo = EntityCache.Get(entity.GetType());

            if (!tableInfo.NoUpdatedBy && tableInfo.IsUpdatedByEmpty(entity))
            {
                throw new MissingFieldException("Updated By is required");
            }

            List <string> columns = new List <string>();

            if (!string.IsNullOrEmpty(columnNames))
            {
                columns.AddRange(columnNames.Split(','));
            }
            else
            {
                columns.AddRange(tableInfo.DefaultUpdateColumns); //Get columns from Entity attributes loaded in TableInfo
            }
            StringBuilder cmdText = new StringBuilder();

            cmdText.Append($"UPDATE {tableInfo.FullName} SET ");

            //add default columns if doesn't exists
            if (!doNotAppendCommonFields)
            {
                if (!tableInfo.NoVersionNo && !columns.Contains(Config.VERSIONNO_COLUMN.Name))
                {
                    columns.Add(Config.VERSIONNO_COLUMN.Name);
                }

                if (!tableInfo.NoUpdatedBy && !columns.Contains(Config.UPDATEDBY_COLUMN.Name))
                {
                    columns.Add(Config.UPDATEDBY_COLUMN.Name);
                }

                if (!tableInfo.NoUpdatedOn && !columns.Contains(Config.UPDATEDON_COLUMN.Name))
                {
                    columns.Add(Config.UPDATEDON_COLUMN.Name);
                }
            }

            //remove primarykey, createdon and createdby columns if exists
            columns.RemoveAll(c => tableInfo.PkColumnList.Select(p => p.Name).Contains(c));
            columns.RemoveAll(c => c == Config.CREATEDON_COLUMN.Name ||
                              c == Config.CREATEDBY_COLUMN.Name);

            for (int i = 0; i < columns.Count(); i++)
            {
                if (columns[i].Equals(Config.VERSIONNO_COLUMN.Name, StringComparison.OrdinalIgnoreCase))
                {
                    cmdText.Append($"{columns[i]} = {columns[i]}+1");
                    cmdText.Append(",");
                }
                else if (columns[i].Equals(Config.UPDATEDBY_COLUMN.Name, StringComparison.OrdinalIgnoreCase))
                {
                    cmdText.Append($"{columns[i]} = @{columns[i]}");
                    cmdText.Append(",");
                    cmd.AddInParameter("@" + columns[i], Config.UPDATEDBY_COLUMN.ColumnDbType, tableInfo.GetUpdatedBy(entity));
                }
                else if (columns[i].Equals(Config.UPDATEDON_COLUMN.Name, StringComparison.OrdinalIgnoreCase))
                {
                    var updatedOn = Helper.GetDateTimeOrDatabaseDateTimeSQL(tableInfo.GetUpdatedOn(entity), this, overrideCreatedUpdatedOn);
                    if (updatedOn is string)
                    {
                        cmdText.Append($"{columns[i]} = {CURRENTDATETIMESQL}");
                    }
                    else
                    {
                        cmdText.Append($"{columns[i]} = @{columns[i]}");
                        cmd.AddInParameter("@" + columns[i], Config.UPDATEDON_COLUMN.ColumnDbType, updatedOn);
                    }
                    cmdText.Append(",");
                }
                else
                {
                    bool includeInUpdate = true;
                    tableInfo.Columns.TryGetValue(columns[i], out ColumnAttribute columnInfo); //find column attribute

                    DbType dbType      = DbType.Object;
                    object columnValue = null;

                    if (columnInfo != null && columnInfo.GetMethod != null)
                    {
                        dbType      = columnInfo.ColumnDbType;
                        columnValue = columnInfo.GetAction(entity);

                        includeInUpdate = oldEntity == null; //include in update when oldEntity not available

                        //compare with old object to check whether update is needed or not
                        object oldColumnValue = null;
                        if (oldEntity != null)
                        {
                            oldColumnValue = columnInfo.GetAction(oldEntity);

                            if (oldColumnValue != null && columnValue != null)
                            {
                                if (!oldColumnValue.Equals(columnValue)) //add to history only if property is modified
                                {
                                    includeInUpdate = true;
                                }
                            }
                            else if (oldColumnValue == null && columnValue != null)
                            {
                                includeInUpdate = true;
                            }
                            else if (oldColumnValue != null)
                            {
                                includeInUpdate = true;
                            }
                        }

                        if (tableInfo.NeedsHistory && includeInUpdate)
                        {
                            audit.AppendDetail(columns[i], columnValue, dbType, oldColumnValue);
                        }
                    }

                    if (includeInUpdate)
                    {
                        isUpdateNeeded = true;

                        cmdText.Append($"{columns[i]} = @{columns[i]}");
                        cmdText.Append(",");
                        cmd.AddInParameter("@" + columns[i], dbType, columnValue);
                    }
                }
            }
            cmdText.RemoveLastComma(); //Remove last comma if exists

            cmdText.Append(" WHERE ");
            if (tableInfo.PkColumnList.Count > 1)
            {
                int index = 0;
                foreach (ColumnAttribute pkCol in tableInfo.PkColumnList)
                {
                    cmdText.Append($" {(index > 0 ? " AND " : "")} {pkCol.Name}=@{pkCol.Name}");
                    cmd.AddInParameter("@" + pkCol.Name, pkCol.ColumnDbType, tableInfo.GetKeyId(entity, pkCol));
                    index++;
                }
            }
            else
            {
                cmdText.Append($" {tableInfo.PkColumn.Name}=@{tableInfo.PkColumn.Name}");
                cmd.AddInParameter("@" + tableInfo.PkColumn.Name, tableInfo.PkColumn.ColumnDbType, tableInfo.GetKeyId(entity));
            }

            if (Config.DbConcurrencyCheck && !tableInfo.NoVersionNo)
            {
                cmdText.Append($" AND {Config.VERSIONNO_COLUMN.Name}=@{Config.VERSIONNO_COLUMN.Name}");
                cmd.AddInParameter("@" + Config.VERSIONNO_COLUMN.Name, Config.VERSIONNO_COLUMN.ColumnDbType, tableInfo.GetVersionNo(entity));
            }

            cmd.CommandType = CommandType.Text;
            cmd.CommandText = cmdText.ToString();

            return(isUpdateNeeded);
        }