{"action":"created","comment":{"url":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments/41574454","id":41574454,"diff_hunk":"@@ -0,0 +1,667 @@\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.common.cache;\n+\n+import org.elasticsearch.common.collect.Tuple;\n+import org.elasticsearch.common.util.concurrent.ReleasableLock;\n+\n+import java.util.*;\n+import java.util.concurrent.ExecutionException;\n+import java.util.concurrent.atomic.LongAdder;\n+import java.util.concurrent.locks.ReadWriteLock;\n+import java.util.concurrent.locks.ReentrantLock;\n+import java.util.concurrent.locks.ReentrantReadWriteLock;\n+import java.util.function.ToLongBiFunction;\n+\n+/**\n+ * A simple concurrent cache.\n+ * <p>\n+ * Cache is a simple concurrent cache that supports time-based and weight-based evictions, with notifications for all\n+ * evictions. The design goals for this cache were simplicity and read performance. This means that we are willing to\n+ * accept reduced write performance in exchange for easy-to-understand code. Cache statistics for hits, misses and\n+ * evictions are exposed.\n+ * <p>\n+ * The design of the cache is relatively simple. The cache is segmented into 256 segments which are backed by HashMaps.\n+ * Each segment is protected by a re-entrant read/write lock. The read/write locks permit multiple concurrent readers\n+ * without contention, and the segments gives us write throughput without impacting readers (so readers are blocked only\n+ * if they are reading a segment that a writer is writing to).\n+ * <p>\n+ * The LRU functionality is backed by a single doubly-linked list chaining the entries in order of insertion. This\n+ * LRU list is protected by a lock that serializes all writes to it. There are opportunities for improvements\n+ * here if write throughput is a concern.\n+ * <ol>\n+ * <li>LRU list mutations could be inserted into a blocking queue that a single thread is reading from\n+ * and applying to the LRU list.</li>\n+ * <li>Promotions could be deferred for entries that were \"recently\" promoted.</li>\n+ * <li>Locks on the list could be taken per node being modified instead of globally.</li>\n+ * </ol>\n+ * <p>\n+ * Evictions only occur after a mutation to the cache (meaning an entry promotion, a cache insertion, or a manual\n+ * invalidation) or an explicit call to {@link #refresh()}.\n+ *\n+ * @param <K> The type of the keys\n+ * @param <V> The type of the values\n+ */\n+public class Cache<K, V> {\n+    // positive if entries have an expiration\n+    private long expireAfterAccess = -1;\n+\n+    // true if entries can expire after access\n+    private boolean entriesExpireAfterAccess;\n+\n+    // positive if entries have an expiration after write\n+    private long expireAfterWrite = -1;\n+\n+    // true if entries can expire after initial insertion\n+    private boolean entriesExpireAfterWrite;\n+\n+    // the number of entries in the cache\n+    private int count = 0;\n+\n+    // the weight of the entries in the cache\n+    private long weight = 0;\n+\n+    // the maximum weight that this cache supports\n+    private long maximumWeight = -1;\n+\n+    // the weigher of entries\n+    private ToLongBiFunction<K, V> weigher = (k, v) -> 1;\n+\n+    // the removal callback\n+    private RemovalListener<K, V> removalListener = notification -> {\n+    };\n+\n+    // use CacheBuilder to construct\n+    Cache() {\n+    }\n+\n+    void setExpireAfterAccess(long expireAfterAccess) {\n+        if (expireAfterAccess <= 0) {\n+            throw new IllegalArgumentException(\"expireAfterAccess <= 0\");\n+        }\n+        this.expireAfterAccess = expireAfterAccess;\n+        this.entriesExpireAfterAccess = true;\n+    }\n+\n+    void setExpireAfterWrite(long expireAfterWrite) {\n+        if (expireAfterWrite <= 0) {\n+            throw new IllegalArgumentException(\"expireAfterWrite <= 0\");\n+        }\n+        this.expireAfterWrite = expireAfterWrite;\n+        this.entriesExpireAfterWrite = true;\n+    }\n+\n+    void setMaximumWeight(long maximumWeight) {\n+        if (maximumWeight < 0) {\n+            throw new IllegalArgumentException(\"maximumWeight < 0\");\n+        }\n+        this.maximumWeight = maximumWeight;\n+    }\n+\n+    void setWeigher(ToLongBiFunction<K, V> weigher) {\n+        Objects.requireNonNull(weigher);\n+        this.weigher = weigher;\n+    }\n+\n+    void setRemovalListener(RemovalListener<K, V> removalListener) {\n+        Objects.requireNonNull(removalListener);\n+        this.removalListener = removalListener;\n+    }\n+\n+    /**\n+     * The relative time used to track time-based evictions.\n+     *\n+     * @return the current relative time\n+     */\n+    protected long now() {\n+        // System.nanoTime takes non-negligible time, so we only use it if we need it\n+        // use System.nanoTime because we want relative time, not absolute time\n+        return entriesExpireAfterAccess || entriesExpireAfterWrite ? System.nanoTime() : 0;\n+    }\n+\n+    // the state of an entry in the LRU list\n+    enum State {\n+        NEW, EXISTING, DELETED\n+    }\n+\n+    static class Entry<K, V> {\n+        final K key;\n+        final V value;\n+        long writeTime;\n+        volatile long accessTime;\n+        Entry<K, V> before;\n+        Entry<K, V> after;\n+        State state = State.NEW;\n+\n+        public Entry(K key, V value, long writeTime) {\n+            this.key = key;\n+            this.value = value;\n+            this.writeTime = this.accessTime = writeTime;\n+        }\n+    }\n+\n+    /**\n+     * A cache segment.\n+     * <p>\n+     * A CacheSegment is backed by a HashMap and is protected by a read/write lock.\n+     *\n+     * @param <K> the type of the keys\n+     * @param <V> the type of the values\n+     */\n+    private static class CacheSegment<K, V> {\n+        // read/write lock protecting mutations to the segment\n+        ReadWriteLock segmentLock = new ReentrantReadWriteLock();\n+\n+        ReleasableLock readLock = new ReleasableLock(segmentLock.readLock());\n+        ReleasableLock writeLock = new ReleasableLock(segmentLock.writeLock());\n+\n+        Map<K, Entry<K, V>> map = new HashMap<>();\n+        SegmentStats segmentStats = new SegmentStats();\n+\n+        /**\n+         * get an entry from the segment\n+         *\n+         * @param key the key of the entry to get from the cache\n+         * @param now the access time of this entry\n+         * @return the entry if there was one, otherwise null\n+         */\n+        Entry<K, V> get(K key, long now) {\n+            Entry<K, V> entry;\n+            try (ReleasableLock ignored = readLock.acquire()) {\n+                entry = map.get(key);\n+            }\n+            if (entry != null) {\n+                segmentStats.hit();\n+                entry.accessTime = now;\n+            } else {\n+                segmentStats.miss();\n+            }\n+            return entry;\n+        }\n+\n+        /**\n+         * put an entry into the segment\n+         *\n+         * @param key   the key of the entry to add to the cache\n+         * @param value the value of the entry to add to the cache\n+         * @param now   the access time of this entry\n+         * @return a tuple of the new entry and the existing entry, if there was one otherwise null\n+         */\n+        Tuple<Entry<K, V>, Entry<K, V>> put(K key, V value, long now) {\n+            Entry<K, V> entry = new Entry<>(key, value, now);\n+            Entry<K, V> existing;\n+            try (ReleasableLock ignored = writeLock.acquire()) {\n+                existing = map.put(key, entry);\n+            }\n+            return Tuple.tuple(entry, existing);\n+        }\n+\n+        /**\n+         * remove an entry from the segment\n+         *\n+         * @param key the key of the entry to remove from the cache\n+         * @return the removed entry if there was one, otherwise null\n+         */\n+        Entry<K, V> remove(K key) {\n+            Entry<K, V> entry;\n+            try (ReleasableLock ignored = writeLock.acquire()) {\n+                entry = map.remove(key);\n+            }\n+            if (entry != null) {\n+                segmentStats.eviction();\n+            }\n+            return entry;\n+        }\n+\n+        private static class SegmentStats {\n+            private final LongAdder hits = new LongAdder();\n+            private final LongAdder misses = new LongAdder();\n+            private final LongAdder evictions = new LongAdder();\n+\n+            void hit() {\n+                hits.increment();\n+            }\n+\n+            void miss() {\n+                misses.increment();\n+            }\n+\n+            void eviction() {\n+                evictions.increment();\n+            }\n+        }\n+    }\n+\n+    private final CacheSegment<K, V>[] segments = new CacheSegment[256];\n+\n+    {\n+        for (int i = 0; i < segments.length; i++) {\n+            segments[i] = new CacheSegment<>();\n+        }\n+    }\n+\n+    Entry<K, V> head;\n+    Entry<K, V> tail;\n+\n+    // lock protecting mutations to the LRU list\n+    private ReleasableLock lruLock = new ReleasableLock(new ReentrantLock());\n+\n+    /**\n+     * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.\n+     *\n+     * @param key the key whose associated value is to be returned\n+     * @return the value to which the specified key is mapped, or null if this map contains no mapping for the key\n+     */\n+    public V get(K key) {\n+        return get(key, now());\n+    }\n+\n+    private V get(K key, long now) {\n+        CacheSegment<K, V> segment = getCacheSegment(key);\n+        Entry<K, V> entry = segment.get(key, now);\n+        if (entry == null || isExpired(entry, now)) {\n+            return null;\n+        } else {\n+            promote(entry, now);\n+            return entry.value;\n+        }\n+    }\n+\n+    /**\n+     * If the specified key is not already associated with a value (or is mapped to null), attempts to compute its\n+     * value using the given mapping function and enters it into this map unless null.\n+     *\n+     * @param key    the key whose associated value is to be returned or computed for if non-existant\n+     * @param loader the function to compute a value given a key\n+     * @return the current (existing or computed) value associated with the specified key, or null if the computed\n+     * value is null\n+     * @throws ExecutionException thrown if loader throws an exception\n+     */\n+    public V computeIfAbsent(K key, CacheLoader<K, V> loader) throws ExecutionException {\n+        long now = now();\n+        V value = get(key, now);\n+        if (value == null) {\n+            CacheSegment<K, V> segment = getCacheSegment(key);\n+            try (ReleasableLock ignored = segment.writeLock.acquire()) {","path":"core/src/main/java/org/elasticsearch/common/cache/Cache.java","position":302,"original_position":302,"commit_id":"a556e310101f2dec9a36403147164869c7c43ae6","original_commit_id":"9ca032ae9d2ad1622cb3916be710d41a9cc36442","user":{"login":"jpountz","id":299848,"avatar_url":"https://avatars.githubusercontent.com/u/299848?v=3","gravatar_id":"","url":"https://api.github.com/users/jpountz","html_url":"https://github.com/jpountz","followers_url":"https://api.github.com/users/jpountz/followers","following_url":"https://api.github.com/users/jpountz/following{/other_user}","gists_url":"https://api.github.com/users/jpountz/gists{/gist_id}","starred_url":"https://api.github.com/users/jpountz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jpountz/subscriptions","organizations_url":"https://api.github.com/users/jpountz/orgs","repos_url":"https://api.github.com/users/jpountz/repos","events_url":"https://api.github.com/users/jpountz/events{/privacy}","received_events_url":"https://api.github.com/users/jpountz/received_events","type":"User","site_admin":false},"body":"so maybe let's just leave a comment about what you said?","created_at":"2015-10-08T21:55:51Z","updated_at":"2015-10-08T21:55:51Z","html_url":"https://github.com/elastic/elasticsearch/pull/13879#discussion_r41574454","pull_request_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/13879","_links":{"self":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments/41574454"},"html":{"href":"https://github.com/elastic/elasticsearch/pull/13879#discussion_r41574454"},"pull_request":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/13879"}}},"pull_request":{"url":"https://api.github.com/repos/elastic/elasticsearch/pulls/13879","id":46450431,"html_url":"https://github.com/elastic/elasticsearch/pull/13879","diff_url":"https://github.com/elastic/elasticsearch/pull/13879.diff","patch_url":"https://github.com/elastic/elasticsearch/pull/13879.patch","issue_url":"https://api.github.com/repos/elastic/elasticsearch/issues/13879","number":13879,"state":"open","locked":false,"title":"Replace Guava cache with simple concurrent LRU cache","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":"This pull request replaces the Guava cache with a simple concurrent LRU cache with flexible eviction policies.\r\n","created_at":"2015-09-30T20:14:14Z","updated_at":"2015-10-08T21:55:51Z","closed_at":null,"merged_at":null,"merge_commit_sha":"06110e8e1285de192da2cc7c39b6bc8d916ff0ea","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/13879/commits","review_comments_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/13879/comments","review_comment_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments{/number}","comments_url":"https://api.github.com/repos/elastic/elasticsearch/issues/13879/comments","statuses_url":"https://api.github.com/repos/elastic/elasticsearch/statuses/a556e310101f2dec9a36403147164869c7c43ae6","head":{"label":"jasontedor:straight-cache-homey","ref":"straight-cache-homey","sha":"a556e310101f2dec9a36403147164869c7c43ae6","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},"repo":{"id":38618383,"name":"elasticsearch","full_name":"jasontedor/elasticsearch","owner":{"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},"private":false,"html_url":"https://github.com/jasontedor/elasticsearch","description":"Open Source, Distributed, RESTful Search Engine","fork":true,"url":"https://api.github.com/repos/jasontedor/elasticsearch","forks_url":"https://api.github.com/repos/jasontedor/elasticsearch/forks","keys_url":"https://api.github.com/repos/jasontedor/elasticsearch/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jasontedor/elasticsearch/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jasontedor/elasticsearch/teams","hooks_url":"https://api.github.com/repos/jasontedor/elasticsearch/hooks","issue_events_url":"https://api.github.com/repos/jasontedor/elasticsearch/issues/events{/number}","events_url":"https://api.github.com/repos/jasontedor/elasticsearch/events","assignees_url":"https://api.github.com/repos/jasontedor/elasticsearch/assignees{/user}","branches_url":"https://api.github.com/repos/jasontedor/elasticsearch/branches{/branch}","tags_url":"https://api.github.com/repos/jasontedor/elasticsearch/tags","blobs_url":"https://api.github.com/repos/jasontedor/elasticsearch/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jasontedor/elasticsearch/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jasontedor/elasticsearch/git/refs{/sha}","trees_url":"https://api.github.com/repos/jasontedor/elasticsearch/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jasontedor/elasticsearch/statuses/{sha}","languages_url":"https://api.github.com/repos/jasontedor/elasticsearch/languages","stargazers_url":"https://api.github.com/repos/jasontedor/elasticsearch/stargazers","contributors_url":"https://api.github.com/repos/jasontedor/elasticsearch/contributors","subscribers_url":"https://api.github.com/repos/jasontedor/elasticsearch/subscribers","subscription_url":"https://api.github.com/repos/jasontedor/elasticsearch/subscription","commits_url":"https://api.github.com/repos/jasontedor/elasticsearch/commits{/sha}","git_commits_url":"https://api.github.com/repos/jasontedor/elasticsearch/git/commits{/sha}","comments_url":"https://api.github.com/repos/jasontedor/elasticsearch/comments{/number}","issue_comment_url":"https://api.github.com/repos/jasontedor/elasticsearch/issues/comments{/number}","contents_url":"https://api.github.com/repos/jasontedor/elasticsearch/contents/{+path}","compare_url":"https://api.github.com/repos/jasontedor/elasticsearch/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jasontedor/elasticsearch/merges","archive_url":"https://api.github.com/repos/jasontedor/elasticsearch/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jasontedor/elasticsearch/downloads","issues_url":"https://api.github.com/repos/jasontedor/elasticsearch/issues{/number}","pulls_url":"https://api.github.com/repos/jasontedor/elasticsearch/pulls{/number}","milestones_url":"https://api.github.com/repos/jasontedor/elasticsearch/milestones{/number}","notifications_url":"https://api.github.com/repos/jasontedor/elasticsearch/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jasontedor/elasticsearch/labels{/name}","releases_url":"https://api.github.com/repos/jasontedor/elasticsearch/releases{/id}","created_at":"2015-07-06T12:48:14Z","updated_at":"2015-07-06T12:48:36Z","pushed_at":"2015-10-08T21:51:25Z","git_url":"git://github.com/jasontedor/elasticsearch.git","ssh_url":"git@github.com:jasontedor/elasticsearch.git","clone_url":"https://github.com/jasontedor/elasticsearch.git","svn_url":"https://github.com/jasontedor/elasticsearch","homepage":"https://www.elastic.co/products/elasticsearch","size":161053,"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":"c208add6251f273c0544cc59251a161527257a15","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}","created_at":"2010-02-08T13:20:56Z","updated_at":"2015-10-08T20:00:54Z","pushed_at":"2015-10-08T21:51:26Z","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":449299,"stargazers_count":13000,"watchers_count":13000,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":4240,"mirror_url":null,"open_issues_count":1264,"forks":4240,"open_issues":1264,"watchers":13000,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/13879"},"html":{"href":"https://github.com/elastic/elasticsearch/pull/13879"},"issue":{"href":"https://api.github.com/repos/elastic/elasticsearch/issues/13879"},"comments":{"href":"https://api.github.com/repos/elastic/elasticsearch/issues/13879/comments"},"review_comments":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/13879/comments"},"review_comment":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/13879/commits"},"statuses":{"href":"https://api.github.com/repos/elastic/elasticsearch/statuses/a556e310101f2dec9a36403147164869c7c43ae6"}}}}