{"action":"created","issue":{"url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client/issues/307","repository_url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client","labels_url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client/issues/307/labels{/name}","comments_url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client/issues/307/comments","events_url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client/issues/307/events","html_url":"https://github.com/rabbitmq/rabbitmq-dotnet-client/pull/307","id":205293233,"number":307,"title":"Introduce async consumer dispatcher","user":{"login":"danielmarbach","id":174258,"avatar_url":"https://avatars2.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},"labels":[{"id":178242242,"url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client/labels/effort-medium","name":"effort-medium","color":"5080b8","default":false},{"id":188382900,"url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client/labels/enhancement","name":"enhancement","color":"009800","default":true}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client/milestones/23","html_url":"https://github.com/rabbitmq/rabbitmq-dotnet-client/milestone/23","labels_url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client/milestones/23/labels","id":2009507,"number":23,"title":"5.0.0","description":"","creator":{"login":"michaelklishin","id":1090,"avatar_url":"https://avatars0.githubusercontent.com/u/1090?v=3","gravatar_id":"","url":"https://api.github.com/users/michaelklishin","html_url":"https://github.com/michaelklishin","followers_url":"https://api.github.com/users/michaelklishin/followers","following_url":"https://api.github.com/users/michaelklishin/following{/other_user}","gists_url":"https://api.github.com/users/michaelklishin/gists{/gist_id}","starred_url":"https://api.github.com/users/michaelklishin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michaelklishin/subscriptions","organizations_url":"https://api.github.com/users/michaelklishin/orgs","repos_url":"https://api.github.com/users/michaelklishin/repos","events_url":"https://api.github.com/users/michaelklishin/events{/privacy}","received_events_url":"https://api.github.com/users/michaelklishin/received_events","type":"User","site_admin":false},"open_issues":0,"closed_issues":6,"state":"open","created_at":"2016-09-16T16:07:30Z","updated_at":"2017-03-06T21:16:54Z","due_on":null,"closed_at":null},"comments":24,"created_at":"2017-02-03T23:02:11Z","updated_at":"2017-03-06T22:27:39Z","closed_at":"2017-03-06T09:17:41Z","pull_request":{"url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client/pulls/307","html_url":"https://github.com/rabbitmq/rabbitmq-dotnet-client/pull/307","diff_url":"https://github.com/rabbitmq/rabbitmq-dotnet-client/pull/307.diff","patch_url":"https://github.com/rabbitmq/rabbitmq-dotnet-client/pull/307.patch"},"body":"I was following an idea to see if it would be possible to make a tiny step in a minor version to become async on the path where the client dispatches to the consumers. I was able to get it going by doing the following:\r\n\r\n- Introduce an internal `IAsyncConnection`\r\n- Introduce an `Async` flag on the connection factory which allows to opt-in for the async consumer dispatcher\r\n- Decorate the `IConnection `with an `IAsyncConnection `when the connection factory flag is set\r\n- Introduce `IAsyncBasicConsumer `for fully asynchronous consumers\r\n- Basic consume ensures when `IAsyncConnection `is used you can only use the `IAsyncBasicConsumer ` implementations\r\n- `AsyncConsumerDispatcher `stick to `IConsumerDispatcher `but internally enqueues `Func<Task> `instead of `Action`\r\n- The worker pool can now use `Task.Run` with an async body. The idle phase is implemented with a `TaskCompletionSource `and a `Task.WhenAny `that either completes when the delay is due or the TCS is set. Use atomic field reference assigned to create a new `TaskCompletionSource `after each iteration (this should be fine since we only have one loop and concurrent enqueues should still work due to TrySetResult)\r\n- Removed closure allocation in dispatcher. Removes the `Func<Task> `allocations per call on the consumer dispatcher. So that means we save one allocation per call\r\n\r\n#### Before\r\n```\r\npublic void HandleBasicDeliver(IBasicConsumer consumer, string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, IBasicProperties basicProperties, byte[] body)\r\n{\r\n    AsyncConsumerDispatcher.\\u003C\\u003Ec__DisplayClass11_0 cDisplayClass110 = new AsyncConsumerDispatcher.\\u003C\\u003Ec__DisplayClass11_0();\r\n    cDisplayClass110.\\u003C\\u003E4__this = this;\r\n    cDisplayClass110.consumer = consumer;\r\n    cDisplayClass110.consumerTag = consumerTag;\r\n    cDisplayClass110.deliveryTag = deliveryTag;\r\n    cDisplayClass110.redelivered = redelivered;\r\n    cDisplayClass110.exchange = exchange;\r\n    cDisplayClass110.routingKey = routingKey;\r\n    cDisplayClass110.basicProperties = basicProperties;\r\n    cDisplayClass110.body = body;\r\n    // ISSUE: method pointer\r\n    this.UnlessShuttingDown(new Func<Task>((object) cDisplayClass110, __methodptr(\\u003CHandleBasicDeliver\\u003Eb__0)));\r\n}\r\n```\r\n#### After\r\n```\r\npublic void HandleBasicDeliver(IBasicConsumer consumer, string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, IBasicProperties basicProperties, byte[] body)\r\n{\r\n    this.ScheduleUnlessShuttingDown((Work) new BasicDeliver(consumer, consumerTag, deliveryTag, redelivered, exchange, routingKey, basicProperties, body));\r\n}\r\n```\r\n\r\n## Benefits\r\n\r\n- Fully async for the consumer implementation\r\n- Consumer worker thread is no longer blocked at any time and can participate in consumer implementation execution and continuations\r\n- Can be implemented as a minor withouth breaking anything while introducing slightly less efficient consumer dispatching\r\n\r\n## Tradeoffs\r\n\r\n- Some evil internal casting (but I think this is necessary if we want to make it none breaking)\r\n- Introduced `AsyncEventHandler` to stick as much as possible to the current way how consumers work although probably a `Func<Args, Task>` would be more elegant.\r\n- Requires a few `GetAwaiter().GetResult()` calls (*shudder*)\r\n- Changed the shutdown phase to wait for the work pool to complete (happy to remove if not desired)"},"comment":{"url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client/issues/comments/284554716","html_url":"https://github.com/rabbitmq/rabbitmq-dotnet-client/pull/307#issuecomment-284554716","issue_url":"https://api.github.com/repos/rabbitmq/rabbitmq-dotnet-client/issues/307","id":284554716,"user":{"login":"danielmarbach","id":174258,"avatar_url":"https://avatars2.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},"created_at":"2017-03-06T22:27:39Z","updated_at":"2017-03-06T22:27:39Z","body":"Btw @kjnilsson the changes could go into a minor. doesn't have to be a major bump. "}}
{"actor":{"display_login":"danielmarbach"}}