Skip to content

Commit

Permalink
Updates from docs review (#6975)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpalac authored Feb 4, 2025
1 parent 3008733 commit f8cde2b
Show file tree
Hide file tree
Showing 5 changed files with 18 additions and 18 deletions.
15 changes: 7 additions & 8 deletions persistence/nhibernate/outbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Outbox with NHibernate Persistence
summary: How to use the outbox with NHibernate
versions: '[6.0,)'
component: NHibernate
reviewed: 2022-10-19
reviewed: 2025-02-03
related:
- nservicebus/outbox
redirects:
Expand All @@ -12,7 +12,6 @@ redirects:

The [outbox](/nservicebus/outbox) feature requires persistent storage in order to store the messages and enable deduplication.


## Table

To keep track of duplicate messages, the NHibernate implementation of the outbox requires the creation of an `OutboxRecord` table.
Expand All @@ -27,9 +26,9 @@ partial: transactionisolation

By default the outbox records are persisted in the following way:

* The table has an auto-incremented integer primary key.
* The `MessageId` column has a unique index.
* There are indices on `Dispatched` and `DispatchedAt` columns.
- The table has an auto-incremented integer primary key.
- The `MessageId` column has a unique index.
- There are indices on `Dispatched` and `DispatchedAt` columns.

The following API can be used to provide a different mapping of outbox data to the underlying storage:

Expand All @@ -39,8 +38,8 @@ snippet: OutboxNHibernateCustomMapping

If custom mapping is required, the following characteristics of the original mapping must be preserved:

* Values stored in the `MessageId` column must be unique and an attempt to insert a duplicate entry must cause an exception.
* Querying by `Dispatched` and `DispatchedAt` columns must be efficient because these columns are used by the cleanup process to remove outdated records.
- Values stored in the `MessageId` column must be unique and an attempt to insert a duplicate entry must cause an exception.
- Querying by `Dispatched` and `DispatchedAt` columns must be efficient because these columns are used by the cleanup process to remove outdated records.

## Deduplication record lifespan

Expand All @@ -50,7 +49,7 @@ Specify different values in the config file using [timestamp strings](https://do

snippet: OutboxNHibernateTimeToKeep

By specifying a value of `-00:00:00.001` (i.e. 1 millisecond, the value of `Timeout.InfiniteTimeSpan`) for the `NServiceBus/Outbox/NHibernate/FrequencyToRunDeduplicationDataCleanup` app settings, the cleanup task is disabled. This is useful when an endpoint is scaled out and instances are competing to run the cleanup task.
By specifying a value of `-00:00:00.001` (i.e. -1 millisecond, the value of `Timeout.InfiniteTimeSpan`) for the `NServiceBus/Outbox/NHibernate/FrequencyToRunDeduplicationDataCleanup` app settings, the cleanup task is disabled. This is useful when an endpoint is scaled out and instances are competing to run the cleanup task.

> [!NOTE]
> It is advised to run the cleanup task on only one NServiceBus endpoint instance per database. Disable the cleanup task on all other NServiceBus endpoint instances for the most efficient cleanup execution.
13 changes: 7 additions & 6 deletions persistence/nhibernate/outbox_modes_nhibernate_[8.4,).partial.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

By default the outbox uses optimistic concurrency control. That means that when two copies of the same message arrive at the endpoint, both messages are picked up (if concurrency settings of the endpoint allow for it) and processing begins on both of them. When the message handlers are completed, both processing threads attempt to insert the outbox record as part of the transaction that includes the application state change.

At this point of of the transactions succeeds and the other fails due to unique index constraint violation. When the copy of the message that failed is picked up again, it is discarded as a duplicate.
At this point one of the transactions succeeds and the other fails due to unique index constraint violation. When the copy of the message that failed is picked up again, it is discarded as a duplicate.

The outcome is that the application state change is applied only once (the other attempt has been rolled back) but the message handlers have been executed twice. If the message handler contains logic that has non-transactional side effects (e.g. sending an e-mail), that logic may be executed multiple times.

Expand All @@ -12,7 +12,7 @@ The pessimistic concurrency control mode can be activated using the following AP

snippet: OutboxPessimisticMode

In the pessimistic mode the outbox record is inserted before the handlers are executed. As a result, when using a database that creates locks on insert, only one thread is allowed to execute the message handlers. The other thread, even though it picked up the second copy of a message, is blocked on a database lock. Once the first thread commits the transaction, the second thread is interrupted with an exception as it is not allowed to insert the outbox. As a result, the message handlers are executed only once.
In the pessimistic mode the outbox record is inserted before the handlers are executed. As a result, when using a database that creates locks on insert, only one thread is allowed to execute the message handlers. The other thread, even though it picked up the second copy of a message, is blocked on a database lock. Once the first thread commits the transaction, the second thread is interrupted with an exception as it is not allowed to insert the outbox record. As a result, the message handlers are executed only once.

The trade-off is that each message processing attempt requires additional round trip to the database.

Expand All @@ -33,9 +33,10 @@ In cases where the outbox transaction spans multiple databases, the `Transaction
snippet: OutboxTransactionScopeMode

In this mode the NHibernate persistence creates a `TransactionScope` that wraps the whole message processing attempt and within that scope it opens a session, that is used for:
- storing the outbox record
- persisting the application state change applied via `SynchronizedStorageSession`

In addition to that session managed by NServiceBus, users can open their own NHibernate sessions or plain database connections in the message handlers. If the underlying database technology supports distributed transactions managed by Microsoft Distributed Transaction Coordinator -- MS DTC (e.g. SQL Server, Oracle or PostgreSQL), the transaction gets escalated to a distributed transaction.
- storing the outbox record
- persisting the application state change applied via `SynchronizedStorageSession`

The `TransactionScope` mode is most useful in legacy scenarios e.g. when migrating from MSMQ transport to a messaging infrastructure that does not support MS DTC. In order to maintain consistency the outbox has to be used in place of distributed transport-database transactions. If the legacy database cannot be modified to add the outbox table, the only option is to place the outbox table in a separate database and use distributed transactions between the databases.
In addition to the session managed by NServiceBus, users can open their own NHibernate sessions or plain database connections in the message handlers. If the underlying database technology supports distributed transactions managed by Microsoft Distributed Transaction Coordinator -- MS DTC (e.g. SQL Server, Oracle or PostgreSQL), the transaction gets escalated to a distributed transaction.

The `TransactionScope` mode is most useful in legacy scenarios e.g. when migrating from MSMQ transport to a messaging infrastructure that does not support MS DTC. In order to maintain consistency the outbox has to be used in place of distributed transport-database transactions. If the legacy database cannot be modified to add the outbox table, the only option is to place the outbox table in a separate database and use distributed transactions between the databases.
4 changes: 2 additions & 2 deletions samples/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Samples
summary: Samples using NServiceBus and the Particular Service Platform
reviewed: 2022-10-03
reviewed: 2025-02-03
redirects:
- samples/netcore
---
Expand Down Expand Up @@ -72,7 +72,7 @@ In many samples, messages are defined in a shared project along with reusable he

### [Message destinations](/nservicebus/messaging/routing.md)

Many samples make use of `SendLocal` and send to an endpoint directly by specify the destination using a string in code. This is done to simplify the amount of configuration in samples. In a production solution, most message destinations should be defined via [routing configuration](/nservicebus/messaging/routing.md).
Many samples make use of `SendLocal` or send to an endpoint directly by specifing the destination using a string in code. This is done to simplify the amount of configuration in samples. In a production solution, most message destinations should be defined via [routing configuration](/nservicebus/messaging/routing.md).

### [Dependency injection](/nservicebus/dependency-injection/)

Expand Down
2 changes: 1 addition & 1 deletion samples/pipeline/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Pipeline Samples
reviewed: 2022-10-12
reviewed: 2025-02-03
related:
- samples/throttling
- nservicebus/pipeline
Expand Down
2 changes: 1 addition & 1 deletion samples/serializers/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Serializer Samples
reviewed: 2022-10-12
reviewed: 2025-02-03
related:
- nservicebus/serialization
---

0 comments on commit f8cde2b

Please sign in to comment.