You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: nservicebus/sagas/concurrency.md
+12-11Lines changed: 12 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ summary: NServiceBus ensures consistency between saga state and messaging.
4
4
component: Core
5
5
redirects:
6
6
- nservicebus/nservicebus-sagas-and-concurrency
7
-
reviewed: 2024-10-08
7
+
reviewed: 2026-06-09
8
8
related:
9
9
- persistence/nhibernate/saga-concurrency
10
10
- persistence/ravendb/saga-concurrency
@@ -27,7 +27,7 @@ Saga state is created when a saga is started by a message. It may be updated whe
27
27
28
28
When simultaneous messages initiating the same saga instance are received, NServiceBus ensures that only one message starts the saga. Currently, saga creation uses optimistic concurrency.
29
29
30
-
Optimistic concurrency only allows one message handler to succeed. The saga state is created and sent messages are dispatched. The other handlers fail, roll back, and their messages enter [recoverability](/nservicebus/recoverability/). When those messages are retried, the existing saga state is found. Handling the messages may involve changing the state or completing the saga. It's important to note that enabling pessimistic concurrency for the persister does not impact the optimistic concurrency of saga creation. See below for how those scenarios are handled.
30
+
Optimistic concurrency only allows one message handler to succeed. The saga state is created and outgoing messages are dispatched. The other handlers fail, roll back, and their messages enter [recoverability](/nservicebus/recoverability/). When those messages are retried, the existing saga state is found. Handling the messages may involve changing the state or completing the saga. It's important to note that enabling pessimistic concurrency for the persister does not impact the optimistic concurrency of saga creation. See below for how those scenarios are handled.
31
31
32
32
## Changes to saga state
33
33
@@ -39,17 +39,17 @@ See the [documentation for each persister](/persistence/) for details of which m
39
39
40
40
## Completing a saga
41
41
42
-
When using some persisters, messages received simultaneously that correlate to the same existing saga instance are not handled simultaneously. The persister use pessimistic locking to ensure that message handlers are invoked one after another.
42
+
When using some persisters, messages received simultaneously that correlate to the same existing saga instance are not handled simultaneously. The persister uses pessimistic locking to ensure that message handlers are invoked one after another.
43
43
44
-
When using other persisters, and simultaneously receiving messages which cause changes to the state of the same existing saga instance, and at least one of those messages causes the saga to be completed, NServiceBus ensures that only one message either changes the state or completes the saga. Using optimistic concurrency, only one message handler is allowed to succeed. If the message changes the saga, the saga stated is updated, and sent messages are dispatched. If the message completes the saga, the saga state is deleted, and sent messages are dispatched. The other handlers fail, roll back, and their messages enter [recoverability](/nservicebus/recoverability/).
44
+
When using other persisters, and simultaneously receiving messages which cause changes to the state of the same existing saga instance, and at least one of those messages causes the saga to be completed, NServiceBus ensures that only one message either changes the state or completes the saga. Using optimistic concurrency, only one message handler is allowed to succeed. If the message changes the saga, the saga state is updated, and sent messages are dispatched. If the message completes the saga, the saga state is deleted, and sent messages are dispatched. The other handlers fail, roll back, and their messages enter [recoverability](/nservicebus/recoverability/).
45
45
46
46
In both cases, if a message completes the saga, when subsequent messages are received, the saga state is not found and those messages are [discarded](/nservicebus/sagas/saga-not-found.md).
47
47
48
48
See the [documentation for each persister](/persistence/) for details of which method is used.
49
49
50
50
## High-load scenarios
51
51
52
-
Under high load, simultaneous attempts to create, update, or delete the state of a saga instance may lead to decreased performance due to the data contention scenarios described above. In effect, the use of pessimistic locking or optimistic currency control may lead to [block contention](https://en.wikipedia.org/wiki/Block_contention).
52
+
Under high load, simultaneous attempts to create, update, or delete the state of a saga instance may lead to decreased performance due to the data contention scenarios described above. In effect, the use of pessimistic locking or optimistic concurrency control may lead to [block contention](https://en.wikipedia.org/wiki/Block_contention).
53
53
54
54
In these scenarios, the symptoms of high data contention differ depending on the persister being used. Potential symptoms include:
55
55
@@ -78,7 +78,7 @@ The following saga persisters support pessimistic locking:
78
78
-[MongoDB](/persistence/mongodb/) (since version 2.2.0)
79
79
-[SQL](/persistence/sql/) (since version 4.1.1)
80
80
-[RavenDB](/persistence/ravendb/) (available since version 6.4.0, default since version 7.0.0)
81
-
-[CosmosDB](/persistence/cosmosdb/)
81
+
-[Azure Cosmos DB](/persistence/cosmosdb/)
82
82
-[DynamoDB](/persistence/dynamodb/)
83
83
84
84
#### Optimistic concurrency control
@@ -90,12 +90,11 @@ Due to recoverability, OCC conflicts in high data contention scenarios may resul
90
90
The following saga persisters support OCC:
91
91
92
92
-[Azure Table](/persistence/azure-table/)
93
-
-[Azure Cosmos DB](/persistence/cosmosdb)
93
+
-[Azure Cosmos DB](/persistence/cosmosdb/)
94
94
-[Non-Durable](/persistence/non-durable/)
95
95
-[MongoDB](/persistence/mongodb/) (prior to 2.2.0)
96
96
-[RavenDB](/persistence/ravendb/) (prior to 7.0.0)
97
97
-[SQL](/persistence/sql/) (prior to 4.1.1)
98
-
-[CosmosDB](/persistence/cosmosdb/)
99
98
-[DynamoDB](/persistence/dynamodb/)
100
99
101
100
### Use custom recoverability for OCC conflicts
@@ -114,7 +113,7 @@ To avoid impacting the processing of messages which are not related to the saga,
114
113
The number of OCC conflicts can be reduced by [decreasing the concurrency limit](/nservicebus/operations/tuning.md). Message handling can even be made sequential by setting the concurrency limit to 1.
115
114
116
115
> [!NOTE]
117
-
> Sequential messaging handling when using OCC is only possible for a single endpoint instance. When an endpoint is [scaled out](/nservicebus/scaling.md#scaling-out-to-multiple-nodes), message handling cannot be made sequential when all instances are running. An alternative is to have only one instance running at a time, in an active/passive configuration.
116
+
> Sequential message handling when using OCC is only possible for a single endpoint instance. When an endpoint is [scaled out](/nservicebus/scaling.md#scaling-out-to-multiple-nodes), message handling cannot be made sequential when all instances are running. An alternative is to have only one instance running at a time, in an active/passive configuration.
118
117
119
118
The concurrency limit applies to an entire endpoint. If the endpoint hosts many handlers and sagas, they will all be subject to the concurrency limit. When decreasing the concurrency limit to reduce data contention for a given saga, consider hosting the saga in a dedicated endpoint.
120
119
@@ -140,6 +139,8 @@ Saga data storage, and endpoints hosting sagas, often have separate network loca
140
139
141
140
### Redesign the sagas
142
141
142
+
When designing sagas, take these approaches into consideration to reduce data contention.
143
+
143
144
#### Minimize saga data
144
145
145
146
Saga state is retrieved from and submitted to storage for every message received. Saga state should only contain the minimum data required for the saga to make its decisions. Other data, especially large strings or [BLOBs](https://en.wikipedia.org/wiki/Binary_large_object) should not be contained in saga data. Instead this data could be forwarded to another handler for storage or processing, and the saga state could store only a reference to it.
@@ -156,15 +157,15 @@ For example, instead of a saga creating 1,000 requests, it could split the reque
156
157
157
158
In the above example, the requests are split into two groups each time. Depending on the dynamics of the system, splitting them into more groups is also an option.
158
159
159
-
A simpler approach is to split the request just once and have a single level of sub-sagas sending the requests and aggregating the responses. This will not reduce data contention to the same degree, since the originating saga will be aggregating more multiple responses.
160
+
A simpler approach is to split the request just once and have a single level of sub-sagas sending the requests and aggregating the responses. This will not reduce data contention to the same degree, since the originating saga will be aggregating multiple responses.
160
161
161
162
#### Sequential scatter/gather
162
163
163
164
When using a [scatter-gather](https://www.enterpriseintegrationpatterns.com/patterns/messaging/BroadcastAggregate.html) pattern or similar, simultaneously sending a large number of requests may result in simultaneous processing of a large number of responses, which may lead to data contention. Instead, consider sending requests sequentially. In each iteration, send the next request only after the response from the previous request was processed. This approach removes data contention when processing responses but may increase the overall duration. Also, the size of the saga state may increase, because it may need to contain some of the data from the message which initiated the scatter-gather so that that data can be included in each request.
164
165
165
166
#### Create an append-only saga data model
166
167
167
-
Currently, this is only possible with the NHibernate persister and a custom mapping. It requires advanced knowledge of NHibernate. Data added to a collection must be mapped to another entity so that the the master/parent row does not need to be updated. This way there is no data contention on the table row representing the saga instance data root.
168
+
Currently, this is only possible with the NHibernate persister and a custom mapping. It requires advanced knowledge of NHibernate. Data added to a collection must be mapped to another entity so that the master/parent row does not need to be updated. This way there is no data contention on the table row representing the saga instance data root.
0 commit comments