{"action":"created","comment":{"url":"https://api.github.com/repos/Particular/docs.particular.net/pulls/comments/74198353","id":74198353,"diff_hunk":"@@ -7,37 +7,66 @@ tags:\n - Transactions\n ---\n \n+## Summary\n \n-## Transactions and delivery guarantees\n+#### Transport transaction - Sends atomic with Receive\n \n-NServiceBus Azure Service Bus transport relies on the underlying Azure Service Bus library which requires the use of the `Serializable` isolation level (the most restrictive isolation level that does not permit `dirty reads`, `phantom reads` and `non repeatable reads`; will block any reader until the writer is committed. For more information refer to [Transaction Isolation Levels Explained in Details](http://dotnetspeak.com/2013/04/transaction-isolation-levels-explained-in-details) article.\n+Note: `SendAtomicWithReceive` level is supported only as from version 7 and when destination and receive queues are in the same namespace.\n \n-NServiceBus Azure Service Bus transport configuration is hard-coded to `Serializable` isolation level. Users can't override it.\n+The `SendAtomicWithReceive` guarantee is achieved by using `ViaEntityPath` property on outbound message senders. It's value is set to the receiving queue.\n \n+If the `ViaEntityPath` is not empty, then messages will be added to the receive queue. The messages will be forwarded to their actual destination (inside the broker) only when the complete operation is called on the received brokered message. The message won't be forwarded if the lock duration limit is exceeded (30 seconds by default) or if the message is explicitly abandoned.\n \n-### Versions 6 and above\n \n-Azure Service Bus Transport supports `SendAtomicWithReceive`, `ReceiveOnly` and `None` levels.\n+#### Transport transaction - Receive Only\n \n+The `ReceiveOnly` guarantee is based on the Azure Service Bus Peek-Lock mechanism.\n \n-#### Transport transaction - Sends atomic with Receive\n+The message is not removed from the queue directly after receive, but it's hidden for, by default , 30 seconds. That prevents other instances from picking it up. If the receiver fails to process the message withing that timeframe or explicitly abandons the message, then the message will become visible again. Other instances will be able to pick it up (effectively works like a rollback).\n \n-Note: `SendAtomicWithReceive` level is supported only when destination and receive queues are in the same namespace.\n \n-The `SendAtomicWithReceive` guarantee is achieved by using `ViaEntityPath` property on outbound messages. It's value is set to the receiving queue.\n+#### Unreliable (Transactions Disabled)\n \n-If the `ViaEntityPath` is not empty, then messages will be added to the receive queue. The messages will be forwarded to their actual destination (inside the broker) only when the complete operation is called on the received brokered message. The message won't be forwarded if the lock duration limit is exceeded (30 seconds by default) or if the message is explicitly abandoned.\n+When transactions are disabled then NServiceBus the transport uses the [ASB's ReceiveAndDelete mode](https://msdn.microsoft.com/en-us/library/microsoft.servicebus.messaging.receivemode.aspx).\n \n+The message is deleted from the queue directly after receive operation completes, before it is processed.\n \n-#### Transport transaction - Receive Only\n+This mode is highly unrecommended!\n \n-The `ReceiveOnly` guarantee is based on the Azure Service Bus Peek-Lock mechanism.\n \n-The message is not removed from the queue directly after receive, but it's hidden by default for 30 seconds. That prevents other instances from picking it up. If the receiver fails to process the message withing that timeframe or explicitly abandons the message, then the message will become visible again. Other instances will be able to pick it up.\n+## Understanding internal transactions and delivery guarantees\n \n+### Versions 6 and below\n \n-#### Unreliable (Transactions Disabled)\n+NServiceBus relies on transaction scopes to control how it behaves when it comes to transactions, message dispatching and commit/rollback guarantees. The architecture is schematically represented like this:\n \n-When transactions are disabled then NServiceBus uses the [ASB's ReceiveAndDelete mode](https://msdn.microsoft.com/en-us/library/microsoft.servicebus.messaging.receivemode.aspx).\n+[TODO: insert picture 1]\n+\n+The Azure Service Bus library, which is used in the Azure Service Bus transport, has a few requirements, when it is used inside a transaction scope, that must be taken into account.\n+* It requires the use of the `Serializable` isolation level when it's used inside a transaction scope (the most restrictive isolation level that does not permit `dirty reads`, `phantom reads` and `non repeatable reads`; will block any reader until the writer is committed). For more information refer to [Transaction Isolation Levels Explained in Details](http://dotnetspeak.com/2013/04/transaction-isolation-levels-explained-in-details) article. NServiceBus Azure Service Bus transport configuration is therefore hard-coded to `Serializable` isolation level. Users can't override it.\n+* It also proactively checks to see whether it is used alone inside a transaction and it will throw an exception if used together with another transactional resource such as a database connection. To allow the user code to still use a transaction for it's database connections, in another transaction scope provided by NServiceBus around a handler, the 2 scopes are kept apart and synchronized using an implementation of `IEnlistmentNotification` for both the send and completion operations. (Schematically represented by the orange to yellow star notation in the diagram)\n+* Finally, it does not allow send operation on different messaging entities in the same transaction. So for example a send to Queue1 and a send to Queue2 in the same scope, will throw an exception. Again this problem is negated using separate instances of `IEnlistmentNotification` on each send operation. \n+\n+The benefit of this approach is that user code can use database connections and send messages to multiple endpoints while maintaining the aspects expected from a transaction. However the downside is that each operation is individual. Even though they are executed at the same moment in a transaction, it can happen in theory that one of the operations fails, while the others do not. This problem is again mitigated by using multiple layers of retry behavior to reduce the chance, so a short interruption in connectivity or broker outage will not have a significant impact and operations will eventually succeed. However a full outage right after the database transaction committed could lead to a send operation that did not execute. \n+\n+The maximum guarantee that the transport can deliver in these versions is in effect `ReceiveOnly`. \n+\n+Enabling/Disabling DTC will have no effect, this setting is ignored either way as Azure Service Bus does not support it.\n+\n+Disabling transactions will also turn off the `PeekLock` mechanism, so that the transport immediately completes any incoming message before processing and shows no retry behavior. \n+  \n+\n+### Versions 7 and above\n+\n+In NServiceBus version 6 and above, the shape of the pipeline has changed so that a transaction scope is no longer mandatory in order to orchestrate dispatching of messages with complete/rollback behavior. The new architecture is schematically represented like this:\n+\n+[TODO: insert picture 2]\n+\n+Note the fork in the pipeline which is separating the user code invocation path from the dispatching path. By putting a suppress scope around this section of the pipeline, the transport can prevent any other transactional resource from enlisting in the scope.","path":"nservicebus/azure-service-bus/transaction-support.md","position":73,"original_position":73,"commit_id":"f3bad522cf755aa3358120020028efec8354f06f","original_commit_id":"f3bad522cf755aa3358120020028efec8354f06f","user":{"login":"danielmarbach","id":174258,"avatar_url":"https://avatars.githubusercontent.com/u/174258?v=3","gravatar_id":"","url":"https://api.github.com/users/danielmarbach","html_url":"https://github.com/danielmarbach","followers_url":"https://api.github.com/users/danielmarbach/followers","following_url":"https://api.github.com/users/danielmarbach/following{/other_user}","gists_url":"https://api.github.com/users/danielmarbach/gists{/gist_id}","starred_url":"https://api.github.com/users/danielmarbach/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/danielmarbach/subscriptions","organizations_url":"https://api.github.com/users/danielmarbach/orgs","repos_url":"https://api.github.com/users/danielmarbach/repos","events_url":"https://api.github.com/users/danielmarbach/events{/privacy}","received_events_url":"https://api.github.com/users/danielmarbach/received_events","type":"User","site_admin":false},"body":"Link here to explanation of forks?","created_at":"2016-08-10T07:38:33Z","updated_at":"2016-08-10T07:38:33Z","html_url":"https://github.com/Particular/docs.particular.net/pull/1755#discussion_r74198353","pull_request_url":"https://api.github.com/repos/Particular/docs.particular.net/pulls/1755","_links":{"self":{"href":"https://api.github.com/repos/Particular/docs.particular.net/pulls/comments/74198353"},"html":{"href":"https://github.com/Particular/docs.particular.net/pull/1755#discussion_r74198353"},"pull_request":{"href":"https://api.github.com/repos/Particular/docs.particular.net/pulls/1755"}}},"pull_request":{"url":"https://api.github.com/repos/Particular/docs.particular.net/pulls/1755","id":80619234,"html_url":"https://github.com/Particular/docs.particular.net/pull/1755","diff_url":"https://github.com/Particular/docs.particular.net/pull/1755.diff","patch_url":"https://github.com/Particular/docs.particular.net/pull/1755.patch","issue_url":"https://api.github.com/repos/Particular/docs.particular.net/issues/1755","number":1755,"state":"open","locked":false,"title":"[WIP] Asb atomic send receive","user":{"login":"yvesgoeleven","id":471354,"avatar_url":"https://avatars.githubusercontent.com/u/471354?v=3","gravatar_id":"","url":"https://api.github.com/users/yvesgoeleven","html_url":"https://github.com/yvesgoeleven","followers_url":"https://api.github.com/users/yvesgoeleven/followers","following_url":"https://api.github.com/users/yvesgoeleven/following{/other_user}","gists_url":"https://api.github.com/users/yvesgoeleven/gists{/gist_id}","starred_url":"https://api.github.com/users/yvesgoeleven/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yvesgoeleven/subscriptions","organizations_url":"https://api.github.com/users/yvesgoeleven/orgs","repos_url":"https://api.github.com/users/yvesgoeleven/repos","events_url":"https://api.github.com/users/yvesgoeleven/events{/privacy}","received_events_url":"https://api.github.com/users/yvesgoeleven/received_events","type":"User","site_admin":false},"body":"Properly documents how ASB transport interacts with transactions (behavior after core beta 6 update)","created_at":"2016-08-09T13:23:50Z","updated_at":"2016-08-10T07:38:33Z","closed_at":null,"merged_at":null,"merge_commit_sha":"29277b69e92cf0d4a065f61f414ad6ad9a110134","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/Particular/docs.particular.net/pulls/1755/commits","review_comments_url":"https://api.github.com/repos/Particular/docs.particular.net/pulls/1755/comments","review_comment_url":"https://api.github.com/repos/Particular/docs.particular.net/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Particular/docs.particular.net/issues/1755/comments","statuses_url":"https://api.github.com/repos/Particular/docs.particular.net/statuses/f3bad522cf755aa3358120020028efec8354f06f","head":{"label":"Particular:asb-atomic-send-receive","ref":"asb-atomic-send-receive","sha":"f3bad522cf755aa3358120020028efec8354f06f","user":{"login":"Particular","id":5038441,"avatar_url":"https://avatars.githubusercontent.com/u/5038441?v=3","gravatar_id":"","url":"https://api.github.com/users/Particular","html_url":"https://github.com/Particular","followers_url":"https://api.github.com/users/Particular/followers","following_url":"https://api.github.com/users/Particular/following{/other_user}","gists_url":"https://api.github.com/users/Particular/gists{/gist_id}","starred_url":"https://api.github.com/users/Particular/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Particular/subscriptions","organizations_url":"https://api.github.com/users/Particular/orgs","repos_url":"https://api.github.com/users/Particular/repos","events_url":"https://api.github.com/users/Particular/events{/privacy}","received_events_url":"https://api.github.com/users/Particular/received_events","type":"Organization","site_admin":false},"repo":{"id":11727301,"name":"docs.particular.net","full_name":"Particular/docs.particular.net","owner":{"login":"Particular","id":5038441,"avatar_url":"https://avatars.githubusercontent.com/u/5038441?v=3","gravatar_id":"","url":"https://api.github.com/users/Particular","html_url":"https://github.com/Particular","followers_url":"https://api.github.com/users/Particular/followers","following_url":"https://api.github.com/users/Particular/following{/other_user}","gists_url":"https://api.github.com/users/Particular/gists{/gist_id}","starred_url":"https://api.github.com/users/Particular/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Particular/subscriptions","organizations_url":"https://api.github.com/users/Particular/orgs","repos_url":"https://api.github.com/users/Particular/repos","events_url":"https://api.github.com/users/Particular/events{/privacy}","received_events_url":"https://api.github.com/users/Particular/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Particular/docs.particular.net","description":"Contains all the content for the Particular documentation site","fork":false,"url":"https://api.github.com/repos/Particular/docs.particular.net","forks_url":"https://api.github.com/repos/Particular/docs.particular.net/forks","keys_url":"https://api.github.com/repos/Particular/docs.particular.net/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Particular/docs.particular.net/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Particular/docs.particular.net/teams","hooks_url":"https://api.github.com/repos/Particular/docs.particular.net/hooks","issue_events_url":"https://api.github.com/repos/Particular/docs.particular.net/issues/events{/number}","events_url":"https://api.github.com/repos/Particular/docs.particular.net/events","assignees_url":"https://api.github.com/repos/Particular/docs.particular.net/assignees{/user}","branches_url":"https://api.github.com/repos/Particular/docs.particular.net/branches{/branch}","tags_url":"https://api.github.com/repos/Particular/docs.particular.net/tags","blobs_url":"https://api.github.com/repos/Particular/docs.particular.net/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Particular/docs.particular.net/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Particular/docs.particular.net/git/refs{/sha}","trees_url":"https://api.github.com/repos/Particular/docs.particular.net/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Particular/docs.particular.net/statuses/{sha}","languages_url":"https://api.github.com/repos/Particular/docs.particular.net/languages","stargazers_url":"https://api.github.com/repos/Particular/docs.particular.net/stargazers","contributors_url":"https://api.github.com/repos/Particular/docs.particular.net/contributors","subscribers_url":"https://api.github.com/repos/Particular/docs.particular.net/subscribers","subscription_url":"https://api.github.com/repos/Particular/docs.particular.net/subscription","commits_url":"https://api.github.com/repos/Particular/docs.particular.net/commits{/sha}","git_commits_url":"https://api.github.com/repos/Particular/docs.particular.net/git/commits{/sha}","comments_url":"https://api.github.com/repos/Particular/docs.particular.net/comments{/number}","issue_comment_url":"https://api.github.com/repos/Particular/docs.particular.net/issues/comments{/number}","contents_url":"https://api.github.com/repos/Particular/docs.particular.net/contents/{+path}","compare_url":"https://api.github.com/repos/Particular/docs.particular.net/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Particular/docs.particular.net/merges","archive_url":"https://api.github.com/repos/Particular/docs.particular.net/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Particular/docs.particular.net/downloads","issues_url":"https://api.github.com/repos/Particular/docs.particular.net/issues{/number}","pulls_url":"https://api.github.com/repos/Particular/docs.particular.net/pulls{/number}","milestones_url":"https://api.github.com/repos/Particular/docs.particular.net/milestones{/number}","notifications_url":"https://api.github.com/repos/Particular/docs.particular.net/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Particular/docs.particular.net/labels{/name}","releases_url":"https://api.github.com/repos/Particular/docs.particular.net/releases{/id}","deployments_url":"https://api.github.com/repos/Particular/docs.particular.net/deployments","created_at":"2013-07-28T23:18:15Z","updated_at":"2016-07-19T19:24:20Z","pushed_at":"2016-08-10T07:37:24Z","git_url":"git://github.com/Particular/docs.particular.net.git","ssh_url":"git@github.com:Particular/docs.particular.net.git","clone_url":"https://github.com/Particular/docs.particular.net.git","svn_url":"https://github.com/Particular/docs.particular.net","homepage":"http://docs.particular.net","size":55893,"stargazers_count":31,"watchers_count":31,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":140,"mirror_url":null,"open_issues_count":105,"forks":140,"open_issues":105,"watchers":31,"default_branch":"master"}},"base":{"label":"Particular:master","ref":"master","sha":"8b6afa3b9332c8b289031b6b752712ae5ad8841a","user":{"login":"Particular","id":5038441,"avatar_url":"https://avatars.githubusercontent.com/u/5038441?v=3","gravatar_id":"","url":"https://api.github.com/users/Particular","html_url":"https://github.com/Particular","followers_url":"https://api.github.com/users/Particular/followers","following_url":"https://api.github.com/users/Particular/following{/other_user}","gists_url":"https://api.github.com/users/Particular/gists{/gist_id}","starred_url":"https://api.github.com/users/Particular/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Particular/subscriptions","organizations_url":"https://api.github.com/users/Particular/orgs","repos_url":"https://api.github.com/users/Particular/repos","events_url":"https://api.github.com/users/Particular/events{/privacy}","received_events_url":"https://api.github.com/users/Particular/received_events","type":"Organization","site_admin":false},"repo":{"id":11727301,"name":"docs.particular.net","full_name":"Particular/docs.particular.net","owner":{"login":"Particular","id":5038441,"avatar_url":"https://avatars.githubusercontent.com/u/5038441?v=3","gravatar_id":"","url":"https://api.github.com/users/Particular","html_url":"https://github.com/Particular","followers_url":"https://api.github.com/users/Particular/followers","following_url":"https://api.github.com/users/Particular/following{/other_user}","gists_url":"https://api.github.com/users/Particular/gists{/gist_id}","starred_url":"https://api.github.com/users/Particular/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Particular/subscriptions","organizations_url":"https://api.github.com/users/Particular/orgs","repos_url":"https://api.github.com/users/Particular/repos","events_url":"https://api.github.com/users/Particular/events{/privacy}","received_events_url":"https://api.github.com/users/Particular/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Particular/docs.particular.net","description":"Contains all the content for the Particular documentation site","fork":false,"url":"https://api.github.com/repos/Particular/docs.particular.net","forks_url":"https://api.github.com/repos/Particular/docs.particular.net/forks","keys_url":"https://api.github.com/repos/Particular/docs.particular.net/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Particular/docs.particular.net/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Particular/docs.particular.net/teams","hooks_url":"https://api.github.com/repos/Particular/docs.particular.net/hooks","issue_events_url":"https://api.github.com/repos/Particular/docs.particular.net/issues/events{/number}","events_url":"https://api.github.com/repos/Particular/docs.particular.net/events","assignees_url":"https://api.github.com/repos/Particular/docs.particular.net/assignees{/user}","branches_url":"https://api.github.com/repos/Particular/docs.particular.net/branches{/branch}","tags_url":"https://api.github.com/repos/Particular/docs.particular.net/tags","blobs_url":"https://api.github.com/repos/Particular/docs.particular.net/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Particular/docs.particular.net/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Particular/docs.particular.net/git/refs{/sha}","trees_url":"https://api.github.com/repos/Particular/docs.particular.net/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Particular/docs.particular.net/statuses/{sha}","languages_url":"https://api.github.com/repos/Particular/docs.particular.net/languages","stargazers_url":"https://api.github.com/repos/Particular/docs.particular.net/stargazers","contributors_url":"https://api.github.com/repos/Particular/docs.particular.net/contributors","subscribers_url":"https://api.github.com/repos/Particular/docs.particular.net/subscribers","subscription_url":"https://api.github.com/repos/Particular/docs.particular.net/subscription","commits_url":"https://api.github.com/repos/Particular/docs.particular.net/commits{/sha}","git_commits_url":"https://api.github.com/repos/Particular/docs.particular.net/git/commits{/sha}","comments_url":"https://api.github.com/repos/Particular/docs.particular.net/comments{/number}","issue_comment_url":"https://api.github.com/repos/Particular/docs.particular.net/issues/comments{/number}","contents_url":"https://api.github.com/repos/Particular/docs.particular.net/contents/{+path}","compare_url":"https://api.github.com/repos/Particular/docs.particular.net/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Particular/docs.particular.net/merges","archive_url":"https://api.github.com/repos/Particular/docs.particular.net/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Particular/docs.particular.net/downloads","issues_url":"https://api.github.com/repos/Particular/docs.particular.net/issues{/number}","pulls_url":"https://api.github.com/repos/Particular/docs.particular.net/pulls{/number}","milestones_url":"https://api.github.com/repos/Particular/docs.particular.net/milestones{/number}","notifications_url":"https://api.github.com/repos/Particular/docs.particular.net/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Particular/docs.particular.net/labels{/name}","releases_url":"https://api.github.com/repos/Particular/docs.particular.net/releases{/id}","deployments_url":"https://api.github.com/repos/Particular/docs.particular.net/deployments","created_at":"2013-07-28T23:18:15Z","updated_at":"2016-07-19T19:24:20Z","pushed_at":"2016-08-10T07:37:24Z","git_url":"git://github.com/Particular/docs.particular.net.git","ssh_url":"git@github.com:Particular/docs.particular.net.git","clone_url":"https://github.com/Particular/docs.particular.net.git","svn_url":"https://github.com/Particular/docs.particular.net","homepage":"http://docs.particular.net","size":55893,"stargazers_count":31,"watchers_count":31,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":140,"mirror_url":null,"open_issues_count":105,"forks":140,"open_issues":105,"watchers":31,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Particular/docs.particular.net/pulls/1755"},"html":{"href":"https://github.com/Particular/docs.particular.net/pull/1755"},"issue":{"href":"https://api.github.com/repos/Particular/docs.particular.net/issues/1755"},"comments":{"href":"https://api.github.com/repos/Particular/docs.particular.net/issues/1755/comments"},"review_comments":{"href":"https://api.github.com/repos/Particular/docs.particular.net/pulls/1755/comments"},"review_comment":{"href":"https://api.github.com/repos/Particular/docs.particular.net/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Particular/docs.particular.net/pulls/1755/commits"},"statuses":{"href":"https://api.github.com/repos/Particular/docs.particular.net/statuses/f3bad522cf755aa3358120020028efec8354f06f"}}}}
{"actor":{"display_login":"danielmarbach"}}