public virtual async Task <int> Purge(TableBasedQueue queue, CancellationToken cancellationToken = default) { using (var connection = await connectionFactory.OpenNewConnection(cancellationToken).ConfigureAwait(false)) { return(await queue.Purge(connection, cancellationToken).ConfigureAwait(false)); } }
public async Task Purge(TableBasedQueue queue, CancellationToken cancellationToken) { Logger.DebugFormat("Starting a new expired message purge task for table {0}.", queue); var totalPurgedRowsCount = 0; try { using (var connection = await openConnection(queue).ConfigureAwait(false)) { var continuePurging = true; while (continuePurging && !cancellationToken.IsCancellationRequested) { var purgedRowsCount = await queue.PurgeBatchOfExpiredMessages(connection, purgeBatchSize).ConfigureAwait(false); totalPurgedRowsCount += purgedRowsCount; continuePurging = purgedRowsCount == purgeBatchSize; } } Logger.DebugFormat("{0} expired messages were successfully purged from table {1}", totalPurgedRowsCount, queue); } catch { Logger.WarnFormat("Purging expired messages from table {0} failed after purging {1} messages.", queue, totalPurgedRowsCount); throw; } }
public virtual async Task <int> Purge(TableBasedQueue queue) { using (var connection = await connectionFactory.OpenNewConnection().ConfigureAwait(false)) { return(await queue.Purge(connection).ConfigureAwait(false)); } }
Task VerifyNonClusteredRowVersionIndex(TableBasedQueue queue) { return(VerifyIndex( queue, (q, c) => q.CheckNonClusteredRowVersionIndexPresence(c), $"Table {queue.Name} does not contain non-clustered index 'Index_RowVersion'.{Environment.NewLine}Migrating to this non-clustered index improves performance for send and receive operations.")); }
Task VerifyExpiredIndex(TableBasedQueue queue) { return(VerifyIndex( queue, (q, c) => q.CheckExpiresIndexPresence(c), $"Table {queue.Name} does not contain index 'Index_Expires'.{Environment.NewLine}Adding this index will speed up the process of purging expired messages from the queue. Please consult the documentation for further information." )); }
public void Init(TableBasedQueue inputQueue, TableBasedQueue errorQueue, Func <MessageContext, Task> onMessage, Func <ErrorContext, Task <ErrorHandleResult> > onError, CriticalError criticalError) { InputQueue = inputQueue; ErrorQueue = errorQueue; this.onMessage = onMessage; this.onError = onError; this.criticalError = criticalError; }
public async Task <int> Peek(TableBasedQueue inputQueue, RepeatedFailuresOverTimeCircuitBreaker circuitBreaker, CancellationToken cancellationToken) { var messageCount = 0; try { #if NETFRAMEWORK using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }, TransactionScopeAsyncFlowOption.Enabled)) using (var connection = await connectionFactory.OpenNewConnection().ConfigureAwait(false)) { messageCount = await inputQueue.TryPeek(connection, null, cancellationToken).ConfigureAwait(false); scope.Complete(); } #else using (var scope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) using (var connection = await connectionFactory.OpenNewConnection().ConfigureAwait(false)) using (var tx = connection.BeginTransaction()) { messageCount = await inputQueue.TryPeek(connection, tx, cancellationToken).ConfigureAwait(false); tx.Commit(); scope.Complete(); } #endif circuitBreaker.Success(); } catch (OperationCanceledException) { //Graceful shutdown } catch (SqlException e) when(cancellationToken.IsCancellationRequested) { Logger.Debug("Exception thrown while performing cancellation", e); } catch (Exception ex) { Logger.Warn("Sql peek operation failed", ex); await circuitBreaker.Failure(ex).ConfigureAwait(false); } if (messageCount == 0) { if (Logger.IsDebugEnabled) { Logger.Debug($"Input queue empty. Next peek operation will be delayed for {settings.Delay}."); } // This doesn't require a try/catch (OperationCanceledException) because the upper layers handle the shutdown case gracefully await Task.Delay(settings.Delay, cancellationToken).ConfigureAwait(false); } return(messageCount); }
public void Init(TableBasedQueue inputQueue, TableBasedQueue errorQueue, OnMessage onMessage, OnError onError, Action <string, Exception, CancellationToken> criticalError) { this.inputQueue = inputQueue; this.errorQueue = errorQueue; this.onMessage = onMessage; this.onError = onError; this.criticalError = criticalError; }
public async Task PerformInspection(TableBasedQueue queue, CancellationToken cancellationToken = default) { if (validateExpiredIndex) { await VerifyExpiredIndex(queue, cancellationToken).ConfigureAwait(false); } await VerifyNonClusteredRowVersionIndex(queue, cancellationToken).ConfigureAwait(false); await VerifyHeadersColumnType(queue, cancellationToken).ConfigureAwait(false); }
public async Task <int> Peek(TableBasedQueue inputQueue, RepeatedFailuresOverTimeCircuitBreaker circuitBreaker, CancellationToken cancellationToken = default) { var messageCount = 0; try { #if NETFRAMEWORK using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }, TransactionScopeAsyncFlowOption.Enabled)) using (var connection = await connectionFactory.OpenNewConnection(cancellationToken).ConfigureAwait(false)) { messageCount = await inputQueue.TryPeek(connection, null, cancellationToken : cancellationToken).ConfigureAwait(false); scope.Complete(); } #else using (var scope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) using (var connection = await connectionFactory.OpenNewConnection(cancellationToken).ConfigureAwait(false)) using (var tx = connection.BeginTransaction()) { messageCount = await inputQueue.TryPeek(connection, tx, cancellationToken : cancellationToken).ConfigureAwait(false); tx.Commit(); scope.Complete(); } #endif circuitBreaker.Success(); } catch (Exception ex) when(!ex.IsCausedBy(cancellationToken)) { Logger.Warn("Sql peek operation failed", ex); await circuitBreaker.Failure(ex, cancellationToken).ConfigureAwait(false); } if (messageCount == 0) { if (Logger.IsDebugEnabled) { Logger.Debug($"Input queue empty. Next peek operation will be delayed for {settings.Delay}."); } await Task.Delay(settings.Delay, cancellationToken).ConfigureAwait(false); } return(messageCount); }
async Task VerifyHeadersColumnType(TableBasedQueue queue, CancellationToken cancellationToken) { try { using (var connection = await openConnection(queue, cancellationToken).ConfigureAwait(false)) { var columnType = await queue.CheckHeadersColumnType(connection, cancellationToken).ConfigureAwait(false); if (string.Equals(columnType, "varchar", StringComparison.OrdinalIgnoreCase)) { Logger.Warn($"Table {queue.Name} stores headers in a non Unicode-compatible column (varchar).{Environment.NewLine}This may lead to data loss when sending non-ASCII characters in headers. SQL Server transport 3.1 and newer can take advantage of the nvarchar column type for headers. Please change the column type in the database."); } } } catch (Exception ex) when(!ex.IsCausedBy(cancellationToken)) { Logger.WarnFormat("Checking indexes on table {0} failed. Exception: {1}", queue, ex); } }
async Task VerifyIndex(TableBasedQueue queue, Func <TableBasedQueue, SqlConnection, CancellationToken, Task <bool> > check, string noIndexMessage, CancellationToken cancellationToken) { try { using (var connection = await openConnection(queue, cancellationToken).ConfigureAwait(false)) { var indexExists = await check(queue, connection, cancellationToken).ConfigureAwait(false); if (!indexExists) { Logger.Warn(noIndexMessage); } } } catch (Exception ex) when(!ex.IsCausedBy(cancellationToken)) { Logger.WarnFormat("Checking indexes on table {0} failed. Exception: {1}", queue, ex); } }
public Task Purge(TableBasedQueue queue, CancellationToken cancellationToken) { return(Task.CompletedTask); }