{"action":"created","comment":{"url":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments/210238397","pull_request_review_id":146405129,"id":210238397,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxMDIzODM5Nw==","diff_hunk":"@@ -0,0 +1,203 @@\n+/*\n+ * Licensed to Elasticsearch under one or more contributor\n+ * license agreements. See the NOTICE file distributed with\n+ * this work for additional information regarding copyright\n+ * ownership. Elasticsearch licenses this file to you under\n+ * the Apache License, Version 2.0 (the \"License\"); you may\n+ * not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ *    http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing,\n+ * software distributed under the License is distributed on an\n+ * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n+ * KIND, either express or implied.  See the License for the\n+ * specific language governing permissions and limitations\n+ * under the License.\n+ */\n+\n+package org.elasticsearch.cluster.coordination;\n+\n+import org.elasticsearch.common.SuppressForbidden;\n+import org.elasticsearch.common.component.AbstractComponent;\n+import org.elasticsearch.common.lease.Releasable;\n+import org.elasticsearch.common.settings.Setting;\n+import org.elasticsearch.common.settings.Setting.Property;\n+import org.elasticsearch.common.settings.Settings;\n+import org.elasticsearch.common.unit.TimeValue;\n+import org.elasticsearch.common.util.concurrent.AbstractRunnable;\n+import org.elasticsearch.threadpool.ThreadPool;\n+import org.elasticsearch.threadpool.ThreadPool.Names;\n+\n+import java.util.Random;\n+import java.util.concurrent.atomic.AtomicBoolean;\n+import java.util.concurrent.atomic.AtomicLong;\n+import java.util.function.LongSupplier;\n+\n+/**\n+ * It's provably impossible to guarantee that any leader election algorithm ever elects a leader, but they generally work (with probability\n+ * that approaches 1 over time) as long as elections occur sufficiently infrequently, compared to the time it takes to send a message to\n+ * another node and receive a response back. We do not know the round-trip latency here, but we can approximate it by attempting elections\n+ * randomly at reasonably high frequency and backing off (linearly) until one of them succeeds. We also place an upper bound on the backoff\n+ * so that if elections are failing due to a network partition that lasts for a long time then when the partition heals there is an election\n+ * attempt reasonably quickly.\n+ */\n+public class ElectionSchedulerFactory extends AbstractComponent {\n+\n+    private static final String ELECTION_INITIAL_TIMEOUT_SETTING_KEY = \"cluster.election.initial_timeout\";\n+    private static final String ELECTION_BACK_OFF_TIME_SETTING_KEY = \"cluster.election.back_off_time\";\n+    private static final String ELECTION_MAX_TIMEOUT_SETTING_KEY = \"cluster.election.max_timeout\";\n+\n+    /*\n+     * The first election is scheduled to occur a random number of milliseconds after the scheduler is started, where the random number of\n+     * milliseconds is chosen uniformly from\n+     *\n+     *     (0, min(ELECTION_INITIAL_TIMEOUT_SETTING, ELECTION_MAX_TIMEOUT_SETTING_KEY)]\n+     *\n+     * For `n > 1`, the `n`th election is scheduled to occur a random number of milliseconds after the `n - 1`th election, where the random\n+     * number of milliseconds is chosen uniformly from\n+     *\n+     *     (0, min(ELECTION_INITIAL_TIMEOUT_SETTING + (n-1) * ELECTION_BACK_OFF_TIME_SETTING, ELECTION_MAX_TIMEOUT_SETTING_KEY)]\n+     */\n+\n+    public static final Setting<TimeValue> ELECTION_INITIAL_TIMEOUT_SETTING = Setting.timeSetting(ELECTION_INITIAL_TIMEOUT_SETTING_KEY,\n+        TimeValue.timeValueMillis(100), TimeValue.timeValueMillis(1), Property.NodeScope);\n+\n+    public static final Setting<TimeValue> ELECTION_BACK_OFF_TIME_SETTING = Setting.timeSetting(ELECTION_BACK_OFF_TIME_SETTING_KEY,\n+        TimeValue.timeValueMillis(100), TimeValue.timeValueMillis(1), Property.NodeScope);\n+\n+    public static final Setting<TimeValue> ELECTION_MAX_TIMEOUT_SETTING = Setting.timeSetting(ELECTION_MAX_TIMEOUT_SETTING_KEY,\n+        TimeValue.timeValueSeconds(10), TimeValue.timeValueMillis(200), Property.NodeScope);\n+\n+    private final TimeValue initialTimeout;\n+    private final TimeValue backoffTime;\n+    private final TimeValue maxTimeout;\n+    private final ThreadPool threadPool;\n+    private final Random random;\n+\n+    public ElectionSchedulerFactory(Settings settings, Random random, ThreadPool threadPool) {\n+        super(settings);\n+\n+        this.random = random;\n+        this.threadPool = threadPool;\n+\n+        initialTimeout = ELECTION_INITIAL_TIMEOUT_SETTING.get(settings);\n+        backoffTime = ELECTION_BACK_OFF_TIME_SETTING.get(settings);\n+        maxTimeout = ELECTION_MAX_TIMEOUT_SETTING.get(settings);\n+    }\n+\n+    /**\n+     * Start the process to schedule repeated election attempts.\n+     *\n+     * @param gracePeriod       An initial period to wait before attempting the first election.\n+     * @param scheduledRunnable The action to run each time an election should be attempted.\n+     */\n+    public Releasable startElectionScheduler(TimeValue gracePeriod, Runnable scheduledRunnable) {\n+        final ElectionScheduler currentScheduler = new ElectionScheduler();\n+        currentScheduler.scheduleNextElection(gracePeriod, scheduledRunnable);\n+        return currentScheduler;\n+    }\n+\n+    @SuppressForbidden(reason = \"Argument to Math.abs() is definitely not Long.MIN_VALUE\")\n+    private static long nonNegative(long n) {\n+        return n == Long.MIN_VALUE ? 0 : Math.abs(n);\n+    }\n+\n+    /**\n+     * @param randomSupplier supplier of randomly-chosen longs\n+     * @param upperBound     exclusive upper bound\n+     */\n+    // package-private for testing\n+    static long randomPositiveLongLessThan(LongSupplier randomSupplier, long upperBound) {\n+        assert 1 < upperBound : upperBound;\n+        return nonNegative(randomSupplier.getAsLong()) % (upperBound - 1) + 1;\n+    }\n+\n+    @Override\n+    public String toString() {\n+        return \"ElectionSchedulerFactory{\" +\n+            \"initialTimeout=\" + initialTimeout +\n+            \", backoffTime=\" + backoffTime +\n+            \", maxTimeout=\" + maxTimeout +\n+            '}';\n+    }\n+\n+    private class ElectionScheduler implements Releasable {\n+        /**\n+         * The current maximum timeout: the next election is scheduled randomly no later than this number of milliseconds in the future. On\n+         * each election attempt this value is increased by `backoffTime`, up to the `maxTimeout`, to adapt to higher-than-expected latency.\n+         */\n+        private final AtomicLong currentMaxTimeoutMillis = new AtomicLong(Math.min(initialTimeout.millis(), maxTimeout.millis()));\n+        private final AtomicBoolean isClosed = new AtomicBoolean();\n+\n+        /**\n+         * Calculate the next maximum timeout by backing off the current value by `backoffTime` up to the `maxTimeout`.\n+         */\n+        private long backOffCurrentMaxTimeout(long currentMaxTimeoutMillis) {\n+            return Math.min(maxTimeout.getMillis(), currentMaxTimeoutMillis + backoffTime.getMillis());\n+        }\n+\n+        void scheduleNextElection(final TimeValue gracePeriod, final Runnable scheduledRunnable) {\n+            if (isClosed.get()) {\n+                logger.debug(\"{} not scheduling election\", this);\n+                return;\n+            }\n+\n+            final long maxDelayMillis = currentMaxTimeoutMillis.getAndUpdate(this::backOffCurrentMaxTimeout);\n+            final long delayMillis = randomPositiveLongLessThan(random::nextLong, maxDelayMillis + 1) + gracePeriod.millis();\n+            final Runnable runnable = new AbstractRunnable() {\n+                @Override\n+                public void onFailure(Exception e) {\n+                    logger.debug(\"unexpected exception in wakeup\", e);\n+                    assert false : e;\n+                }\n+\n+                @Override\n+                protected void doRun() {\n+                    if (isClosed.get()) {\n+                        logger.debug(\"{} not starting election\", this);\n+                        return;\n+                    }\n+                    logger.debug(\"{} starting election\", this);\n+                    scheduledRunnable.run();\n+                }\n+\n+                @Override\n+                public void onAfter() {\n+                    scheduleNextElection(TimeValue.ZERO, scheduledRunnable);\n+                }\n+\n+                @Override\n+                public String toString() {\n+                    return \"scheduleNextElection{gracePeriod=\" + gracePeriod\n+                        + \", maxDelayMillis=\" + maxDelayMillis\n+                        + \", delayMillis=\" + delayMillis\n+                        + \", \" + ElectionScheduler.this + \"}\";\n+                }\n+\n+                @Override\n+                public boolean isForceExecution() {\n+                    // There are very few of these scheduled, and they back off, but it's important that they're not rejected as\n+                    // this could prevent a cluster from ever forming.\n+                    return true;\n+                }\n+            };\n+\n+            logger.debug(\"scheduling {}\", runnable);\n+            threadPool.schedule(TimeValue.timeValueMillis(delayMillis), Names.GENERIC, runnable);\n+        }\n+\n+        @Override\n+        public String toString() {\n+            return \"ElectionScheduler{currentMaxTimeoutMillis=\" + currentMaxTimeoutMillis\n+                + \", \" + ElectionSchedulerFactory.this + \"}\";\n+        }\n+\n+        @Override\n+        public void close() {\n+            boolean isClosedChanged = isClosed.compareAndSet(false, true);","path":"server/src/main/java/org/elasticsearch/cluster/coordination/ElectionSchedulerFactory.java","position":199,"original_position":199,"commit_id":"cd0d5270e30e583c51599b880ee4e8411ae19ad1","original_commit_id":"cd0d5270e30e583c51599b880ee4e8411ae19ad1","user":{"login":"ywelsch","id":3718355,"node_id":"MDQ6VXNlcjM3MTgzNTU=","avatar_url":"https://avatars3.githubusercontent.com/u/3718355?v=4","gravatar_id":"","url":"https://api.github.com/users/ywelsch","html_url":"https://github.com/ywelsch","followers_url":"https://api.github.com/users/ywelsch/followers","following_url":"https://api.github.com/users/ywelsch/following{/other_user}","gists_url":"https://api.github.com/users/ywelsch/gists{/gist_id}","starred_url":"https://api.github.com/users/ywelsch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ywelsch/subscriptions","organizations_url":"https://api.github.com/users/ywelsch/orgs","repos_url":"https://api.github.com/users/ywelsch/repos","events_url":"https://api.github.com/users/ywelsch/events{/privacy}","received_events_url":"https://api.github.com/users/ywelsch/received_events","type":"User","site_admin":false},"body":"maybe call this `previouslyNotClosed`","created_at":"2018-08-15T11:17:23Z","updated_at":"2018-08-15T11:34:08Z","html_url":"https://github.com/elastic/elasticsearch/pull/32846#discussion_r210238397","pull_request_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/32846","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments/210238397"},"html":{"href":"https://github.com/elastic/elasticsearch/pull/32846#discussion_r210238397"},"pull_request":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/32846"}}},"pull_request":{"url":"https://api.github.com/repos/elastic/elasticsearch/pulls/32846","id":208297345,"node_id":"MDExOlB1bGxSZXF1ZXN0MjA4Mjk3MzQ1","html_url":"https://github.com/elastic/elasticsearch/pull/32846","diff_url":"https://github.com/elastic/elasticsearch/pull/32846.diff","patch_url":"https://github.com/elastic/elasticsearch/pull/32846.patch","issue_url":"https://api.github.com/repos/elastic/elasticsearch/issues/32846","number":32846,"state":"open","locked":false,"title":"[Zen2] Introduce ElectionScheduler","user":{"login":"DaveCTurner","id":5058284,"node_id":"MDQ6VXNlcjUwNTgyODQ=","avatar_url":"https://avatars3.githubusercontent.com/u/5058284?v=4","gravatar_id":"","url":"https://api.github.com/users/DaveCTurner","html_url":"https://github.com/DaveCTurner","followers_url":"https://api.github.com/users/DaveCTurner/followers","following_url":"https://api.github.com/users/DaveCTurner/following{/other_user}","gists_url":"https://api.github.com/users/DaveCTurner/gists{/gist_id}","starred_url":"https://api.github.com/users/DaveCTurner/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DaveCTurner/subscriptions","organizations_url":"https://api.github.com/users/DaveCTurner/orgs","repos_url":"https://api.github.com/users/DaveCTurner/repos","events_url":"https://api.github.com/users/DaveCTurner/events{/privacy}","received_events_url":"https://api.github.com/users/DaveCTurner/received_events","type":"User","site_admin":false},"body":"The ElectionScheduler runs while there is no known elected master and is\r\nresponsible for scheduling elections randomly, backing off on failure, to\r\nbalance the desire to elect a master quickly with the desire to avoid more than\r\none node starting an election at once.","created_at":"2018-08-14T14:17:08Z","updated_at":"2018-08-15T11:34:08Z","closed_at":null,"merged_at":null,"merge_commit_sha":"db3af4424ee7cf3acb53cb6021153f20d1e2d67b","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":881394071,"node_id":"MDU6TGFiZWw4ODEzOTQwNzE=","url":"https://api.github.com/repos/elastic/elasticsearch/labels/:Distributed/ZenDiscovery","name":":Distributed/ZenDiscovery","color":"0e8a16","default":false},{"id":23174,"node_id":"MDU6TGFiZWwyMzE3NA==","url":"https://api.github.com/repos/elastic/elasticsearch/labels/%3Eenhancement","name":">enhancement","color":"4a4ea8","default":false},{"id":484855464,"node_id":"MDU6TGFiZWw0ODQ4NTU0NjQ=","url":"https://api.github.com/repos/elastic/elasticsearch/labels/v7.0.0","name":"v7.0.0","color":"dddddd","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/32846/commits","review_comments_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/32846/comments","review_comment_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments{/number}","comments_url":"https://api.github.com/repos/elastic/elasticsearch/issues/32846/comments","statuses_url":"https://api.github.com/repos/elastic/elasticsearch/statuses/cd0d5270e30e583c51599b880ee4e8411ae19ad1","head":{"label":"DaveCTurner:2018-08-14-election-scheduler","ref":"2018-08-14-election-scheduler","sha":"cd0d5270e30e583c51599b880ee4e8411ae19ad1","user":{"login":"DaveCTurner","id":5058284,"node_id":"MDQ6VXNlcjUwNTgyODQ=","avatar_url":"https://avatars3.githubusercontent.com/u/5058284?v=4","gravatar_id":"","url":"https://api.github.com/users/DaveCTurner","html_url":"https://github.com/DaveCTurner","followers_url":"https://api.github.com/users/DaveCTurner/followers","following_url":"https://api.github.com/users/DaveCTurner/following{/other_user}","gists_url":"https://api.github.com/users/DaveCTurner/gists{/gist_id}","starred_url":"https://api.github.com/users/DaveCTurner/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DaveCTurner/subscriptions","organizations_url":"https://api.github.com/users/DaveCTurner/orgs","repos_url":"https://api.github.com/users/DaveCTurner/repos","events_url":"https://api.github.com/users/DaveCTurner/events{/privacy}","received_events_url":"https://api.github.com/users/DaveCTurner/received_events","type":"User","site_admin":false},"repo":{"id":104302209,"node_id":"MDEwOlJlcG9zaXRvcnkxMDQzMDIyMDk=","name":"elasticsearch","full_name":"DaveCTurner/elasticsearch","owner":{"login":"DaveCTurner","id":5058284,"node_id":"MDQ6VXNlcjUwNTgyODQ=","avatar_url":"https://avatars3.githubusercontent.com/u/5058284?v=4","gravatar_id":"","url":"https://api.github.com/users/DaveCTurner","html_url":"https://github.com/DaveCTurner","followers_url":"https://api.github.com/users/DaveCTurner/followers","following_url":"https://api.github.com/users/DaveCTurner/following{/other_user}","gists_url":"https://api.github.com/users/DaveCTurner/gists{/gist_id}","starred_url":"https://api.github.com/users/DaveCTurner/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DaveCTurner/subscriptions","organizations_url":"https://api.github.com/users/DaveCTurner/orgs","repos_url":"https://api.github.com/users/DaveCTurner/repos","events_url":"https://api.github.com/users/DaveCTurner/events{/privacy}","received_events_url":"https://api.github.com/users/DaveCTurner/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/DaveCTurner/elasticsearch","description":"Open Source, Distributed, RESTful Search Engine","fork":true,"url":"https://api.github.com/repos/DaveCTurner/elasticsearch","forks_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/forks","keys_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/keys{/key_id}","collaborators_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/teams","hooks_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/hooks","issue_events_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/issues/events{/number}","events_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/events","assignees_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/assignees{/user}","branches_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/branches{/branch}","tags_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/tags","blobs_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/git/refs{/sha}","trees_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/git/trees{/sha}","statuses_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/statuses/{sha}","languages_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/languages","stargazers_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/stargazers","contributors_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/contributors","subscribers_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/subscribers","subscription_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/subscription","commits_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/commits{/sha}","git_commits_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/git/commits{/sha}","comments_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/comments{/number}","issue_comment_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/issues/comments{/number}","contents_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/contents/{+path}","compare_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/compare/{base}...{head}","merges_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/merges","archive_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/downloads","issues_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/issues{/number}","pulls_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/pulls{/number}","milestones_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/milestones{/number}","notifications_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/labels{/name}","releases_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/releases{/id}","deployments_url":"https://api.github.com/repos/DaveCTurner/elasticsearch/deployments","created_at":"2017-09-21T04:38:01Z","updated_at":"2018-08-14T07:08:22Z","pushed_at":"2018-08-15T10:27:14Z","git_url":"git://github.com/DaveCTurner/elasticsearch.git","ssh_url":"git@github.com:DaveCTurner/elasticsearch.git","clone_url":"https://github.com/DaveCTurner/elasticsearch.git","svn_url":"https://github.com/DaveCTurner/elasticsearch","homepage":"https://www.elastic.co/products/elasticsearch","size":495378,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"elastic:zen2","ref":"zen2","sha":"e122505a9131a9fb23a7923927911bf3058e7636","user":{"login":"elastic","id":6764390,"node_id":"MDEyOk9yZ2FuaXphdGlvbjY3NjQzOTA=","avatar_url":"https://avatars0.githubusercontent.com/u/6764390?v=4","gravatar_id":"","url":"https://api.github.com/users/elastic","html_url":"https://github.com/elastic","followers_url":"https://api.github.com/users/elastic/followers","following_url":"https://api.github.com/users/elastic/following{/other_user}","gists_url":"https://api.github.com/users/elastic/gists{/gist_id}","starred_url":"https://api.github.com/users/elastic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elastic/subscriptions","organizations_url":"https://api.github.com/users/elastic/orgs","repos_url":"https://api.github.com/users/elastic/repos","events_url":"https://api.github.com/users/elastic/events{/privacy}","received_events_url":"https://api.github.com/users/elastic/received_events","type":"Organization","site_admin":false},"repo":{"id":507775,"node_id":"MDEwOlJlcG9zaXRvcnk1MDc3NzU=","name":"elasticsearch","full_name":"elastic/elasticsearch","owner":{"login":"elastic","id":6764390,"node_id":"MDEyOk9yZ2FuaXphdGlvbjY3NjQzOTA=","avatar_url":"https://avatars0.githubusercontent.com/u/6764390?v=4","gravatar_id":"","url":"https://api.github.com/users/elastic","html_url":"https://github.com/elastic","followers_url":"https://api.github.com/users/elastic/followers","following_url":"https://api.github.com/users/elastic/following{/other_user}","gists_url":"https://api.github.com/users/elastic/gists{/gist_id}","starred_url":"https://api.github.com/users/elastic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elastic/subscriptions","organizations_url":"https://api.github.com/users/elastic/orgs","repos_url":"https://api.github.com/users/elastic/repos","events_url":"https://api.github.com/users/elastic/events{/privacy}","received_events_url":"https://api.github.com/users/elastic/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/elastic/elasticsearch","description":"Open Source, Distributed, RESTful Search Engine","fork":false,"url":"https://api.github.com/repos/elastic/elasticsearch","forks_url":"https://api.github.com/repos/elastic/elasticsearch/forks","keys_url":"https://api.github.com/repos/elastic/elasticsearch/keys{/key_id}","collaborators_url":"https://api.github.com/repos/elastic/elasticsearch/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/elastic/elasticsearch/teams","hooks_url":"https://api.github.com/repos/elastic/elasticsearch/hooks","issue_events_url":"https://api.github.com/repos/elastic/elasticsearch/issues/events{/number}","events_url":"https://api.github.com/repos/elastic/elasticsearch/events","assignees_url":"https://api.github.com/repos/elastic/elasticsearch/assignees{/user}","branches_url":"https://api.github.com/repos/elastic/elasticsearch/branches{/branch}","tags_url":"https://api.github.com/repos/elastic/elasticsearch/tags","blobs_url":"https://api.github.com/repos/elastic/elasticsearch/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/elastic/elasticsearch/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/elastic/elasticsearch/git/refs{/sha}","trees_url":"https://api.github.com/repos/elastic/elasticsearch/git/trees{/sha}","statuses_url":"https://api.github.com/repos/elastic/elasticsearch/statuses/{sha}","languages_url":"https://api.github.com/repos/elastic/elasticsearch/languages","stargazers_url":"https://api.github.com/repos/elastic/elasticsearch/stargazers","contributors_url":"https://api.github.com/repos/elastic/elasticsearch/contributors","subscribers_url":"https://api.github.com/repos/elastic/elasticsearch/subscribers","subscription_url":"https://api.github.com/repos/elastic/elasticsearch/subscription","commits_url":"https://api.github.com/repos/elastic/elasticsearch/commits{/sha}","git_commits_url":"https://api.github.com/repos/elastic/elasticsearch/git/commits{/sha}","comments_url":"https://api.github.com/repos/elastic/elasticsearch/comments{/number}","issue_comment_url":"https://api.github.com/repos/elastic/elasticsearch/issues/comments{/number}","contents_url":"https://api.github.com/repos/elastic/elasticsearch/contents/{+path}","compare_url":"https://api.github.com/repos/elastic/elasticsearch/compare/{base}...{head}","merges_url":"https://api.github.com/repos/elastic/elasticsearch/merges","archive_url":"https://api.github.com/repos/elastic/elasticsearch/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/elastic/elasticsearch/downloads","issues_url":"https://api.github.com/repos/elastic/elasticsearch/issues{/number}","pulls_url":"https://api.github.com/repos/elastic/elasticsearch/pulls{/number}","milestones_url":"https://api.github.com/repos/elastic/elasticsearch/milestones{/number}","notifications_url":"https://api.github.com/repos/elastic/elasticsearch/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/elastic/elasticsearch/labels{/name}","releases_url":"https://api.github.com/repos/elastic/elasticsearch/releases{/id}","deployments_url":"https://api.github.com/repos/elastic/elasticsearch/deployments","created_at":"2010-02-08T13:20:56Z","updated_at":"2018-08-15T10:21:53Z","pushed_at":"2018-08-15T10:48:30Z","git_url":"git://github.com/elastic/elasticsearch.git","ssh_url":"git@github.com:elastic/elasticsearch.git","clone_url":"https://github.com/elastic/elasticsearch.git","svn_url":"https://github.com/elastic/elasticsearch","homepage":"https://www.elastic.co/products/elasticsearch","size":502379,"stargazers_count":33495,"watchers_count":33495,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":11392,"mirror_url":null,"archived":false,"open_issues_count":1618,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":11392,"open_issues":1618,"watchers":33495,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/32846"},"html":{"href":"https://github.com/elastic/elasticsearch/pull/32846"},"issue":{"href":"https://api.github.com/repos/elastic/elasticsearch/issues/32846"},"comments":{"href":"https://api.github.com/repos/elastic/elasticsearch/issues/32846/comments"},"review_comments":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/32846/comments"},"review_comment":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/32846/commits"},"statuses":{"href":"https://api.github.com/repos/elastic/elasticsearch/statuses/cd0d5270e30e583c51599b880ee4e8411ae19ad1"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"ywelsch"}}