{"action":"created","comment":{"url":"https://api.github.com/repos/dotnet/orleans/pulls/comments/93143781","pull_request_review_id":13665892,"id":93143781,"diff_hunk":"@@ -0,0 +1,226 @@\n+﻿using Orleans.LogConsistency;\n+using System;\n+using System.Collections.Generic;\n+using System.Linq;\n+using System.Text;\n+using System.Threading.Tasks;\n+\n+namespace Orleans.EventSourcing.Common\n+{\n+\n+    /// <summary>\n+    /// Helper class for tracking notifications that a grain sends to other clusters after updating the log.\n+    /// </summary>\n+    internal class NotificationTracker\n+    {\n+        internal IProtocolServices services;\n+        internal IConnectionIssueListener listener;\n+        internal int maxNotificationBatchSize;\n+\n+        private Dictionary<string, NotificationWorker> sendWorkers;\n+\n+        public NotificationTracker(IProtocolServices services, IEnumerable<string> remoteInstances, int maxNotificationBatchSize, IConnectionIssueListener listener)\n+        {\n+            this.services = services;\n+            this.listener = listener;\n+            sendWorkers = new Dictionary<string, NotificationWorker>();\n+            this.maxNotificationBatchSize = maxNotificationBatchSize;\n+\n+            foreach (var x in remoteInstances)\n+            {\n+                services.Verbose(\"Now sending notifications to {0}\", x);\n+                sendWorkers.Add(x, new NotificationWorker(this, x));\n+            }\n+        }\n+\n+        public void BroadcastNotification(INotificationMessage msg, string exclude = null)\n+        {\n+            foreach (var kvp in sendWorkers)\n+            {\n+                if (kvp.Key != exclude)\n+                {\n+                    var w = kvp.Value;\n+                    w.Enqueue(msg);\n+                }\n+            }\n+        }\n+\n+        /// <summary>\n+        /// returns unresolved connection issues observed by the workers\n+        /// </summary>\n+        public IEnumerable<ConnectionIssue> UnresolvedConnectionIssues\n+        {\n+            get\n+            {\n+                return sendWorkers.Values.Select(sw => sw.LastConnectionIssue.Issue).Where(i => i != null);\n+            }\n+        }\n+\n+        /// <summary>\n+        /// Update the multicluster configuration (change who to send notifications to)\n+        /// </summary>\n+        public void UpdateNotificationTargets(IReadOnlyList<string> remoteInstances)\n+        {\n+            var removed = sendWorkers.Keys.Except(remoteInstances);\n+            foreach (var x in removed)\n+            {\n+                services.Verbose(\"No longer sending notifications to {0}\", x);\n+                sendWorkers[x].Done = true;\n+                sendWorkers.Remove(x);\n+            }\n+\n+            var added = remoteInstances.Except(sendWorkers.Keys);\n+            foreach (var x in added)\n+            {\n+                if (x != services.MyClusterId)\n+                {\n+                    services.Verbose(\"Now sending notifications to {0}\", x);\n+                    sendWorkers.Add(x, new NotificationWorker(this, x));\n+                }\n+            }\n+        }\n+\n+\n+        public enum NotificationQueueState\n+        {\n+            Empty,\n+            Single,\n+            Batch,\n+            VersionOnly\n+        }\n+\n+        /// <summary>\n+        /// Asynchronous batch worker that sends notfications to a particular cluster.\n+        /// </summary>\n+        public class NotificationWorker : BatchWorker\n+        {\n+            private NotificationTracker tracker;\n+            private string clusterId;\n+\n+            /// <summary>\n+            /// Queue messages\n+            /// </summary>\n+            public INotificationMessage QueuedMessage = null;\n+            /// <summary>\n+            /// Queue state\n+            /// </summary>\n+            public NotificationQueueState QueueState = NotificationQueueState.Empty;","path":"src/OrleansEventSourcing/Common/NotificationTracker.cs","position":107,"original_position":107,"commit_id":"7c9008a537c6cb888d3fa68adc31f4dc21368ac9","original_commit_id":"7c9008a537c6cb888d3fa68adc31f4dc21368ac9","user":{"login":"sebastianburckhardt","id":6415760,"avatar_url":"https://avatars.githubusercontent.com/u/6415760?v=3","gravatar_id":"","url":"https://api.github.com/users/sebastianburckhardt","html_url":"https://github.com/sebastianburckhardt","followers_url":"https://api.github.com/users/sebastianburckhardt/followers","following_url":"https://api.github.com/users/sebastianburckhardt/following{/other_user}","gists_url":"https://api.github.com/users/sebastianburckhardt/gists{/gist_id}","starred_url":"https://api.github.com/users/sebastianburckhardt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sebastianburckhardt/subscriptions","organizations_url":"https://api.github.com/users/sebastianburckhardt/orgs","repos_url":"https://api.github.com/users/sebastianburckhardt/repos","events_url":"https://api.github.com/users/sebastianburckhardt/events{/privacy}","received_events_url":"https://api.github.com/users/sebastianburckhardt/received_events","type":"User","site_admin":false},"body":"Yes, that's right. I'll make it private.","created_at":"2016-12-19T23:35:59Z","updated_at":"2016-12-19T23:35:59Z","html_url":"https://github.com/dotnet/orleans/pull/1854#discussion_r93143781","pull_request_url":"https://api.github.com/repos/dotnet/orleans/pulls/1854","_links":{"self":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/comments/93143781"},"html":{"href":"https://github.com/dotnet/orleans/pull/1854#discussion_r93143781"},"pull_request":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/1854"}}},"pull_request":{"url":"https://api.github.com/repos/dotnet/orleans/pulls/1854","id":74301135,"html_url":"https://github.com/dotnet/orleans/pull/1854","diff_url":"https://github.com/dotnet/orleans/pull/1854.diff","patch_url":"https://github.com/dotnet/orleans/pull/1854.patch","issue_url":"https://api.github.com/repos/dotnet/orleans/issues/1854","number":1854,"state":"open","locked":false,"title":"Log Consistency Providers","user":{"login":"sebastianburckhardt","id":6415760,"avatar_url":"https://avatars.githubusercontent.com/u/6415760?v=3","gravatar_id":"","url":"https://api.github.com/users/sebastianburckhardt","html_url":"https://github.com/sebastianburckhardt","followers_url":"https://api.github.com/users/sebastianburckhardt/followers","following_url":"https://api.github.com/users/sebastianburckhardt/following{/other_user}","gists_url":"https://api.github.com/users/sebastianburckhardt/gists{/gist_id}","starred_url":"https://api.github.com/users/sebastianburckhardt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sebastianburckhardt/subscriptions","organizations_url":"https://api.github.com/users/sebastianburckhardt/orgs","repos_url":"https://api.github.com/users/sebastianburckhardt/repos","events_url":"https://api.github.com/users/sebastianburckhardt/events{/privacy}","received_events_url":"https://api.github.com/users/sebastianburckhardt/received_events","type":"User","site_admin":false},"body":"An alternative provider API for persistent grains whose state is defined as a view of a log. This will be used for supporting event sourcing, and geo-replicated grains.\n","created_at":"2016-06-17T20:32:21Z","updated_at":"2016-12-19T23:35:59Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a770137eae6a9c831ec102f22625153a9fd4945e","assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/dotnet/orleans/milestones/14","html_url":"https://github.com/dotnet/orleans/milestone/14","labels_url":"https://api.github.com/repos/dotnet/orleans/milestones/14/labels","id":2017852,"number":14,"title":"1.4.0","description":"","creator":{"login":"sergeybykov","id":8248806,"avatar_url":"https://avatars.githubusercontent.com/u/8248806?v=3","gravatar_id":"","url":"https://api.github.com/users/sergeybykov","html_url":"https://github.com/sergeybykov","followers_url":"https://api.github.com/users/sergeybykov/followers","following_url":"https://api.github.com/users/sergeybykov/following{/other_user}","gists_url":"https://api.github.com/users/sergeybykov/gists{/gist_id}","starred_url":"https://api.github.com/users/sergeybykov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sergeybykov/subscriptions","organizations_url":"https://api.github.com/users/sergeybykov/orgs","repos_url":"https://api.github.com/users/sergeybykov/repos","events_url":"https://api.github.com/users/sergeybykov/events{/privacy}","received_events_url":"https://api.github.com/users/sergeybykov/received_events","type":"User","site_admin":false},"open_issues":33,"closed_issues":35,"state":"open","created_at":"2016-09-20T23:16:17Z","updated_at":"2016-12-19T20:32:16Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/dotnet/orleans/pulls/1854/commits","review_comments_url":"https://api.github.com/repos/dotnet/orleans/pulls/1854/comments","review_comment_url":"https://api.github.com/repos/dotnet/orleans/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dotnet/orleans/issues/1854/comments","statuses_url":"https://api.github.com/repos/dotnet/orleans/statuses/7c9008a537c6cb888d3fa68adc31f4dc21368ac9","head":{"label":"sebastianburckhardt:geo-logviewproviders","ref":"geo-logviewproviders","sha":"7c9008a537c6cb888d3fa68adc31f4dc21368ac9","user":{"login":"sebastianburckhardt","id":6415760,"avatar_url":"https://avatars.githubusercontent.com/u/6415760?v=3","gravatar_id":"","url":"https://api.github.com/users/sebastianburckhardt","html_url":"https://github.com/sebastianburckhardt","followers_url":"https://api.github.com/users/sebastianburckhardt/followers","following_url":"https://api.github.com/users/sebastianburckhardt/following{/other_user}","gists_url":"https://api.github.com/users/sebastianburckhardt/gists{/gist_id}","starred_url":"https://api.github.com/users/sebastianburckhardt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sebastianburckhardt/subscriptions","organizations_url":"https://api.github.com/users/sebastianburckhardt/orgs","repos_url":"https://api.github.com/users/sebastianburckhardt/repos","events_url":"https://api.github.com/users/sebastianburckhardt/events{/privacy}","received_events_url":"https://api.github.com/users/sebastianburckhardt/received_events","type":"User","site_admin":false},"repo":{"id":32952293,"name":"orleans","full_name":"sebastianburckhardt/orleans","owner":{"login":"sebastianburckhardt","id":6415760,"avatar_url":"https://avatars.githubusercontent.com/u/6415760?v=3","gravatar_id":"","url":"https://api.github.com/users/sebastianburckhardt","html_url":"https://github.com/sebastianburckhardt","followers_url":"https://api.github.com/users/sebastianburckhardt/followers","following_url":"https://api.github.com/users/sebastianburckhardt/following{/other_user}","gists_url":"https://api.github.com/users/sebastianburckhardt/gists{/gist_id}","starred_url":"https://api.github.com/users/sebastianburckhardt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sebastianburckhardt/subscriptions","organizations_url":"https://api.github.com/users/sebastianburckhardt/orgs","repos_url":"https://api.github.com/users/sebastianburckhardt/repos","events_url":"https://api.github.com/users/sebastianburckhardt/events{/privacy}","received_events_url":"https://api.github.com/users/sebastianburckhardt/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/sebastianburckhardt/orleans","description":"Orleans - Distributed Actor Model","fork":true,"url":"https://api.github.com/repos/sebastianburckhardt/orleans","forks_url":"https://api.github.com/repos/sebastianburckhardt/orleans/forks","keys_url":"https://api.github.com/repos/sebastianburckhardt/orleans/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sebastianburckhardt/orleans/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sebastianburckhardt/orleans/teams","hooks_url":"https://api.github.com/repos/sebastianburckhardt/orleans/hooks","issue_events_url":"https://api.github.com/repos/sebastianburckhardt/orleans/issues/events{/number}","events_url":"https://api.github.com/repos/sebastianburckhardt/orleans/events","assignees_url":"https://api.github.com/repos/sebastianburckhardt/orleans/assignees{/user}","branches_url":"https://api.github.com/repos/sebastianburckhardt/orleans/branches{/branch}","tags_url":"https://api.github.com/repos/sebastianburckhardt/orleans/tags","blobs_url":"https://api.github.com/repos/sebastianburckhardt/orleans/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sebastianburckhardt/orleans/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sebastianburckhardt/orleans/git/refs{/sha}","trees_url":"https://api.github.com/repos/sebastianburckhardt/orleans/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sebastianburckhardt/orleans/statuses/{sha}","languages_url":"https://api.github.com/repos/sebastianburckhardt/orleans/languages","stargazers_url":"https://api.github.com/repos/sebastianburckhardt/orleans/stargazers","contributors_url":"https://api.github.com/repos/sebastianburckhardt/orleans/contributors","subscribers_url":"https://api.github.com/repos/sebastianburckhardt/orleans/subscribers","subscription_url":"https://api.github.com/repos/sebastianburckhardt/orleans/subscription","commits_url":"https://api.github.com/repos/sebastianburckhardt/orleans/commits{/sha}","git_commits_url":"https://api.github.com/repos/sebastianburckhardt/orleans/git/commits{/sha}","comments_url":"https://api.github.com/repos/sebastianburckhardt/orleans/comments{/number}","issue_comment_url":"https://api.github.com/repos/sebastianburckhardt/orleans/issues/comments{/number}","contents_url":"https://api.github.com/repos/sebastianburckhardt/orleans/contents/{+path}","compare_url":"https://api.github.com/repos/sebastianburckhardt/orleans/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sebastianburckhardt/orleans/merges","archive_url":"https://api.github.com/repos/sebastianburckhardt/orleans/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sebastianburckhardt/orleans/downloads","issues_url":"https://api.github.com/repos/sebastianburckhardt/orleans/issues{/number}","pulls_url":"https://api.github.com/repos/sebastianburckhardt/orleans/pulls{/number}","milestones_url":"https://api.github.com/repos/sebastianburckhardt/orleans/milestones{/number}","notifications_url":"https://api.github.com/repos/sebastianburckhardt/orleans/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sebastianburckhardt/orleans/labels{/name}","releases_url":"https://api.github.com/repos/sebastianburckhardt/orleans/releases{/id}","deployments_url":"https://api.github.com/repos/sebastianburckhardt/orleans/deployments","created_at":"2015-03-26T21:15:24Z","updated_at":"2016-08-16T11:03:10Z","pushed_at":"2016-12-14T17:51:42Z","git_url":"git://github.com/sebastianburckhardt/orleans.git","ssh_url":"git@github.com:sebastianburckhardt/orleans.git","clone_url":"https://github.com/sebastianburckhardt/orleans.git","svn_url":"https://github.com/sebastianburckhardt/orleans","homepage":"http://dotnet.github.io/orleans","size":35899,"stargazers_count":4,"watchers_count":4,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":1,"forks":2,"open_issues":1,"watchers":4,"default_branch":"geo-orleans"}},"base":{"label":"dotnet:master","ref":"master","sha":"fbcdd24b403bcd58e803cf923e02c77726aaa779","user":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"repo":{"id":28232663,"name":"orleans","full_name":"dotnet/orleans","owner":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dotnet/orleans","description":"Orleans - Distributed Virtual Actor Model","fork":false,"url":"https://api.github.com/repos/dotnet/orleans","forks_url":"https://api.github.com/repos/dotnet/orleans/forks","keys_url":"https://api.github.com/repos/dotnet/orleans/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dotnet/orleans/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dotnet/orleans/teams","hooks_url":"https://api.github.com/repos/dotnet/orleans/hooks","issue_events_url":"https://api.github.com/repos/dotnet/orleans/issues/events{/number}","events_url":"https://api.github.com/repos/dotnet/orleans/events","assignees_url":"https://api.github.com/repos/dotnet/orleans/assignees{/user}","branches_url":"https://api.github.com/repos/dotnet/orleans/branches{/branch}","tags_url":"https://api.github.com/repos/dotnet/orleans/tags","blobs_url":"https://api.github.com/repos/dotnet/orleans/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dotnet/orleans/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dotnet/orleans/git/refs{/sha}","trees_url":"https://api.github.com/repos/dotnet/orleans/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dotnet/orleans/statuses/{sha}","languages_url":"https://api.github.com/repos/dotnet/orleans/languages","stargazers_url":"https://api.github.com/repos/dotnet/orleans/stargazers","contributors_url":"https://api.github.com/repos/dotnet/orleans/contributors","subscribers_url":"https://api.github.com/repos/dotnet/orleans/subscribers","subscription_url":"https://api.github.com/repos/dotnet/orleans/subscription","commits_url":"https://api.github.com/repos/dotnet/orleans/commits{/sha}","git_commits_url":"https://api.github.com/repos/dotnet/orleans/git/commits{/sha}","comments_url":"https://api.github.com/repos/dotnet/orleans/comments{/number}","issue_comment_url":"https://api.github.com/repos/dotnet/orleans/issues/comments{/number}","contents_url":"https://api.github.com/repos/dotnet/orleans/contents/{+path}","compare_url":"https://api.github.com/repos/dotnet/orleans/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dotnet/orleans/merges","archive_url":"https://api.github.com/repos/dotnet/orleans/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dotnet/orleans/downloads","issues_url":"https://api.github.com/repos/dotnet/orleans/issues{/number}","pulls_url":"https://api.github.com/repos/dotnet/orleans/pulls{/number}","milestones_url":"https://api.github.com/repos/dotnet/orleans/milestones{/number}","notifications_url":"https://api.github.com/repos/dotnet/orleans/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dotnet/orleans/labels{/name}","releases_url":"https://api.github.com/repos/dotnet/orleans/releases{/id}","deployments_url":"https://api.github.com/repos/dotnet/orleans/deployments","created_at":"2014-12-19T14:32:50Z","updated_at":"2016-12-19T17:12:15Z","pushed_at":"2016-12-19T20:31:40Z","git_url":"git://github.com/dotnet/orleans.git","ssh_url":"git@github.com:dotnet/orleans.git","clone_url":"https://github.com/dotnet/orleans.git","svn_url":"https://github.com/dotnet/orleans","homepage":"http://dotnet.github.io/orleans","size":33374,"stargazers_count":2439,"watchers_count":2439,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":607,"mirror_url":null,"open_issues_count":249,"forks":607,"open_issues":249,"watchers":2439,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/1854"},"html":{"href":"https://github.com/dotnet/orleans/pull/1854"},"issue":{"href":"https://api.github.com/repos/dotnet/orleans/issues/1854"},"comments":{"href":"https://api.github.com/repos/dotnet/orleans/issues/1854/comments"},"review_comments":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/1854/comments"},"review_comment":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/1854/commits"},"statuses":{"href":"https://api.github.com/repos/dotnet/orleans/statuses/7c9008a537c6cb888d3fa68adc31f4dc21368ac9"}}}}
{"actor":{"display_login":"sebastianburckhardt"}}