static async Task CreateQueue(string creationScript, CanonicalQueueAddress canonicalQueueAddress, SqlConnection connection, SqlTransaction transaction, bool createMessageBodyColumn) { try { var sql = string.Format(creationScript, canonicalQueueAddress.QualifiedTableName, canonicalQueueAddress.QuotedCatalogName); using (var command = new SqlCommand(sql, connection, transaction) { CommandType = CommandType.Text }) { await command.ExecuteNonQueryAsync().ConfigureAwait(false); } } catch (Exception e) when(e.Message.StartsWith("There is already an object named") || e.Message.StartsWith("The operation failed because an index or statistics with name")) { // ignored because of race when multiple endpoints start } if (createMessageBodyColumn) { var bodyStringSql = string.Format(SqlConstants.AddMessageBodyStringColumn, canonicalQueueAddress.QualifiedTableName, canonicalQueueAddress.QuotedCatalogName); using (var command = new SqlCommand(bodyStringSql, connection, transaction) { CommandType = CommandType.Text }) { await command.ExecuteNonQueryAsync().ConfigureAwait(false); } } }
public QueueCreator(SqlConnectionFactory connectionFactory, QueueAddressTranslator addressTranslator, CanonicalQueueAddress delayedQueueAddress, bool createMessageBodyColumn = false) { this.connectionFactory = connectionFactory; this.addressTranslator = addressTranslator; this.delayedQueueAddress = delayedQueueAddress; this.createMessageBodyColumn = createMessageBodyColumn; }
static async Task CreateQueue(string creationScript, CanonicalQueueAddress canonicalQueueAddress, SqlConnection connection, bool createMessageBodyColumn, CancellationToken cancellationToken) { try { using (var transaction = connection.BeginTransaction()) { var sql = string.Format(creationScript, canonicalQueueAddress.QualifiedTableName, canonicalQueueAddress.QuotedCatalogName); using (var command = new SqlCommand(sql, connection, transaction) { CommandType = CommandType.Text }) { await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); } transaction.Commit(); } } catch (SqlException e) when(e.Number is 2714 or 1913) //Object already exists { //Table creation scripts are based on sys.objects metadata views. //It looks that these views are not fully transactional and might //not return information on already created table under heavy load. //This in turn can result in executing table create or index create queries //for objects that already exists. These queries will fail with // 2714 (table) and 1913 (index) error codes. } if (createMessageBodyColumn) { var bodyStringSql = string.Format(SqlConstants.AddMessageBodyStringColumn, canonicalQueueAddress.QualifiedTableName, canonicalQueueAddress.QuotedCatalogName); using (var transaction = connection.BeginTransaction()) { using (var command = new SqlCommand(bodyStringSql, connection, transaction) { CommandType = CommandType.Text }) { await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); } transaction.Commit(); } } } SqlConnectionFactory connectionFactory; QueueAddressTranslator addressTranslator; bool createMessageBodyColumn; }
public async Task CreateQueueIfNecessary(string[] queueAddresses, CanonicalQueueAddress delayedQueueAddress, CancellationToken cancellationToken = default) { using (var connection = await connectionFactory.OpenNewConnection(cancellationToken).ConfigureAwait(false)) { foreach (var address in queueAddresses) { await CreateQueue(SqlConstants.CreateQueueText, addressTranslator.Parse(address), connection, createMessageBodyColumn, cancellationToken).ConfigureAwait(false); } if (delayedQueueAddress != null) { await CreateQueue(SqlConstants.CreateDelayedMessageStoreText, delayedQueueAddress, connection, createMessageBodyColumn, cancellationToken).ConfigureAwait(false); } } }
public async Task ConfigureReceiveInfrastructure(ReceiveSettings[] receiveSettings, string[] sendingAddresses, CancellationToken cancellationToken = default) { var transactionOptions = transport.TransactionScope.TransactionOptions; diagnostics.Add("NServiceBus.Transport.SqlServer.Transactions", new { TransactionMode = transport.TransportTransactionMode, transactionOptions.IsolationLevel, transactionOptions.Timeout }); diagnostics.Add("NServiceBus.Transport.SqlServer.CircuitBreaker", new { TimeToWaitBeforeTriggering = transport.TimeToWaitBeforeTriggeringCircuitBreaker }); var queuePeekerOptions = transport.QueuePeeker; var createMessageBodyComputedColumn = transport.CreateMessageBodyComputedColumn; Func <TransportTransactionMode, ProcessStrategy> processStrategyFactory = guarantee => SelectProcessStrategy(guarantee, transactionOptions, connectionFactory); var queuePurger = new QueuePurger(connectionFactory); var queuePeeker = new QueuePeeker(connectionFactory, queuePeekerOptions); IExpiredMessagesPurger expiredMessagesPurger; bool validateExpiredIndex; if (transport.ExpiredMessagesPurger.PurgeOnStartup == false) { diagnostics.Add("NServiceBus.Transport.SqlServer.ExpiredMessagesPurger", new { Enabled = false, }); expiredMessagesPurger = new NoOpExpiredMessagesPurger(); validateExpiredIndex = false; } else { var purgeBatchSize = transport.ExpiredMessagesPurger.PurgeBatchSize; diagnostics.Add("NServiceBus.Transport.SqlServer.ExpiredMessagesPurger", new { Enabled = true, BatchSize = purgeBatchSize }); expiredMessagesPurger = new ExpiredMessagesPurger((_, token) => connectionFactory.OpenNewConnection(token), purgeBatchSize); validateExpiredIndex = true; } var schemaVerification = new SchemaInspector((queue, token) => connectionFactory.OpenNewConnection(token), validateExpiredIndex); var queueFactory = transport.Testing.QueueFactoryOverride ?? (queueName => new TableBasedQueue(addressTranslator.Parse(queueName).QualifiedTableName, queueName, !isEncrypted)); //Create delayed delivery infrastructure CanonicalQueueAddress delayedQueueCanonicalAddress = null; if (transport.DisableDelayedDelivery == false) { var delayedDelivery = transport.DelayedDelivery; diagnostics.Add("NServiceBus.Transport.SqlServer.DelayedDelivery", new { Native = true, Suffix = delayedDelivery.TableSuffix, Interval = delayedDelivery.ProcessingInterval, delayedDelivery.BatchSize, }); var queueAddress = new Transport.QueueAddress(hostSettings.Name, null, new Dictionary <string, string>(), delayedDelivery.TableSuffix); delayedQueueCanonicalAddress = addressTranslator.GetCanonicalForm(addressTranslator.Generate(queueAddress)); //For backwards-compatibility with previous version of the seam and endpoints that have delayed //delivery infrastructure, we assume that the first receiver address matches main input queue address //from version 7 of Core. For raw usages this will still work but delayed-delivery messages //might be moved to arbitrary picked receiver var mainReceiverInputQueueAddress = receiveSettings[0].ReceiveAddress; var inputQueueTable = addressTranslator.Parse(mainReceiverInputQueueAddress).QualifiedTableName; var delayedMessageTable = new DelayedMessageTable(delayedQueueCanonicalAddress.QualifiedTableName, inputQueueTable); //Allows dispatcher to store messages in the delayed store delayedMessageStore = delayedMessageTable; dueDelayedMessageProcessor = new DueDelayedMessageProcessor(delayedMessageTable, connectionFactory, delayedDelivery.ProcessingInterval, delayedDelivery.BatchSize, transport.TimeToWaitBeforeTriggeringCircuitBreaker, hostSettings); } Receivers = receiveSettings.Select(s => { ISubscriptionManager subscriptionManager = transport.SupportsPublishSubscribe ? (ISubscriptionManager) new SubscriptionManager(subscriptionStore, hostSettings.Name, s.ReceiveAddress) : new NoOpSubscriptionManager(); return(new MessageReceiver(transport, s, hostSettings, processStrategyFactory, queueFactory, queuePurger, expiredMessagesPurger, queuePeeker, queuePeekerOptions, schemaVerification, transport.TimeToWaitBeforeTriggeringCircuitBreaker, subscriptionManager)); }).ToDictionary <MessageReceiver, string, IMessageReceiver>(receiver => receiver.Id, receiver => receiver); await ValidateDatabaseAccess(transactionOptions, cancellationToken).ConfigureAwait(false); var receiveAddresses = receiveSettings.Select(r => r.ReceiveAddress).ToList(); if (hostSettings.SetupInfrastructure) { var queuesToCreate = new List <string>(); queuesToCreate.AddRange(sendingAddresses); queuesToCreate.AddRange(receiveAddresses); var queueCreator = new QueueCreator(connectionFactory, addressTranslator, createMessageBodyComputedColumn); await queueCreator.CreateQueueIfNecessary(queuesToCreate.ToArray(), delayedQueueCanonicalAddress, cancellationToken) .ConfigureAwait(false); } dueDelayedMessageProcessor?.Start(cancellationToken); transport.Testing.SendingAddresses = sendingAddresses.Select(s => addressTranslator.Parse(s).QualifiedTableName).ToArray(); transport.Testing.ReceiveAddresses = receiveAddresses.Select(r => addressTranslator.Parse(r).QualifiedTableName).ToArray(); transport.Testing.DelayedDeliveryQueue = delayedQueueCanonicalAddress?.QualifiedTableName; }
public override TransportReceiveInfrastructure ConfigureReceiveInfrastructure() { if (!settings.TryGet(out SqlScopeOptions scopeOptions)) { scopeOptions = new SqlScopeOptions(); } settings.TryGet(out TransportTransactionMode transactionMode); diagnostics.Add("NServiceBus.Transport.SqlServer.Transactions", new { TransactionMode = transactionMode, scopeOptions.TransactionOptions.IsolationLevel, scopeOptions.TransactionOptions.Timeout }); if (!settings.TryGet(SettingsKeys.TimeToWaitBeforeTriggering, out TimeSpan waitTimeCircuitBreaker)) { waitTimeCircuitBreaker = TimeSpan.FromSeconds(30); } diagnostics.Add("NServiceBus.Transport.SqlServer.CircuitBreaker", new { TimeToWaitBeforeTriggering = waitTimeCircuitBreaker }); if (!settings.TryGet(out QueuePeekerOptions queuePeekerOptions)) { queuePeekerOptions = new QueuePeekerOptions(); } var createMessageBodyComputedColumn = settings.GetOrDefault <bool>(SettingsKeys.CreateMessageBodyComputedColumn); Func <TransportTransactionMode, ReceiveStrategy> receiveStrategyFactory = guarantee => SelectReceiveStrategy(guarantee, scopeOptions.TransactionOptions, connectionFactory); var queuePurger = new QueuePurger(connectionFactory); var queuePeeker = new QueuePeeker(connectionFactory, queuePeekerOptions); IExpiredMessagesPurger expiredMessagesPurger; bool validateExpiredIndex; if (settings.GetOrDefault <bool>(SettingsKeys.PurgeEnableKey)) { diagnostics.Add("NServiceBus.Transport.SqlServer.ExpiredMessagesPurger", new { Enabled = false, }); expiredMessagesPurger = new NoOpExpiredMessagesPurger(); validateExpiredIndex = false; } else { var purgeBatchSize = settings.HasSetting(SettingsKeys.PurgeBatchSizeKey) ? settings.Get <int?>(SettingsKeys.PurgeBatchSizeKey) : null; diagnostics.Add("NServiceBus.Transport.SqlServer.ExpiredMessagesPurger", new { Enabled = true, BatchSize = purgeBatchSize }); expiredMessagesPurger = new ExpiredMessagesPurger(_ => connectionFactory.OpenNewConnection(), purgeBatchSize); validateExpiredIndex = true; } var schemaVerification = new SchemaInspector(queue => connectionFactory.OpenNewConnection(), validateExpiredIndex); Func <string, TableBasedQueue> queueFactory = queueName => new TableBasedQueue(addressTranslator.Parse(queueName).QualifiedTableName, queueName, !isEncrypted); //Create delayed delivery infrastructure CanonicalQueueAddress delayedQueueCanonicalAddress = null; if (settings.GetOrDefault <bool>(SettingsKeys.DisableDelayedDelivery) == false) { if (!settings.TryGet(SettingsKeys.DelayedDeliverySuffix, out string suffix)) { suffix = "Delayed"; } if (!settings.TryGet(SettingsKeys.DelayedDeliveryInterval, out TimeSpan interval)) { interval = TimeSpan.FromSeconds(1); } if (!settings.TryGet(SettingsKeys.DelayedDeliveryMatureBatchSize, out int matureBatchSize)) { matureBatchSize = 100; } settings.AddStartupDiagnosticsSection("NServiceBus.Transport.SqlServer.DelayedDelivery", new { Native = true, Suffix = suffix, Interval = interval, BatchSize = matureBatchSize, }); delayedQueueCanonicalAddress = GetDelayedTableAddress(suffix); var inputQueueTable = addressTranslator.Parse(ToTransportAddress(logicalAddress())).QualifiedTableName; var delayedMessageTable = new DelayedMessageTable(delayedQueueCanonicalAddress.QualifiedTableName, inputQueueTable); //Allows dispatcher to store messages in the delayed store delayedMessageStore = delayedMessageTable; dueDelayedMessageProcessor = new DueDelayedMessageProcessor(delayedMessageTable, connectionFactory, interval, matureBatchSize); } return(new TransportReceiveInfrastructure( () => new MessagePump(receiveStrategyFactory, queueFactory, queuePurger, expiredMessagesPurger, queuePeeker, queuePeekerOptions, schemaVerification, waitTimeCircuitBreaker), () => new QueueCreator(connectionFactory, addressTranslator, delayedQueueCanonicalAddress, createMessageBodyComputedColumn), () => ValidateDatabaseAccess(scopeOptions.TransactionOptions))); }