{"action":"created","comment":{"url":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments/60938962","id":60938962,"diff_hunk":"@@ -0,0 +1,466 @@\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.metadata;\n+\n+import org.elasticsearch.cluster.Diff;\n+import org.elasticsearch.common.ParseField;\n+import org.elasticsearch.common.io.stream.StreamInput;\n+import org.elasticsearch.common.io.stream.StreamOutput;\n+import org.elasticsearch.common.io.stream.Writeable;\n+import org.elasticsearch.common.joda.Joda;\n+import org.elasticsearch.common.settings.Setting;\n+import org.elasticsearch.common.settings.Settings;\n+import org.elasticsearch.common.xcontent.ObjectParser;\n+import org.elasticsearch.common.xcontent.ToXContent;\n+import org.elasticsearch.common.xcontent.XContentBuilder;\n+import org.elasticsearch.common.xcontent.XContentParser;\n+import org.elasticsearch.index.Index;\n+\n+import java.io.IOException;\n+import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.EnumSet;\n+import java.util.List;\n+import java.util.Objects;\n+import java.util.Set;\n+import java.util.concurrent.TimeUnit;\n+import java.util.function.BiFunction;\n+\n+/**\n+ * A collection of tombstones for explicitly marking indices as deleted in the cluster state.\n+ *\n+ * The cluster state contains a list of index tombstones for indices that have been\n+ * deleted in the cluster.  Because cluster states are processed asynchronously by\n+ * nodes and a node could be removed from the cluster for a period of time, the\n+ * tombstones remain in the cluster state for a fixed period of time, after which\n+ * they are purged.\n+ */\n+final public class IndexGraveyard implements MetaData.Custom {\n+\n+    /**\n+     * Setting for the maximum tombstones allowed in the cluster state;\n+     * prevents the cluster state size from exploding too large, but it opens the\n+     * very unlikely risk that if there are greater than MAX_TOMBSTONES index\n+     * deletions while a node was offline, when it comes back online, it will have\n+     * missed index deletions that it may need to process.\n+     */\n+    public static final Setting<Integer> SETTING_MAX_TOMBSTONES = Setting.intSetting(\"cluster.indices.tombstones.size\",\n+                                                                                     500, // the default maximum number of tombstones\n+                                                                                     Setting.Property.NodeScope);\n+\n+    public static final IndexGraveyard PROTO = new IndexGraveyard(new ArrayList<>());\n+    public static final String TYPE = \"index-graveyard\";\n+    private static final ParseField TOMBSTONES_FIELD = new ParseField(\"tombstones\");\n+    private static final ObjectParser<List<Tombstone>, Void> GRAVEYARD_PARSER = new ObjectParser<>(\"index_graveyard\", ArrayList::new);\n+    static {\n+        GRAVEYARD_PARSER.declareObjectArray(List::addAll, Tombstone.getParser(), TOMBSTONES_FIELD);\n+    }\n+\n+    private final List<Tombstone> tombstones;\n+\n+    private IndexGraveyard(final List<Tombstone> list) {\n+        tombstones = Collections.unmodifiableList(list);\n+    }\n+\n+    private IndexGraveyard(final StreamInput in) throws IOException {\n+        final int queueSize = in.readVInt();\n+        List<Tombstone> tombstones = new ArrayList<>(queueSize);\n+        for (int i = 0; i < queueSize; i++) {\n+            tombstones.add(new Tombstone(in));\n+        }\n+        this.tombstones = Collections.unmodifiableList(tombstones);\n+    }\n+\n+    public static IndexGraveyard fromStream(final StreamInput in) throws IOException {\n+        return new IndexGraveyard(in);\n+    }\n+\n+    @Override\n+    public String type() {\n+        return TYPE;\n+    }\n+\n+    @Override\n+    public EnumSet<MetaData.XContentContext> context() {\n+        return MetaData.API_AND_GATEWAY;\n+    }\n+\n+    @Override\n+    public boolean equals(Object obj) {\n+        return (obj instanceof IndexGraveyard) && Objects.equals(tombstones, ((IndexGraveyard)obj).tombstones);\n+    }\n+\n+    @Override\n+    public int hashCode() {\n+        return Objects.hash(tombstones);\n+    }\n+\n+    /**\n+     * Get the current unmodifiable index tombstone list.\n+     */\n+    public List<Tombstone> getTombstones() {\n+        return tombstones;\n+    }\n+\n+    @Override\n+    public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {\n+        builder.startArray(TOMBSTONES_FIELD.getPreferredName());\n+        for (Tombstone tombstone : tombstones) {\n+            tombstone.toXContent(builder, params);\n+        }\n+        return builder.endArray();\n+    }\n+\n+    public IndexGraveyard fromXContent(final XContentParser parser) throws IOException {\n+        return new IndexGraveyard(GRAVEYARD_PARSER.parse(parser));\n+    }\n+\n+    @Override\n+    public String toString() {\n+        return \"IndexGraveyard[\" + tombstones + \"]\";\n+    }\n+\n+    @Override\n+    public void writeTo(final StreamOutput out) throws IOException {\n+        out.writeVInt(tombstones.size());\n+        for (Tombstone tombstone : tombstones) {\n+            tombstone.writeTo(out);\n+        }\n+    }\n+\n+    @Override\n+    public IndexGraveyard readFrom(final StreamInput in) throws IOException {\n+        return new IndexGraveyard(in);\n+    }\n+\n+    @Override\n+    @SuppressWarnings(\"unchecked\")\n+    public Diff<MetaData.Custom> diff(final MetaData.Custom previous) {\n+        return new IndexGraveyardDiff((IndexGraveyard) previous, this);\n+    }\n+\n+    @Override\n+    public Diff<MetaData.Custom> readDiffFrom(final StreamInput in) throws IOException {\n+        return new IndexGraveyardDiff(in);\n+    }\n+\n+    public static IndexGraveyard.Builder builder() {\n+        return new IndexGraveyard.Builder();\n+    }\n+\n+    public static IndexGraveyard.Builder builder(final IndexGraveyard graveyard) {\n+        return new IndexGraveyard.Builder(graveyard);\n+    }\n+\n+    /**\n+     * A class to build an IndexGraveyard.\n+     */\n+    final public static class Builder {\n+        private List<Tombstone> tombstones;\n+        private int numPurged = -1;\n+\n+        private Builder() {\n+            tombstones = new ArrayList<>();\n+        }\n+\n+        private Builder(IndexGraveyard that) {\n+            tombstones = new ArrayList<>(that.getTombstones());\n+        }\n+\n+        /**\n+         * A copy of the current tombstones in the builder.\n+         */\n+        public List<Tombstone> tombstones() {\n+            return Collections.unmodifiableList(tombstones);\n+        }\n+\n+        /**\n+         * Add a deleted index to the list of tombstones in the cluster state.\n+         */\n+        public Builder addTombstone(final Index index) {\n+            tombstones.add(new Tombstone(index, System.currentTimeMillis()));\n+            return this;\n+        }\n+\n+        /**\n+         * Add a set of deleted indexes to the list of tombstones in the cluster state.\n+         */\n+        public Builder addTombstones(final Set<Index> indices) {\n+            indices.stream().forEach(this::addTombstone);\n+            return this;\n+        }\n+\n+        /**\n+         * Add a list of tombstones to the graveyard.\n+         */\n+        Builder addBuiltTombstones(final List<Tombstone> tombstones) {\n+            this.tombstones.addAll(tombstones);\n+            return this;\n+        }\n+\n+        /**\n+         * Get the number of tombstones that were purged.  This should *only* be called\n+         * after build() has been called.\n+         */\n+        public int getNumPurged() {\n+            assert numPurged != -1;\n+            return numPurged;\n+        }\n+\n+        /**\n+         * Purge tombstone entries.  Returns the number of entries that were purged.\n+         *\n+         * Tombstones are purged if the number of tombstones in the list\n+         * is greater than the input parameter of maximum allowed tombstones.\n+         * Tombstones are purged until the list is equal to the maximum allowed.\n+         */\n+        private int purge(final int maxTombstones) {\n+            int count = tombstones().size() - maxTombstones;\n+            if (count <= 0) {\n+                return 0;\n+            }\n+            tombstones = tombstones.subList(count, tombstones.size());\n+            return count;\n+        }\n+\n+        public IndexGraveyard build() {\n+            return build(Settings.EMPTY);\n+        }\n+\n+        public IndexGraveyard build(final Settings settings) {\n+            // first, purge the necessary amount of entries\n+            numPurged = purge(SETTING_MAX_TOMBSTONES.get(settings));\n+            return new IndexGraveyard(tombstones);\n+        }\n+    }\n+\n+    /**\n+     * A class representing a diff of two IndexGraveyard objects.\n+     */\n+    final public static class IndexGraveyardDiff implements Diff<MetaData.Custom> {\n+\n+        private final List<Tombstone> added;\n+        private final int removedCount;\n+\n+        IndexGraveyardDiff(final StreamInput in) throws IOException {\n+            added = Collections.unmodifiableList(in.readList((streamInput) -> new Tombstone(streamInput)));\n+            removedCount = in.readVInt();\n+        }\n+\n+        IndexGraveyardDiff(final IndexGraveyard previous, final IndexGraveyard current) {\n+            final List<Tombstone> previousTombstones = previous.tombstones;\n+            final List<Tombstone> currentTombstones = current.tombstones;\n+            final List<Tombstone> added;\n+            final int removed;\n+            if (previousTombstones.isEmpty()) {\n+                // nothing will have been removed, and all entries in current are new\n+                added = new ArrayList<>(currentTombstones);\n+                removed = 0;\n+            } else if (currentTombstones.isEmpty()) {\n+                // nothing will have been added, and all entries in previous are removed\n+                added = Collections.emptyList();\n+                removed = previousTombstones.size();\n+            } else {\n+                // look through the back, starting from the end, for added tombstones\n+                final Tombstone lastAddedTombstone = previousTombstones.get(previousTombstones.size() - 1);\n+                final int addedIndex = currentTombstones.lastIndexOf(lastAddedTombstone);\n+                if (addedIndex < currentTombstones.size()) {\n+                    added = currentTombstones.subList(addedIndex + 1, currentTombstones.size());\n+                } else {\n+                    added = Collections.emptyList();\n+                }\n+                // look from the front for the removed tombstones\n+                final Tombstone firstTombstone = currentTombstones.get(0);\n+                int idx = previousTombstones.indexOf(firstTombstone);\n+                if (idx < 0) {\n+                    // the first tombstone in the current list wasn't found in the previous list,\n+                    // which means all tombstones from the previous list have been deleted.\n+                    assert added.equals(currentTombstones); // all previous are removed, so the current list must be the same as the added\n+                    idx = previousTombstones.size();\n+                }\n+                removed = idx;\n+            }\n+            this.added = Collections.unmodifiableList(added);\n+            this.removedCount = removed;\n+        }\n+\n+        @Override\n+        public void writeTo(final StreamOutput out) throws IOException {\n+            out.writeList(added);\n+            out.writeVInt(removedCount);\n+        }\n+\n+        @Override\n+        public IndexGraveyard apply(final MetaData.Custom previous) {\n+            @SuppressWarnings(\"unchecked\")\n+            final IndexGraveyard old = (IndexGraveyard) previous;\n+            if (removedCount > old.tombstones.size()) {\n+                throw new IllegalStateException(\"IndexGraveyardDiff cannot remove \" + removedCount + \" entries from \" +\n+                                                old.tombstones.size() + \" tombstones.\");\n+            }\n+            final List<Tombstone> newTombstones = new ArrayList<>(old.tombstones.subList(removedCount, old.tombstones.size()));\n+            for (Tombstone tombstone : added) {\n+                newTombstones.add(tombstone);\n+            }\n+            return new IndexGraveyard.Builder().addBuiltTombstones(newTombstones).build();\n+        }\n+\n+        /** The index tombstones that were added between two states */\n+        public List<Tombstone> getAdded() {\n+            return added;\n+        }\n+\n+        /** The number of index tombstones that were removed between two states */\n+        public int getRemovedCount() {\n+            return removedCount;\n+        }\n+    }\n+\n+    /**\n+     * An individual tombstone entry for representing a deleted index.\n+     */\n+    final public static class Tombstone implements ToXContent, Writeable<Tombstone> {\n+\n+        private static final String INDEX_KEY = \"index\";\n+        private static final String DELETE_DATE_IN_MILLIS_KEY = \"delete_date_in_millis\";\n+        private static final String DELETE_DATE_KEY = \"delete_date\";\n+        private static final ObjectParser<Tombstone.Builder, Void> TOMBSTONE_PARSER =\n+            new ObjectParser<>(\"tombstoneEntry\", Tombstone.Builder::new);\n+        static {\n+            TOMBSTONE_PARSER.declareObject(Tombstone.Builder::index, Index::parseIndex, new ParseField(INDEX_KEY));\n+            TOMBSTONE_PARSER.declareLong(Tombstone.Builder::deleteDateInMillis, new ParseField(DELETE_DATE_IN_MILLIS_KEY));\n+            TOMBSTONE_PARSER.declareString((b,s) -> {}, new ParseField(DELETE_DATE_KEY));\n+        }\n+\n+        static BiFunction<XContentParser, Void, Tombstone> getParser() {\n+            return (p,c) -> TOMBSTONE_PARSER.apply(p, c).build();","path":"core/src/main/java/org/elasticsearch/cluster/metadata/IndexGraveyard.java","position":353,"original_position":353,"commit_id":"dd3fbef9ee7d5b88e6a95299d96226891eba8c72","original_commit_id":"dd3fbef9ee7d5b88e6a95299d96226891eba8c72","user":{"login":"jasontedor","id":4744941,"avatar_url":"https://avatars.githubusercontent.com/u/4744941?v=3","gravatar_id":"","url":"https://api.github.com/users/jasontedor","html_url":"https://github.com/jasontedor","followers_url":"https://api.github.com/users/jasontedor/followers","following_url":"https://api.github.com/users/jasontedor/following{/other_user}","gists_url":"https://api.github.com/users/jasontedor/gists{/gist_id}","starred_url":"https://api.github.com/users/jasontedor/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jasontedor/subscriptions","organizations_url":"https://api.github.com/users/jasontedor/orgs","repos_url":"https://api.github.com/users/jasontedor/repos","events_url":"https://api.github.com/users/jasontedor/events{/privacy}","received_events_url":"https://api.github.com/users/jasontedor/received_events","type":"User","site_admin":false},"body":"Nit: `(p,c)` -> `(p, c)`","created_at":"2016-04-25T15:59:48Z","updated_at":"2016-04-25T15:59:48Z","html_url":"https://github.com/elastic/elasticsearch/pull/17265#discussion_r60938962","pull_request_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/17265","_links":{"self":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments/60938962"},"html":{"href":"https://github.com/elastic/elasticsearch/pull/17265#discussion_r60938962"},"pull_request":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/17265"}}},"pull_request":{"url":"https://api.github.com/repos/elastic/elasticsearch/pulls/17265","id":63827841,"html_url":"https://github.com/elastic/elasticsearch/pull/17265","diff_url":"https://github.com/elastic/elasticsearch/pull/17265.diff","patch_url":"https://github.com/elastic/elasticsearch/pull/17265.patch","issue_url":"https://api.github.com/repos/elastic/elasticsearch/issues/17265","number":17265,"state":"open","locked":false,"title":"Adds tombstones to cluster state for index deletions","user":{"login":"abeyad","id":1631297,"avatar_url":"https://avatars.githubusercontent.com/u/1631297?v=3","gravatar_id":"","url":"https://api.github.com/users/abeyad","html_url":"https://github.com/abeyad","followers_url":"https://api.github.com/users/abeyad/followers","following_url":"https://api.github.com/users/abeyad/following{/other_user}","gists_url":"https://api.github.com/users/abeyad/gists{/gist_id}","starred_url":"https://api.github.com/users/abeyad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/abeyad/subscriptions","organizations_url":"https://api.github.com/users/abeyad/orgs","repos_url":"https://api.github.com/users/abeyad/repos","events_url":"https://api.github.com/users/abeyad/events{/privacy}","received_events_url":"https://api.github.com/users/abeyad/received_events","type":"User","site_admin":false},"body":"Previously, we would determine index deletes in the cluster state by\r\ncomparing the index metadatas between the current cluster state and the\r\nprevious cluster state and decipher which ones were missing (the missing\r\nones are deleted indices).  This led to a situation where a node that went \r\noffline and rejoined the cluster could potentially cause dangling indices to\r\nbe imported which should have been deleted, because when a node rejoins,\r\nits previous cluster state does not contain reliable state.\r\n\r\nThis commit introduces the notion of index tombstones in the cluster\r\nstate, where we are explicit about which indices have been deleted.\r\nIn the case where the previous cluster state is not useful for index metadata\r\ncomparisons, a node now determines which indices are to be deleted based\r\non these tombstones in the cluster state.  There is also functionality to \r\npurge the tombstones after exceeding a certain amount. \r\n\r\nCloses #16358\r\nCloses #17435 ","created_at":"2016-03-23T03:46:12Z","updated_at":"2016-04-25T15:59:48Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3e8f2328a95275cc51375608bd84ade3e3ed8830","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/17265/commits","review_comments_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/17265/comments","review_comment_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments{/number}","comments_url":"https://api.github.com/repos/elastic/elasticsearch/issues/17265/comments","statuses_url":"https://api.github.com/repos/elastic/elasticsearch/statuses/dd3fbef9ee7d5b88e6a95299d96226891eba8c72","head":{"label":"abeyad:feature/tombstone-deleted-indices","ref":"feature/tombstone-deleted-indices","sha":"dd3fbef9ee7d5b88e6a95299d96226891eba8c72","user":{"login":"abeyad","id":1631297,"avatar_url":"https://avatars.githubusercontent.com/u/1631297?v=3","gravatar_id":"","url":"https://api.github.com/users/abeyad","html_url":"https://github.com/abeyad","followers_url":"https://api.github.com/users/abeyad/followers","following_url":"https://api.github.com/users/abeyad/following{/other_user}","gists_url":"https://api.github.com/users/abeyad/gists{/gist_id}","starred_url":"https://api.github.com/users/abeyad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/abeyad/subscriptions","organizations_url":"https://api.github.com/users/abeyad/orgs","repos_url":"https://api.github.com/users/abeyad/repos","events_url":"https://api.github.com/users/abeyad/events{/privacy}","received_events_url":"https://api.github.com/users/abeyad/received_events","type":"User","site_admin":false},"repo":{"id":48216890,"name":"elasticsearch","full_name":"abeyad/elasticsearch","owner":{"login":"abeyad","id":1631297,"avatar_url":"https://avatars.githubusercontent.com/u/1631297?v=3","gravatar_id":"","url":"https://api.github.com/users/abeyad","html_url":"https://github.com/abeyad","followers_url":"https://api.github.com/users/abeyad/followers","following_url":"https://api.github.com/users/abeyad/following{/other_user}","gists_url":"https://api.github.com/users/abeyad/gists{/gist_id}","starred_url":"https://api.github.com/users/abeyad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/abeyad/subscriptions","organizations_url":"https://api.github.com/users/abeyad/orgs","repos_url":"https://api.github.com/users/abeyad/repos","events_url":"https://api.github.com/users/abeyad/events{/privacy}","received_events_url":"https://api.github.com/users/abeyad/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/abeyad/elasticsearch","description":"Open Source, Distributed, RESTful Search Engine","fork":true,"url":"https://api.github.com/repos/abeyad/elasticsearch","forks_url":"https://api.github.com/repos/abeyad/elasticsearch/forks","keys_url":"https://api.github.com/repos/abeyad/elasticsearch/keys{/key_id}","collaborators_url":"https://api.github.com/repos/abeyad/elasticsearch/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/abeyad/elasticsearch/teams","hooks_url":"https://api.github.com/repos/abeyad/elasticsearch/hooks","issue_events_url":"https://api.github.com/repos/abeyad/elasticsearch/issues/events{/number}","events_url":"https://api.github.com/repos/abeyad/elasticsearch/events","assignees_url":"https://api.github.com/repos/abeyad/elasticsearch/assignees{/user}","branches_url":"https://api.github.com/repos/abeyad/elasticsearch/branches{/branch}","tags_url":"https://api.github.com/repos/abeyad/elasticsearch/tags","blobs_url":"https://api.github.com/repos/abeyad/elasticsearch/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/abeyad/elasticsearch/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/abeyad/elasticsearch/git/refs{/sha}","trees_url":"https://api.github.com/repos/abeyad/elasticsearch/git/trees{/sha}","statuses_url":"https://api.github.com/repos/abeyad/elasticsearch/statuses/{sha}","languages_url":"https://api.github.com/repos/abeyad/elasticsearch/languages","stargazers_url":"https://api.github.com/repos/abeyad/elasticsearch/stargazers","contributors_url":"https://api.github.com/repos/abeyad/elasticsearch/contributors","subscribers_url":"https://api.github.com/repos/abeyad/elasticsearch/subscribers","subscription_url":"https://api.github.com/repos/abeyad/elasticsearch/subscription","commits_url":"https://api.github.com/repos/abeyad/elasticsearch/commits{/sha}","git_commits_url":"https://api.github.com/repos/abeyad/elasticsearch/git/commits{/sha}","comments_url":"https://api.github.com/repos/abeyad/elasticsearch/comments{/number}","issue_comment_url":"https://api.github.com/repos/abeyad/elasticsearch/issues/comments{/number}","contents_url":"https://api.github.com/repos/abeyad/elasticsearch/contents/{+path}","compare_url":"https://api.github.com/repos/abeyad/elasticsearch/compare/{base}...{head}","merges_url":"https://api.github.com/repos/abeyad/elasticsearch/merges","archive_url":"https://api.github.com/repos/abeyad/elasticsearch/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/abeyad/elasticsearch/downloads","issues_url":"https://api.github.com/repos/abeyad/elasticsearch/issues{/number}","pulls_url":"https://api.github.com/repos/abeyad/elasticsearch/pulls{/number}","milestones_url":"https://api.github.com/repos/abeyad/elasticsearch/milestones{/number}","notifications_url":"https://api.github.com/repos/abeyad/elasticsearch/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/abeyad/elasticsearch/labels{/name}","releases_url":"https://api.github.com/repos/abeyad/elasticsearch/releases{/id}","deployments_url":"https://api.github.com/repos/abeyad/elasticsearch/deployments","created_at":"2015-12-18T05:46:26Z","updated_at":"2015-12-18T05:46:48Z","pushed_at":"2016-04-21T20:17:15Z","git_url":"git://github.com/abeyad/elasticsearch.git","ssh_url":"git@github.com:abeyad/elasticsearch.git","clone_url":"https://github.com/abeyad/elasticsearch.git","svn_url":"https://github.com/abeyad/elasticsearch","homepage":"https://www.elastic.co/products/elasticsearch","size":213790,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"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":"elastic:master","ref":"master","sha":"83944c562856e99a5f18cf352c7b8899cd90d352","user":{"login":"elastic","id":6764390,"avatar_url":"https://avatars.githubusercontent.com/u/6764390?v=3","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,"name":"elasticsearch","full_name":"elastic/elasticsearch","owner":{"login":"elastic","id":6764390,"avatar_url":"https://avatars.githubusercontent.com/u/6764390?v=3","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":"2016-04-25T14:29:28Z","pushed_at":"2016-04-25T14:09:09Z","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":309290,"stargazers_count":16147,"watchers_count":16147,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":5429,"mirror_url":null,"open_issues_count":1103,"forks":5429,"open_issues":1103,"watchers":16147,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/17265"},"html":{"href":"https://github.com/elastic/elasticsearch/pull/17265"},"issue":{"href":"https://api.github.com/repos/elastic/elasticsearch/issues/17265"},"comments":{"href":"https://api.github.com/repos/elastic/elasticsearch/issues/17265/comments"},"review_comments":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/17265/comments"},"review_comment":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/17265/commits"},"statuses":{"href":"https://api.github.com/repos/elastic/elasticsearch/statuses/dd3fbef9ee7d5b88e6a95299d96226891eba8c72"}}}}