public async Task Diagnostic_headers_are_ignored() { using (var connection = sqlConnectionFactory.OpenNewConnection().GetAwaiter().GetResult()) { using (var transaction = connection.BeginTransaction()) { var transportTransaction = new TransportTransaction(); transportTransaction.Set(connection); transportTransaction.Set(transaction); var context = new ContextBag(); context.Set(transportTransaction); var headers = new Dictionary <string, string> { [Headers.TimeToBeReceived] = TimeSpan.FromMinutes(-1).ToString() }; var operation = new TransportOperation( new OutgoingMessage("1", headers, new byte[0]), new UnicastAddressTag(validAddress) ); await dispatcher.Dispatch(new TransportOperations(operation), transportTransaction, context).ConfigureAwait(false); transaction.Commit(); } var expired = await queue.PurgeBatchOfExpiredMessages(connection, 100).ConfigureAwait(false); Assert.AreEqual(0, expired); } }
public async Task Defaults_to_no_ttbr() { using (var connection = sqlConnectionFactory.OpenNewConnection().GetAwaiter().GetResult()) { using (var transaction = connection.BeginTransaction()) { var transportTransaction = new TransportTransaction(); transportTransaction.Set(connection); transportTransaction.Set(transaction); var context = new ContextBag(); context.Set(transportTransaction); var operation = new TransportOperation( new OutgoingMessage("1", new Dictionary <string, string>(), new byte[0]), new UnicastAddressTag(validAddress) ); await dispatcher.Dispatch(new TransportOperations(operation), transportTransaction, context).ConfigureAwait(false); transaction.Commit(); } var expired = await queue.PurgeBatchOfExpiredMessages(connection, 100).ConfigureAwait(false); Assert.AreEqual(0, expired); } }
static TransportTransaction CreateTransportTransaction(string messagePartitionKey, CommittableTransaction transaction, ServiceBusClient serviceBusClient) { var transportTransaction = new TransportTransaction(); transportTransaction.Set(serviceBusClient); transportTransaction.Set("IncomingQueue.PartitionKey", messagePartitionKey); transportTransaction.Set(transaction); return(transportTransaction); }
/// <summary> /// Enables providing SqlConnection and SqlTransaction instances that will be used by send operations. The same connection and transaction /// can be used in more than one send operation. /// </summary> /// <param name="options">The <see cref="SendOptions" /> to extend.</param> /// <param name="connection">Open SqlConnection instance to be used by send operations.</param> /// <param name="transaction">SqlTransaction instance that will be used by any operations perfromed by the transport.</param> public static void UseCustomSqlConnectionAndTransaction(this SendOptions options, SqlConnection connection, SqlTransaction transaction) { var transportTransaction = new TransportTransaction(); transportTransaction.Set(connection); transportTransaction.Set(transaction); options.GetExtensions().Set(transportTransaction); }
TransportTransaction PrepareTransportTransaction(SqlConnection connection) { var transportTransaction = new TransportTransaction(); //those resources are meant to be used by anyone except message dispatcher e.g. persister transportTransaction.Set(connection); transportTransaction.Set(Transaction.Current); return(transportTransaction); }
public Task DeduplicateSQLMessages(string queue, MessageContext message, Dispatch dispatch, Func <Dispatch, Task> forward) { #region Deduplicate async Task DeduplicateDispatch(TransportOperations messages, TransportTransaction transaction, ContextBag context) { using (var conn = await OpenConnection().ConfigureAwait(false)) using (var tx = conn.BeginTransaction()) { var duplicateOps = new List <UnicastTransportOperation>(); //Detect duplicates foreach (var operation in messages.UnicastTransportOperations) { var messageId = operation.Message.MessageId; if (await WasForwarded(conn, tx, messageId).ConfigureAwait(false)) { duplicateOps.Add(operation); } } //Remove duplicates foreach (var duplicateOp in duplicateOps) { messages.UnicastTransportOperations.Remove(duplicateOp); } //Set the connection and transaction for the outgoing op var forwardTransaction = new TransportTransaction(); forwardTransaction.Set(conn); forwardTransaction.Set(tx); //Dispatch await dispatch(messages, forwardTransaction, context) .ConfigureAwait(false); //Mark as processed (atomically with dispatch) foreach (var operation in messages.UnicastTransportOperations) { var messageId = operation.Message.MessageId; await MarkAsForwarded(conn, tx, messageId).ConfigureAwait(false); } tx.Commit(); } } #endregion return(forward(message.TransportTransaction.TryGet <SqlConnection>(out var _) ? dispatch : DeduplicateDispatch)); }
/// <summary> /// Enables the use of custom SqlTransaction instances for publish operations. The same transaction can be used in more than one publish operation. /// </summary> /// <param name="options">The <see cref="PublishOptions" /> to extend.</param> /// <param name="transaction">SqlTransaction instance that will be used by any operations performed by the transport.</param> public static void UseCustomSqlTransaction(this PublishOptions options, SqlTransaction transaction) { // When dispatching, the TransportTransaction is overwritten. // The only way for a custom transaction to work is by using immediate dispatch and messages should only appear when the user commits the custom transaction. // Which is exactly what will happen after NServiceBus dispatches this message immediately. options.RequireImmediateDispatch(); var transportTransaction = new TransportTransaction(); transportTransaction.Set(SettingsKeys.IsUserProvidedTransactionKey, true); transportTransaction.Set(SettingsKeys.TransportTransactionSqlTransactionKey, transaction); options.GetExtensions().Set(transportTransaction); }
TransportTransaction CreateTransportTransaction(string incomingQueuePartitionKey, CommittableTransaction transaction) { var transportTransaction = new TransportTransaction(); if (transportSettings.TransportTransactionMode == TransportTransactionMode.SendsAtomicWithReceive) { transportTransaction.Set(serviceBusClient); transportTransaction.Set("IncomingQueue.PartitionKey", incomingQueuePartitionKey); transportTransaction.Set(transaction); } return(transportTransaction); }
static async Task Forward(RawContext context, Func <RawContext, Task> next, SqlConnection conn, SqlTransaction trans) { var receivedTransportTransaction = context.Extensions.Get <TransportTransaction>(); var sqlTransportTransaction = new TransportTransaction(); sqlTransportTransaction.Set(conn); sqlTransportTransaction.Set(trans); context.Extensions.Set(sqlTransportTransaction); await next(context); context.Extensions.Set(receivedTransportTransaction); }
public override async Task Invoke(IOutgoingPhysicalMessageContext context, Func <Task> next) { if (!DedupePipelineState.TryGet(context, out var dedupePipelineState)) { await next(); return; } var connectionTask = connectionBuilder(CancellationToken.None); if (context.Extensions.TryGet(out TransportTransaction _)) { throw new NotSupportedException("Deduplication is currently designed to be used from outside the NServiceBus pipeline. For example to dedup messages being sent from inside a web service endpoint."); } var messageId = GetMessageId(context); var transportTransaction = new TransportTransaction(); context.Extensions.Set(transportTransaction); await using var connection = await connectionTask; await using var transaction = (SqlTransaction) await connection.BeginTransactionAsync(); transportTransaction.Set(connection); transportTransaction.Set(transaction); var dedupeManager = new DedupeManager(transaction, table); var writeResult = await dedupeManager.WriteDedupRecord(messageId, dedupePipelineState.Context); dedupePipelineState.DedupeOutcome = writeResult.DedupeOutcome; dedupePipelineState.Context = writeResult.Context; if (dedupePipelineState.DedupeOutcome == DedupeOutcome.Deduplicated) { logger.Info($"Message deduplicated. MessageId: {messageId}"); return; } await next(); var commitResult = await dedupeManager.CommitWithDedupCheck(messageId, dedupePipelineState.Context); dedupePipelineState.DedupeOutcome = commitResult.DedupeOutcome; dedupePipelineState.Context = commitResult.Context; if (commitResult.DedupeOutcome == DedupeOutcome.Deduplicated) { logger.Info($"Message deduplicated. MessageId: {messageId}"); } }
public async Task Adapts_transport_connection() { var transportTransaction = new TransportTransaction(); var transportConnection = dbConnection(); var transaction = transportConnection.BeginTransaction(); transportTransaction.Set("System.Data.SqlClient.SqlConnection", transportConnection); transportTransaction.Set("System.Data.SqlClient.SqlTransaction", transaction); var result = await sqlDialect.Convert().TryAdaptTransportConnection(transportTransaction, new ContextBag(), () => throw new Exception("Should not be called"), (conn, tx, arg3) => new StorageSession(conn, tx, false, null)); Assert.IsNotNull(result); }
private async Task ProcessFileWithTransaction(string filePath, string messageId) { using (var transaction = new DirectoryBasedTransaction(_messageDirectory)) { transaction.BeginTransaction(filePath); var messageFile = File.ReadAllLines(transaction.FileToProcess); var bodyPath = messageFile.First(); var messageHeaderJson = string.Join("", messageFile.Skip(1)); var messageHeaders = HeaderSerializer.DeSerialize(messageHeaderJson); if (RemoveFileIfExpired(messageHeaders, transaction)) { return; } var fileContents = File.ReadAllBytes(bodyPath); var transportTransaction = new TransportTransaction(); transportTransaction.Set(transaction); var shouldCommit = await HandleMessageWithRetries(messageId, messageHeaders, fileContents, transportTransaction, 1); if (shouldCommit) { transaction.Commit(); } } }
public async Task It_invokes_callbacks_when_session_is_completed() { using (var scope = new TransactionScope()) { var transportTransaction = new TransportTransaction(); transportTransaction.Set(Transaction.Current); var callbackInvoked = false; var adapter = new NHibernateSynchronizedStorageAdapter(SessionFactory); using (var storageSession = await adapter.TryAdapt(transportTransaction, new ContextBag())) { storageSession.Session(); //Make sure session is initialized storageSession.OnSaveChanges(s => { callbackInvoked = true; return(Task.FromResult(0)); }); await storageSession.CompleteAsync(); Assert.IsTrue(callbackInvoked); } scope.Complete(); } }
async Task<bool> ProcessMessage(MessageQueueTransaction msmqTransaction, Message message, Dictionary<string, string> headers) { var transportTransaction = new TransportTransaction(); transportTransaction.Set(msmqTransaction); MsmqFailureInfoStorage.ProcessingFailureInfo failureInfo; var shouldTryProcessMessage = true; if (failureInfoStorage.TryGetFailureInfoForMessage(message.Id, out failureInfo)) { var errorHandleResult = await HandleError(message, headers, failureInfo.Exception, transportTransaction, failureInfo.NumberOfProcessingAttempts).ConfigureAwait(false); shouldTryProcessMessage = errorHandleResult != ErrorHandleResult.Handled; } if (shouldTryProcessMessage) { try { using (var bodyStream = message.BodyStream) { var shouldAbortMessageProcessing = await TryProcessMessage(message.Id, headers, bodyStream, transportTransaction).ConfigureAwait(false); if (shouldAbortMessageProcessing) { return false; } } } catch (Exception exception) { failureInfoStorage.RecordFailureInfoForMessage(message.Id, exception); return false; } } failureInfoStorage.ClearFailureInfoForMessage(message.Id); return true; }
public async Task Save_fails_when_data_changes_between_concurrent_instances() { var saga = new TestSagaData { Id = Guid.NewGuid() }; var persister = new InMemorySagaPersister(); var storageAdapter = new InMemoryTransactionalSynchronizedStorageAdapter(); var insertSession = new InMemorySynchronizedStorageSession(); await persister.Save(saga, SagaMetadataHelper.GetMetadata<TestSaga>(saga), insertSession, new ContextBag()); await insertSession.CompleteAsync(); Assert.That(async () => { using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { Transaction.Current.EnlistDurable(EnlistmentWhichEnforcesDtcEscalation.Id, new EnlistmentWhichEnforcesDtcEscalation(), EnlistmentOptions.None); var transportTransaction = new TransportTransaction(); transportTransaction.Set(Transaction.Current); var unenlistedSession = new InMemorySynchronizedStorageSession(); var enlistedSession = await storageAdapter.TryAdapt(transportTransaction, new ContextBag()); var unenlistedSessionContext = new ContextBag(); var unenlistedRecord = await persister.Get<TestSagaData>(saga.Id, unenlistedSession, unenlistedSessionContext); var enlistedSessionContext = new ContextBag(); var enlistedRecord = await persister.Get<TestSagaData>("Id", saga.Id, enlistedSession, enlistedSessionContext); await persister.Update(unenlistedRecord, unenlistedSession, unenlistedSessionContext); await persister.Update(enlistedRecord, enlistedSession, enlistedSessionContext); await unenlistedSession.CompleteAsync(); tx.Complete(); } }, Throws.Exception.TypeOf<TransactionAbortedException>()); }