{"action":"created","issue":{"url":"https://api.github.com/repos/netty/netty/issues/5395","repository_url":"https://api.github.com/repos/netty/netty","labels_url":"https://api.github.com/repos/netty/netty/issues/5395/labels{/name}","comments_url":"https://api.github.com/repos/netty/netty/issues/5395/comments","events_url":"https://api.github.com/repos/netty/netty/issues/5395/events","html_url":"https://github.com/netty/netty/pull/5395","id":160315188,"number":5395,"title":"Auto-flush for all channels.","user":{"login":"NiteshKant","id":820005,"avatar_url":"https://avatars.githubusercontent.com/u/820005?v=3","gravatar_id":"","url":"https://api.github.com/users/NiteshKant","html_url":"https://github.com/NiteshKant","followers_url":"https://api.github.com/users/NiteshKant/followers","following_url":"https://api.github.com/users/NiteshKant/following{/other_user}","gists_url":"https://api.github.com/users/NiteshKant/gists{/gist_id}","starred_url":"https://api.github.com/users/NiteshKant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NiteshKant/subscriptions","organizations_url":"https://api.github.com/users/NiteshKant/orgs","repos_url":"https://api.github.com/users/NiteshKant/repos","events_url":"https://api.github.com/users/NiteshKant/events{/privacy}","received_events_url":"https://api.github.com/users/NiteshKant/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/netty/netty/labels/feature","name":"feature","color":"009800"},{"url":"https://api.github.com/repos/netty/netty/labels/improvement","name":"improvement","color":"0052cc"}],"state":"open","locked":false,"assignee":{"login":"normanmaurer","id":439362,"avatar_url":"https://avatars.githubusercontent.com/u/439362?v=3","gravatar_id":"","url":"https://api.github.com/users/normanmaurer","html_url":"https://github.com/normanmaurer","followers_url":"https://api.github.com/users/normanmaurer/followers","following_url":"https://api.github.com/users/normanmaurer/following{/other_user}","gists_url":"https://api.github.com/users/normanmaurer/gists{/gist_id}","starred_url":"https://api.github.com/users/normanmaurer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/normanmaurer/subscriptions","organizations_url":"https://api.github.com/users/normanmaurer/orgs","repos_url":"https://api.github.com/users/normanmaurer/repos","events_url":"https://api.github.com/users/normanmaurer/events{/privacy}","received_events_url":"https://api.github.com/users/normanmaurer/received_events","type":"User","site_admin":false},"assignees":[{"login":"normanmaurer","id":439362,"avatar_url":"https://avatars.githubusercontent.com/u/439362?v=3","gravatar_id":"","url":"https://api.github.com/users/normanmaurer","html_url":"https://github.com/normanmaurer","followers_url":"https://api.github.com/users/normanmaurer/followers","following_url":"https://api.github.com/users/normanmaurer/following{/other_user}","gists_url":"https://api.github.com/users/normanmaurer/gists{/gist_id}","starred_url":"https://api.github.com/users/normanmaurer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/normanmaurer/subscriptions","organizations_url":"https://api.github.com/users/normanmaurer/orgs","repos_url":"https://api.github.com/users/normanmaurer/repos","events_url":"https://api.github.com/users/normanmaurer/events{/privacy}","received_events_url":"https://api.github.com/users/normanmaurer/received_events","type":"User","site_admin":false}],"milestone":{"url":"https://api.github.com/repos/netty/netty/milestones/145","html_url":"https://github.com/netty/netty/milestones/4.1.3.Final","labels_url":"https://api.github.com/repos/netty/netty/milestones/145/labels","id":1859617,"number":145,"title":"4.1.3.Final","description":"","creator":{"login":"normanmaurer","id":439362,"avatar_url":"https://avatars.githubusercontent.com/u/439362?v=3","gravatar_id":"","url":"https://api.github.com/users/normanmaurer","html_url":"https://github.com/normanmaurer","followers_url":"https://api.github.com/users/normanmaurer/followers","following_url":"https://api.github.com/users/normanmaurer/following{/other_user}","gists_url":"https://api.github.com/users/normanmaurer/gists{/gist_id}","starred_url":"https://api.github.com/users/normanmaurer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/normanmaurer/subscriptions","organizations_url":"https://api.github.com/users/normanmaurer/orgs","repos_url":"https://api.github.com/users/normanmaurer/repos","events_url":"https://api.github.com/users/normanmaurer/events{/privacy}","received_events_url":"https://api.github.com/users/normanmaurer/received_events","type":"User","site_admin":false},"open_issues":46,"closed_issues":1,"state":"open","created_at":"2016-07-01T11:24:12Z","updated_at":"2016-07-07T15:41:58Z","due_on":null,"closed_at":null},"comments":41,"created_at":"2016-06-15T00:35:37Z","updated_at":"2016-07-07T16:16:27Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/netty/netty/pulls/5395","html_url":"https://github.com/netty/netty/pull/5395","diff_url":"https://github.com/netty/netty/pull/5395.diff","patch_url":"https://github.com/netty/netty/pull/5395.patch"},"body":"###   Motivation\r\n\r\n  Netty is extremely flexible in providing a way to batch writes on the physical socket by providing a means to `write` and `flush` writes separately. This works for the usecases when the application pattern is one that provides predictable finite streams of data. In cases, when an infinite stream of data is to be written on a channel which does not have any predictable length and duration of writes, a user has the following choices:\r\n\r\n - Flush every write. \r\n - Flush batches based on time and count (eg: Flush every 10 message or 1 second)   \r\n\r\nNeither of the above is ideal as the nature of stream is completely unpredictable. Flushing every write is costly as `flush` is a system call. Flushing batches is arbitrary and has to be deviced for each stream in an arbitrary way.  \r\n\r\n### Modifications  \r\n\r\nThis change is a Proof Of Concept, is __not__ complete and has the following missing pieces:\r\n\r\n  1. _UnitTests_: I haven't yet added any tests.\r\n2. `DefaultChannelPipeline` caches the value of `ChannelConfig.isAutoRead()` at channel activation and does not update it later.\r\n3. _Documentation_: I haven’t yet added any javadocs or comments to explain the changes.   \r\n\r\n__I would like to invest more time on this change, if there is some consensus on whether this makes sense.  __\r\n\r\n Here are the details of the change:   \r\n\r\n##### `ChannelOption.AUTO_FLUSH`   \r\n\r\n A channel option for enable/disable auto flush (this feature) is added.  When, this option is enabled, explicit flushes of writes on that channel are not required. Users can just call `write` and these writes will be flushed on every iteration of the associated `eventLoop` for that channel.\r\nAny explicit flushes on the channel work as before i.e. it flushes all writes that happened before the flush and were not flushed. \r\n\r\n##### Changes to `EventLoop`\r\n\r\nTwo methods:\r\n\r\n```java\r\nvoid onEventLoopIteration(Runnable task);  \r\nboolean removeOnEventLoopIterationTask(Runnable task);\r\n```\r\n\r\nare added to `EventLoop` interface for adding/removing a task to be executed at the end of each iteration of this `eventloop`.\r\n\r\n##### Changes to `SingleThreadEventExecutor`\r\n\r\nAdded a method to `SingleThreadEventExecutor`\r\n\r\n```java\r\nprotected void afterRunningAllTasks(boolean timedOut) { }\r\n\r\n```\r\n\r\nThis is invoked after all tasks are run for this executor OR if the passed timeout value for `runAllTasks(long timeoutNanos)` is expired. In case of `timeout` the argument to this method is `true`.\r\n\r\n##### Changes to `SingleThreadEventLoop`\r\n\r\nAdded a queue of `tailTasks` to this class to hold all tasks to be executed at the end of every iteration.\r\nIn the case if `afterRunningAllTasks` is invoked as a result of a timeout, no tail tasks are executed. Instead, the tail tasks are executed at the start of the next iteration.\r\n\r\n##### Changes to `DefaultChannelPipeline`\r\n\r\nAdditional calls to `wakeup` the selector for the first `unflushed` write. This is done so that there isn’t a latency between the first `unflushed` write and invocation of the tails tasks.\r\n\r\n##### Changes to `AbstractChannel`\r\n\r\nManages the lifecycle of `AutoFlushTask` per channel as the task to be invoked per iteration of the eventloop.\r\n \r\n### Result\r\n\r\n  This results in a much better model for infinite streams written to a channel.\r\n\r\nI wrote a micro-benchmark(`io.netty.microbench.channel.AutoFlushBenchmark`) to demonstrate the throughput variations between all possible current alternatives.\r\nHere are the results for the same:\r\n\r\n```\r\nBenchmark                                             (flush)  (writeCount)   Mode  Cnt      Score       Error  Units\r\nAutoFlushBenchmark.measureWritesWithFlushAtEnd           true             1  thrpt   10  50679.715 ±  1557.854  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushAtEnd           true            10  thrpt   10  38835.955 ±   887.623  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushAtEnd           true           100  thrpt   10  11916.433 ±    96.373  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushAtEnd          false             1  thrpt   10  50452.022 ±  1366.724  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushAtEnd          false            10  thrpt   10  37895.350 ±   492.081  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushAtEnd          false           100  thrpt   10  11586.364 ±   132.067  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEvery5          true             1  thrpt   10  50592.297 ±  1102.360  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEvery5          true            10  thrpt   10  27176.931 ±   335.812  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEvery5          true           100  thrpt   10   5447.520 ±    82.222  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEvery5         false             1  thrpt   10  49741.284 ±   752.898  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEvery5         false            10  thrpt   10  37140.030 ±  1556.103  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEvery5         false           100  thrpt   10  11503.099 ±    94.868  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEverySecond     true             1  thrpt   10  36295.567 ± 13013.318  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEverySecond     true            10  thrpt   10  26575.064 ±  3091.255  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEverySecond     true           100  thrpt   10   8088.351 ±  1390.408  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEverySecond    false             1  thrpt   10  49721.904 ±   718.826  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEverySecond    false            10  thrpt   10  38451.303 ±   712.448  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushEverySecond    false           100  thrpt   10  11387.047 ±   139.104  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushOnEach          true             1  thrpt   10  51501.154 ±  3866.709  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushOnEach          true            10  thrpt   10  15308.538 ±   351.524  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushOnEach          true           100  thrpt   10   1746.113 ±    31.679  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushOnEach         false             1  thrpt   10  49927.340 ±  1482.713  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushOnEach         false            10  thrpt   10  37389.582 ±  1859.288  ops/s\r\nAutoFlushBenchmark.measureWritesWithFlushOnEach         false           100  thrpt   10  11604.148 ±   342.597  ops/s\r\n```\r\n\r\nThis was done on MBP with 16GB of memory and 8 processors."},"comment":{"url":"https://api.github.com/repos/netty/netty/issues/comments/231129511","html_url":"https://github.com/netty/netty/pull/5395#issuecomment-231129511","issue_url":"https://api.github.com/repos/netty/netty/issues/5395","id":231129511,"user":{"login":"NiteshKant","id":820005,"avatar_url":"https://avatars.githubusercontent.com/u/820005?v=3","gravatar_id":"","url":"https://api.github.com/users/NiteshKant","html_url":"https://github.com/NiteshKant","followers_url":"https://api.github.com/users/NiteshKant/followers","following_url":"https://api.github.com/users/NiteshKant/following{/other_user}","gists_url":"https://api.github.com/users/NiteshKant/gists{/gist_id}","starred_url":"https://api.github.com/users/NiteshKant/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NiteshKant/subscriptions","organizations_url":"https://api.github.com/users/NiteshKant/orgs","repos_url":"https://api.github.com/users/NiteshKant/repos","events_url":"https://api.github.com/users/NiteshKant/events{/privacy}","received_events_url":"https://api.github.com/users/NiteshKant/received_events","type":"User","site_admin":false},"created_at":"2016-07-07T16:16:27Z","updated_at":"2016-07-07T16:16:27Z","body":"@normanmaurer sure, do you want the method `SingleThreadEventLoop.executeAfterEventLoopIteration()` public (with `@UnstableApi`) or package-private as today."}}
{"actor":{"display_login":"NiteshKant"}}