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
* Update Saga with note on persister specific restrictions to saga name length
* Update index.md
* Create index_limitationnote_core_[1,).partial.md
* Update and rename index_limitationnote_core_[1,).partial.md to index_limitationnote_core_[1,].partial.md
* Update index.md
* Update index.md
* Update index.md
* Update index_limitationnote_core_[1,].partial.md
* Update index.md
* Delete nservicebus/sagas/index_limitationnote_core_[1,].partial.md
* Update index.md
* Added inline conditional so that the snippet is rendered only for version 8 and above. Version 7 does not show it on the docs
* Update index.md
* Update index_disable-shared-state-check_core_[8,].partial.md
* Remove partial
* Version
* Sigh
* Revert "Remove partial"
This reverts commit 8e81464.
* Fix
---------
Co-authored-by: Daniel Marbach <[email protected]>
Co-authored-by: Daniel Marbach <[email protected]>
Copy file name to clipboardExpand all lines: nservicebus/sagas/index.md
+21-27Lines changed: 21 additions & 27 deletions
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
title: Sagas
3
3
summary: Maintain statefulness in distributed systems with the saga pattern and NServiceBus' event-driven architecture with built-in fault-tolerance and scalability.
4
4
component: Core
5
-
reviewed: 2024-09-22
5
+
reviewed: 2025-06-24
6
6
redirects:
7
7
- nservicebus/sagas-in-nservicebus
8
8
related:
@@ -39,10 +39,11 @@ snippet: simple-saga-data
39
39
40
40
### Avoid sharing types between sagas
41
41
42
-
Saga data types should not be shared across different sagas. Sharing types can result in persisters physically sharing the same storage structure which should be avoided.
42
+
Saga data types should not be shared across different sagas. Sharing types can result in persisters physically sharing the same storage structure, which should be avoided.
43
43
44
-
> [!WARNING]
45
-
> Sharing property types should also be avoided. Depending on the persister implementation, sharing property types can result in storage structure being shared between endpoints.
44
+
Sharing complex property types should also be avoided. Depending on the persister implementation, sharing property types can result in the storage structure being shared between endpoints.
45
+
46
+
NServiceBus will perform a check at startup to ensure that saga data types are not shared across sagas. An exception will be thrown at startup if any shared root types are found. Complex types in properties that are shared between sagas are not included in this check.
46
47
47
48
partial: disable-shared-state-check
48
49
@@ -55,31 +56,23 @@ The important part of a long-running process is its behavior. Just like regular
55
56
56
57
## Starting a saga
57
58
58
-
Since a saga manages the state of a long-running process, under which conditions should a new saga be created? Sagas are, in essence, a messagedriven state machine. The trigger to start this state machine is the arrival of one or more specified message types. In the previous example, a new saga is started every time a message of type `StartOrder` arrives. This is declared by adding `IAmStartedByMessages<StartOrder>` to the saga.
59
+
Since a saga manages the state of a long-running process, under what conditions should a new saga be created? Sagas are, in essence, a message-driven state machine. The trigger to start this state machine is the arrival of one or more specified message types. In the previous example, a new saga is started every time a message of type `StartOrder` arrives. This is declared by adding `IAmStartedByMessages<StartOrder>` to the saga.
59
60
60
61
> [!NOTE]
61
62
> `IHandleMessages<StartOrder>` is redundant since `IAmStartedByMessages<StartOrder>` already implies that.
62
63
63
-
This interface tells NServiceBus that the saga not only handles `StartOrder`, but that when that type of message arrives, a new instance of this saga should be created to handle it, if there isn't already an existing saga that correlates to the message. As a convenience, in NServiceBus version 6 and above, the message will set its mapped correlation property on the created saga data. In essence the semantics of `IAmStartedByMessages` is:
64
-
65
-
> Create a new instance if an existing one can't be found
66
-
67
-
68
-
> [!NOTE]
69
-
> NServiceBus requires each saga to have at least one message that is able to start it.
70
-
64
+
This interface tells NServiceBus that the saga not only handles `StartOrder`, but also that when a message of that type arrives, a new instance of this saga should be created to handle it, if there isn't already an existing saga that correlates to the message. As a convenience, in NServiceBus version 6 and above, the message will set its mapped correlation property on the created saga data. In essence, the semantics of `IAmStartedByMessages` is:
71
65
72
-
### Dealing with out of order delivery
66
+
> Create a new instance of the saga if an existing instance cannot be found
73
67
74
-
> [!NOTE]
75
-
> Always assume that messages can be delivered out of order, e.g. due to error recovery, network latency, or concurrent message processing.
68
+
### Dealing with out-of-order delivery
76
69
77
-
Sagas not designed to handle the arrival of messages out of order can result in some messages being discarded. In the previous example, this could happen if a `CompleteOrder` message is received before the `StartOrder` message has had a chance to create the saga.
70
+
Messages can be delivered out of order, e.g. due to error recovery, network latency, or concurrent message processing, and sagas must be designed to handle the arrival of out-of-order messages. Sagas not designed to handle the arrival of messages out of order can result in some messages being discarded. In the previous example, this could happen if a `CompleteOrder` message is received before the `StartOrder` message has had a chance to create the saga.
78
71
79
72
To ensure messages are not discarded when they arrive out of order:
80
73
81
-
- Implement multiple `IAmStartedBy<T>` interfaces for any message type that assumes the saga instance should already exist
82
-
- Override the saga not found behavior and throw an exception using `IHandleSagaNotFound` and rely on NServiceBus recoverability capability to retry messages and resolve out of order issues.
74
+
- Implement multiple `IAmStartedByMessages<T>` interfaces for any message type that assumes the saga instance should already exist
75
+
- Override the saga not found behavior and throw an exception using `IHandleSagaNotFound` and rely on NServiceBus recoverability capability to retry messages to resolve out-of-order issues.
83
76
84
77
#### Multiple message types starting a saga
85
78
@@ -91,7 +84,7 @@ When messages arrive in reverse order, the handler for the `CompleteOrder` messa
91
84
92
85
#### Relying on recoverability
93
86
94
-
In most scenarios, an acceptable solution to deal with out of order message delivery is to throw an exception when the saga instance does not exist. The message will be automatically retried, which may resolve the issue, or it will end up in the error queue, where it can be manually retried.
87
+
In most scenarios, an acceptable solution to deal with out-of-order message delivery is to throw an exception when the saga instance does not exist. The message will be automatically retried, which may resolve the issue; otherwise, it will be placed in the error queue, where it can be manually retried.
95
88
96
89
To override the default saga not found behavior [implement `IHandleSagaNotFound` and throw an exception](saga-not-found.md).
97
90
@@ -104,24 +97,23 @@ Correlation is needed in order to find existing saga instances based on data on
104
97
105
98
## Discarding messages when saga is not found
106
99
107
-
If a saga handles a message, but no related saga instance is found, then that message is discarded by default. Typically that happens when the saga has been already completed when the messages arrives and discarding the message is correct. If a different behavior is expected for specific scenarios, the default behavior [can be modified](saga-not-found.md).
100
+
If a saga handles a message but no related saga instance is found, the message is discarded by default. Typically, this happens when the saga has already been completed by the time a message arrives and discarding the message is correct. If a different behavior is expected for specific scenarios, the default behavior [can be modified](saga-not-found.md).
108
101
109
102
## Ending a saga
110
103
111
-
When a saga instance is no longer needed it can be completed using the `MarkAsComplete()` API. This tells the saga infrastructure that the instance is no longer needed and can be cleaned up.
104
+
When a saga instance is no longer needed, it can be completed using the `MarkAsComplete()` API. This tells the saga infrastructure that the instance is no longer needed and can be cleaned up.
112
105
113
-
> [!NOTE]
114
-
> Instance cleanup is implemented differently by the various saga persisters and is not guaranteed to be immediate.
106
+
Instance cleanup is implemented differently by the various saga persisters and is not guaranteed to be immediate.
115
107
116
108
### Outstanding timeouts
117
109
118
110
Outstanding timeouts requested by the saga instance will be discarded when they expire without triggering the [`IHandleSagaNotFound` API](saga-not-found.md)
119
111
120
-
### Messages arriving after saga has been completed
112
+
### Messages arriving after a saga has been completed
121
113
122
114
Messages that [are allowed to start a new saga instance](#starting-a-saga) will cause a new instance with the same correlation id to be created.
123
115
124
-
Messages handled by the saga(`IHandleMessages<T>`), arriving after the saga has completed, will be passed to the [`IHandleSagaNotFound` API](saga-not-found.md).
116
+
Messages handled by the saga(`IHandleMessages<T>`) that arrive after the saga has completed will be passed to the [`IHandleSagaNotFound` API](saga-not-found.md).
125
117
126
118
### Consistency considerations
127
119
@@ -147,12 +139,14 @@ snippet: saga-with-reply
147
139
148
140
This is one of the methods on the saga base class that would be very difficult to implement without tying the saga code to low-level parts of the NServiceBus infrastructure.
149
141
150
-
## Configuring saga persistence
142
+
## Saga persistence
151
143
152
144
Make sure to configure appropriate [saga persistence](/persistence/).
153
145
154
146
snippet: saga-configure
155
147
148
+
The choice of persistence can impact the design of saga data, for e.g. the length of the name of the saga class, virtual properties in saga etc. While NServiceBus persister tries to abstract things away, sometimes the limitations of the specific implementations can have an impact.
149
+
156
150
## Sagas and automatic subscriptions
157
151
158
152
The auto subscription feature applies to sagas as well as the regular message handlers.
NServiceBus will perform a check at startup to ensure that saga data types are not shared across sagas. An exception will be thrown at startup if any shared root types are found.
3
-
4
-
> [!NOTE]
5
-
> Types used in properties that are shared between sagas are not included. Depending on the persister, sharing types between saga root types can result in shared storage schema which should be avoided.
6
-
7
1
The startup check can be disabled by turning off the best practice validation:
0 commit comments