{"action":"created","comment":{"url":"https://api.github.com/repos/XiaoMi/linden/pulls/comments/100985279","pull_request_review_id":21702390,"id":100985279,"diff_hunk":"@@ -40,73 +45,93 @@\n \n public class ShardClient {\n   private static final Logger LOGGER = LoggerFactory.getLogger(ShardClient.class);\n-  private final Map<String, LindenService.ServiceIface> clients = new ConcurrentHashMap<>();\n   private final String zk;\n   private final String path;\n-  private final ZkClient zkClient;\n-  private final String host;\n-  private final int localPort;\n+  private final String localHostPort;\n   private boolean haslocalClient = false;\n-  private final Integer remoteShardId;\n+  private final Integer shardId;\n   private final LindenService.ServiceIface localClient;\n+\n+  private List<LindenService.ServiceIface> clients;\n   private Iterator<LindenService.ServiceIface> clientIter;\n-  private LindenService.ServiceIface[] clientArray;\n \n   public ShardClient(final ZkClient zkClient, final String zk, final String path,\n-                     LindenService.ServiceIface localClient, int localPort, int remoteShardId) {\n+                     final LindenService.ServiceIface localClient, final int localPort, final int shardId) {\n     this.zk = zk;\n     this.path = path;\n-    this.zkClient = zkClient;\n     this.localClient = localClient;\n-    this.localPort = localPort;\n-    this.remoteShardId = remoteShardId;\n+    this.shardId = shardId;\n+    this.localHostPort = String.format(\"%s:%s\", CommonUtils.getLocalHost(), localPort);\n \n     // if the path does not exist, create it.\n     // the path may be created later, so the listener will not work\n     // and the isAvailable will always be false.\n     if (!zkClient.exists(path)) {\n       zkClient.createPersistent(path, true);\n     }\n-    host = CommonUtils.getLocalHost();\n+\n     List<String> children = zkClient.getChildren(path);\n+    final Map<String, Map.Entry<String, LindenService.ServiceIface>> lindenClients = new ConcurrentHashMap<>();\n     zkClient.subscribeChildChanges(path, new LindenZKListener(path, children) {\n       @Override\n       public void onChildChange(String parent, List<String> children, List<String> newAdded, List<String> deleted) {\n-        buildClient(newAdded, deleted);\n+        for (String node : newAdded) {\n+          String fullPath = FilenameUtils.separatorsToUnix(FilenameUtils.concat(path, node));\n+          byte[] bytes = zkClient.readData(fullPath);\n+\n+          ServiceInstance instance = JSONObject.parseObject(new String(bytes), ServiceInstance.class);\n+          String hostPort = String.format(\"%s:%s\", instance.getServiceEndpoint().getHost(),\n+              instance.getServiceEndpoint().getPort());\n+          if (localHostPort.equals(hostPort)) {\n+            haslocalClient = true;\n+            lindenClients.put(node, new AbstractMap.SimpleEntry<>(hostPort, localClient));\n+            LOGGER.info(\"Linden local node {} {} joined shard {}.\", node, hostPort, shardId);\n+          } else {\n+            LindenService.ServiceIface client = Thrift.newIface(hostPort, LindenService.ServiceIface.class);\n+            lindenClients.put(node, new AbstractMap.SimpleEntry<>(hostPort, client));\n+            LOGGER.info(\"Linden node {} {} joined shard {}.\", node, hostPort, shardId);\n+          }\n+        }\n+        for (String node : deleted) {\n+          if (lindenClients.containsKey(node)) {\n+            String hostPort = lindenClients.get(node).getKey();\n+            lindenClients.remove(node);\n+            LOGGER.info(\"Linden node {} {} left shard {}.\", node, hostPort, shardId);\n+          }\n+        }\n+\n+        // ensure the new node overrides the old node.\n+        List<String> sortedNodes = new ArrayList<>();\n+        for (String node : lindenClients.keySet()) {\n+          sortedNodes.add(node);\n+        }\n+        Collections.sort(sortedNodes, Collections.reverseOrder());\n+\n+        Set<String> uniqueClients = new HashSet<>();\n+        for (String node : sortedNodes) {\n+          if (lindenClients.containsKey(node)) {\n+            String hostPort = lindenClients.get(node).getKey();\n+            if (uniqueClients.contains(hostPort)) {\n+              lindenClients.remove(node);\n+              LOGGER.warn(\"Linden node {} {} is duplicated in shard {}, removed!\", node, hostPort, shardId);\n+            } else {\n+              uniqueClients.add(hostPort);\n+            }\n+          }\n+        }\n+\n+        LOGGER.info(\"{} Linden node in shard {}.\", lindenClients.size(), shardId);\n+        clients = new ArrayList<>();","path":"linden-core/src/main/java/com/xiaomi/linden/cluster/ShardClient.java","position":null,"original_position":122,"commit_id":"f7df523c14cad9d101c58bbef8721ad23e77e630","original_commit_id":"c1557fe71eeb87b4dfc7e8a149e214df1734384e","user":{"login":"intergret","id":2054855,"avatar_url":"https://avatars.githubusercontent.com/u/2054855?v=3","gravatar_id":"","url":"https://api.github.com/users/intergret","html_url":"https://github.com/intergret","followers_url":"https://api.github.com/users/intergret/followers","following_url":"https://api.github.com/users/intergret/following{/other_user}","gists_url":"https://api.github.com/users/intergret/gists{/gist_id}","starred_url":"https://api.github.com/users/intergret/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/intergret/subscriptions","organizations_url":"https://api.github.com/users/intergret/orgs","repos_url":"https://api.github.com/users/intergret/repos","events_url":"https://api.github.com/users/intergret/events{/privacy}","received_events_url":"https://api.github.com/users/intergret/received_events","type":"User","site_admin":false},"body":"Done. Also volatile the clients field, make sure other threads will see and use the new reference as discuss here: http://stackoverflow.com/questions/1351223/thread-safe-setting-of-a-variable-java","created_at":"2017-02-14T09:02:29Z","updated_at":"2017-02-14T09:02:29Z","html_url":"https://github.com/XiaoMi/linden/pull/2#discussion_r100985279","pull_request_url":"https://api.github.com/repos/XiaoMi/linden/pulls/2","_links":{"self":{"href":"https://api.github.com/repos/XiaoMi/linden/pulls/comments/100985279"},"html":{"href":"https://github.com/XiaoMi/linden/pull/2#discussion_r100985279"},"pull_request":{"href":"https://api.github.com/repos/XiaoMi/linden/pulls/2"}}},"pull_request":{"url":"https://api.github.com/repos/XiaoMi/linden/pulls/2","id":105601563,"html_url":"https://github.com/XiaoMi/linden/pull/2","diff_url":"https://github.com/XiaoMi/linden/pull/2.diff","patch_url":"https://github.com/XiaoMi/linden/pull/2.patch","issue_url":"https://api.github.com/repos/XiaoMi/linden/issues/2","number":2,"state":"open","locked":false,"title":"remove duplicate linden client with same host and port","user":{"login":"intergret","id":2054855,"avatar_url":"https://avatars.githubusercontent.com/u/2054855?v=3","gravatar_id":"","url":"https://api.github.com/users/intergret","html_url":"https://github.com/intergret","followers_url":"https://api.github.com/users/intergret/followers","following_url":"https://api.github.com/users/intergret/following{/other_user}","gists_url":"https://api.github.com/users/intergret/gists{/gist_id}","starred_url":"https://api.github.com/users/intergret/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/intergret/subscriptions","organizations_url":"https://api.github.com/users/intergret/orgs","repos_url":"https://api.github.com/users/intergret/repos","events_url":"https://api.github.com/users/intergret/events{/privacy}","received_events_url":"https://api.github.com/users/intergret/received_events","type":"User","site_admin":false},"body":"linden client may announce to zk again when zk recovery. check linden client, remove duplicate ones, only keep the newest one.","created_at":"2017-02-10T10:25:51Z","updated_at":"2017-02-14T09:02:29Z","closed_at":null,"merged_at":null,"merge_commit_sha":"47f372d20e4f7e2763d532bfdb958c2dcc89be79","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/XiaoMi/linden/pulls/2/commits","review_comments_url":"https://api.github.com/repos/XiaoMi/linden/pulls/2/comments","review_comment_url":"https://api.github.com/repos/XiaoMi/linden/pulls/comments{/number}","comments_url":"https://api.github.com/repos/XiaoMi/linden/issues/2/comments","statuses_url":"https://api.github.com/repos/XiaoMi/linden/statuses/f7df523c14cad9d101c58bbef8721ad23e77e630","head":{"label":"intergret:master","ref":"master","sha":"f7df523c14cad9d101c58bbef8721ad23e77e630","user":{"login":"intergret","id":2054855,"avatar_url":"https://avatars.githubusercontent.com/u/2054855?v=3","gravatar_id":"","url":"https://api.github.com/users/intergret","html_url":"https://github.com/intergret","followers_url":"https://api.github.com/users/intergret/followers","following_url":"https://api.github.com/users/intergret/following{/other_user}","gists_url":"https://api.github.com/users/intergret/gists{/gist_id}","starred_url":"https://api.github.com/users/intergret/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/intergret/subscriptions","organizations_url":"https://api.github.com/users/intergret/orgs","repos_url":"https://api.github.com/users/intergret/repos","events_url":"https://api.github.com/users/intergret/events{/privacy}","received_events_url":"https://api.github.com/users/intergret/received_events","type":"User","site_admin":false},"repo":{"id":81552316,"name":"linden","full_name":"intergret/linden","owner":{"login":"intergret","id":2054855,"avatar_url":"https://avatars.githubusercontent.com/u/2054855?v=3","gravatar_id":"","url":"https://api.github.com/users/intergret","html_url":"https://github.com/intergret","followers_url":"https://api.github.com/users/intergret/followers","following_url":"https://api.github.com/users/intergret/following{/other_user}","gists_url":"https://api.github.com/users/intergret/gists{/gist_id}","starred_url":"https://api.github.com/users/intergret/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/intergret/subscriptions","organizations_url":"https://api.github.com/users/intergret/orgs","repos_url":"https://api.github.com/users/intergret/repos","events_url":"https://api.github.com/users/intergret/events{/privacy}","received_events_url":"https://api.github.com/users/intergret/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/intergret/linden","description":null,"fork":true,"url":"https://api.github.com/repos/intergret/linden","forks_url":"https://api.github.com/repos/intergret/linden/forks","keys_url":"https://api.github.com/repos/intergret/linden/keys{/key_id}","collaborators_url":"https://api.github.com/repos/intergret/linden/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/intergret/linden/teams","hooks_url":"https://api.github.com/repos/intergret/linden/hooks","issue_events_url":"https://api.github.com/repos/intergret/linden/issues/events{/number}","events_url":"https://api.github.com/repos/intergret/linden/events","assignees_url":"https://api.github.com/repos/intergret/linden/assignees{/user}","branches_url":"https://api.github.com/repos/intergret/linden/branches{/branch}","tags_url":"https://api.github.com/repos/intergret/linden/tags","blobs_url":"https://api.github.com/repos/intergret/linden/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/intergret/linden/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/intergret/linden/git/refs{/sha}","trees_url":"https://api.github.com/repos/intergret/linden/git/trees{/sha}","statuses_url":"https://api.github.com/repos/intergret/linden/statuses/{sha}","languages_url":"https://api.github.com/repos/intergret/linden/languages","stargazers_url":"https://api.github.com/repos/intergret/linden/stargazers","contributors_url":"https://api.github.com/repos/intergret/linden/contributors","subscribers_url":"https://api.github.com/repos/intergret/linden/subscribers","subscription_url":"https://api.github.com/repos/intergret/linden/subscription","commits_url":"https://api.github.com/repos/intergret/linden/commits{/sha}","git_commits_url":"https://api.github.com/repos/intergret/linden/git/commits{/sha}","comments_url":"https://api.github.com/repos/intergret/linden/comments{/number}","issue_comment_url":"https://api.github.com/repos/intergret/linden/issues/comments{/number}","contents_url":"https://api.github.com/repos/intergret/linden/contents/{+path}","compare_url":"https://api.github.com/repos/intergret/linden/compare/{base}...{head}","merges_url":"https://api.github.com/repos/intergret/linden/merges","archive_url":"https://api.github.com/repos/intergret/linden/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/intergret/linden/downloads","issues_url":"https://api.github.com/repos/intergret/linden/issues{/number}","pulls_url":"https://api.github.com/repos/intergret/linden/pulls{/number}","milestones_url":"https://api.github.com/repos/intergret/linden/milestones{/number}","notifications_url":"https://api.github.com/repos/intergret/linden/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/intergret/linden/labels{/name}","releases_url":"https://api.github.com/repos/intergret/linden/releases{/id}","deployments_url":"https://api.github.com/repos/intergret/linden/deployments","created_at":"2017-02-10T10:02:01Z","updated_at":"2017-02-10T10:02:04Z","pushed_at":"2017-02-14T08:52:16Z","git_url":"git://github.com/intergret/linden.git","ssh_url":"git@github.com:intergret/linden.git","clone_url":"https://github.com/intergret/linden.git","svn_url":"https://github.com/intergret/linden","homepage":null,"size":3586,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"XiaoMi:master","ref":"master","sha":"f868bc369f03a50488743d0a4f32ad1e373f9334","user":{"login":"XiaoMi","id":1309360,"avatar_url":"https://avatars.githubusercontent.com/u/1309360?v=3","gravatar_id":"","url":"https://api.github.com/users/XiaoMi","html_url":"https://github.com/XiaoMi","followers_url":"https://api.github.com/users/XiaoMi/followers","following_url":"https://api.github.com/users/XiaoMi/following{/other_user}","gists_url":"https://api.github.com/users/XiaoMi/gists{/gist_id}","starred_url":"https://api.github.com/users/XiaoMi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/XiaoMi/subscriptions","organizations_url":"https://api.github.com/users/XiaoMi/orgs","repos_url":"https://api.github.com/users/XiaoMi/repos","events_url":"https://api.github.com/users/XiaoMi/events{/privacy}","received_events_url":"https://api.github.com/users/XiaoMi/received_events","type":"Organization","site_admin":false},"repo":{"id":76436183,"name":"linden","full_name":"XiaoMi/linden","owner":{"login":"XiaoMi","id":1309360,"avatar_url":"https://avatars.githubusercontent.com/u/1309360?v=3","gravatar_id":"","url":"https://api.github.com/users/XiaoMi","html_url":"https://github.com/XiaoMi","followers_url":"https://api.github.com/users/XiaoMi/followers","following_url":"https://api.github.com/users/XiaoMi/following{/other_user}","gists_url":"https://api.github.com/users/XiaoMi/gists{/gist_id}","starred_url":"https://api.github.com/users/XiaoMi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/XiaoMi/subscriptions","organizations_url":"https://api.github.com/users/XiaoMi/orgs","repos_url":"https://api.github.com/users/XiaoMi/repos","events_url":"https://api.github.com/users/XiaoMi/events{/privacy}","received_events_url":"https://api.github.com/users/XiaoMi/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/XiaoMi/linden","description":null,"fork":false,"url":"https://api.github.com/repos/XiaoMi/linden","forks_url":"https://api.github.com/repos/XiaoMi/linden/forks","keys_url":"https://api.github.com/repos/XiaoMi/linden/keys{/key_id}","collaborators_url":"https://api.github.com/repos/XiaoMi/linden/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/XiaoMi/linden/teams","hooks_url":"https://api.github.com/repos/XiaoMi/linden/hooks","issue_events_url":"https://api.github.com/repos/XiaoMi/linden/issues/events{/number}","events_url":"https://api.github.com/repos/XiaoMi/linden/events","assignees_url":"https://api.github.com/repos/XiaoMi/linden/assignees{/user}","branches_url":"https://api.github.com/repos/XiaoMi/linden/branches{/branch}","tags_url":"https://api.github.com/repos/XiaoMi/linden/tags","blobs_url":"https://api.github.com/repos/XiaoMi/linden/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/XiaoMi/linden/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/XiaoMi/linden/git/refs{/sha}","trees_url":"https://api.github.com/repos/XiaoMi/linden/git/trees{/sha}","statuses_url":"https://api.github.com/repos/XiaoMi/linden/statuses/{sha}","languages_url":"https://api.github.com/repos/XiaoMi/linden/languages","stargazers_url":"https://api.github.com/repos/XiaoMi/linden/stargazers","contributors_url":"https://api.github.com/repos/XiaoMi/linden/contributors","subscribers_url":"https://api.github.com/repos/XiaoMi/linden/subscribers","subscription_url":"https://api.github.com/repos/XiaoMi/linden/subscription","commits_url":"https://api.github.com/repos/XiaoMi/linden/commits{/sha}","git_commits_url":"https://api.github.com/repos/XiaoMi/linden/git/commits{/sha}","comments_url":"https://api.github.com/repos/XiaoMi/linden/comments{/number}","issue_comment_url":"https://api.github.com/repos/XiaoMi/linden/issues/comments{/number}","contents_url":"https://api.github.com/repos/XiaoMi/linden/contents/{+path}","compare_url":"https://api.github.com/repos/XiaoMi/linden/compare/{base}...{head}","merges_url":"https://api.github.com/repos/XiaoMi/linden/merges","archive_url":"https://api.github.com/repos/XiaoMi/linden/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/XiaoMi/linden/downloads","issues_url":"https://api.github.com/repos/XiaoMi/linden/issues{/number}","pulls_url":"https://api.github.com/repos/XiaoMi/linden/pulls{/number}","milestones_url":"https://api.github.com/repos/XiaoMi/linden/milestones{/number}","notifications_url":"https://api.github.com/repos/XiaoMi/linden/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/XiaoMi/linden/labels{/name}","releases_url":"https://api.github.com/repos/XiaoMi/linden/releases{/id}","deployments_url":"https://api.github.com/repos/XiaoMi/linden/deployments","created_at":"2016-12-14T07:38:38Z","updated_at":"2017-02-13T11:55:40Z","pushed_at":"2017-02-14T08:52:17Z","git_url":"git://github.com/XiaoMi/linden.git","ssh_url":"git@github.com:XiaoMi/linden.git","clone_url":"https://github.com/XiaoMi/linden.git","svn_url":"https://github.com/XiaoMi/linden","homepage":null,"size":3575,"stargazers_count":110,"watchers_count":110,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":29,"mirror_url":null,"open_issues_count":1,"forks":29,"open_issues":1,"watchers":110,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/XiaoMi/linden/pulls/2"},"html":{"href":"https://github.com/XiaoMi/linden/pull/2"},"issue":{"href":"https://api.github.com/repos/XiaoMi/linden/issues/2"},"comments":{"href":"https://api.github.com/repos/XiaoMi/linden/issues/2/comments"},"review_comments":{"href":"https://api.github.com/repos/XiaoMi/linden/pulls/2/comments"},"review_comment":{"href":"https://api.github.com/repos/XiaoMi/linden/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/XiaoMi/linden/pulls/2/commits"},"statuses":{"href":"https://api.github.com/repos/XiaoMi/linden/statuses/f7df523c14cad9d101c58bbef8721ad23e77e630"}}}}
{"actor":{"display_login":"intergret"}}