{"action":"created","comment":{"url":"https://api.github.com/repos/Teradata/presto/pulls/comments/82395140","pull_request_review_id":3307376,"id":82395140,"diff_hunk":"@@ -57,31 +79,332 @@\n         }\n     }\n \n+    /*\n+     * The relationship between the PrestoDriver, QueryExecutors, and\n+     * PrestoConnections is complicated:\n+     *\n+     * PrestoConnection <-many:one-> QueryExecutor <-many:one-> PrestoDriver\n+     *                               (HTTP client)\n+     *\n+     * - A PrestoConnection represents the outside world's view of a JDBC\n+     * connection to a Presto server.\n+     * - A PrestoConnection contains a QueryExecutor, which is responsible\n+     * for actually executing queries against the Presto server.\n+     * - QueryExecutor contains a reference to a driver-wide pool of execution\n+     * resources, and an HTTP client that executes requests using the resources\n+     * in that pool.\n+     *\n+     * An HTTP client is expensive to create and destroy, and we would like to\n+     * share it across as many PrestoConnections as possible. If every\n+     * connection could use the same HTTP client, life would be simple.\n+     * Unfortunately, some things that can be specified at the PrestoConnection\n+     * level must be configured at the HTTP client level. The obvious case of\n+     * this is the SSL/TLS trust store configuraion: An HTTP client has to be\n+     * configured with the appropriate trust store to make SSL connections to a\n+     * Presto server.\n+     *\n+     * The PrestoDriver deals in QueryExecutors, which have a 1:1 relationship\n+     * with HTTP clients. In order to create many PrestoConnections with the\n+     * same QueryExecutor, the PrestoDriver maintains a cache of\n+     * QueryExecutors.\n+     *\n+     * This leaves us having to solve one of the two hard problems in computer\n+     * science: cache invalidation, which in this case, is closely intertwined\n+     * with the need to eventually close the HTTP client contained in the\n+     * cached QueryExecutor.\n+     *\n+     * Conceptually, the flow is simple:\n+     * driver.connect() returns either\n+     * 1) A cached QueryExecutor that has an HTTP client that satisfies the\n+     *    requested connection properties.\n+     * 2) A new QueryExecutor with a new HTTP client if no cached\n+     *    QueryExecutor is satisfactory.\n+     *\n+     * connection.close() calls queryExecutor.close()\n+     * If this is the last reference to the queryExecutor, close the HTTP\n+     * client and invalidate() queryExecutor from the cache.\n+     *\n+     * Now we need a reference count. And things get hairy.\n+     *\n+     * com.google.common.cache.LoadingCache is safe for concurrent access, so\n+     * the get() call in driver.connect() and the invalidate() call in\n+     * queryExecutor.close() can't get us into a state where somebody has\n+     * gotten() a reference that's already been invalidated().\n+     *\n+     * The problem is actually the refcount; there's no way to atomically get()\n+     * a QueryExecutor from the cache and increment its refcount. It gets\n+     * worse: there's no way to know ahead of time which QueryExecutor you're\n+     * going to get from get(), so the locking for the refcount has to be done\n+     * against the cache rather than individual QueryExecutors.\n+     *\n+     * The solution is to protect the refcounts by synchronizing on the cache\n+     * and incrementing and decrementing the refcount while holding the cache's\n+     * monitor.\n+     *\n+     * This logic is encapsulated in ReferenceCountingLoadingCache. get() and\n+     * close() operations call acquire() and release(), respectively.\n+     *\n+     * The cleaner count handles the following situation:\n+     * Thread T1 has a reference on V for key K.\n+     * T1 calls release(K1) scheduling future F1 and increments cleaner count -> 1\n+     * time passes.\n+     * A daemon thread starts executing F1, but does not acquire the lock\n+     * Thread T2 calls acquire() and acquires the lock.\n+     *   acquire() increments the refcount\n+     *   acquire tries, but fails to cancel F1\n+     *   acquire() releases the lock\n+     *\n+     * At this point 2 things can happen\n+     * 1) F1 resumes executing. At this point the refcount is sufficient to\n+     *    ensure correctness\n+     * 2) T2 calls release(K1)\n+     *      release() acquires the lock.\n+     *      release schedules a cleanup F2 and increments cleaner count -> 2\n+     *      release fails to cancel F1\n+     *      release() releases the lock.\n+     *    F1 resumes executing and acquires the lock.\n+     *      releaseForCleaning() decrements cleaner count -> 1\n+     *      nothing further happens.\n+     *    more time passes.\n+     *    F2 executes\n+     *      releaseForCleaning() decrements cleaner count -> 0\n+     *      refcount is 0\n+     *      K1 is invalidated from the cache, and V1 is disposed.\n+     *\n+     * Without the cleaner count, we run the risk that some thread T3 calls\n+     * acquire(K1), loading V2 while more time passes. F2 is still using V1's\n+     * Holder, which has a refcount of zero. F1 then invalidates K1, which\n+     * disposes of V2 while it's still being used by T3. Havoc ensues.\n+     *\n+     * Instead of keeping a cleaner count, we could have a boolean flag in the\n+     * Holder that tracks whether or not the value has been invalidated. The\n+     * cleaner count has the advantage of ensuring the desired retention period\n+     * as well as guarding against the above situation, so it's all around\n+     * better.\n+     *\n+     * The other, other approach would be to set an 'invalidate after' time\n+     * in the holder. Now we have to worry about clock resolution and\n+     * monotonicity. No thanks; somebody did that work for the ScheduledFuture/\n+     * ScheduledExecutorService, let's take advantage of that.\n+     *\n+     * The locking hierarchy is as follows:\n+     * 1) The monitor on the ReferenceCountingLoadingCache\n+     * 2) <internal locking of com.google.common.cache.LoadingCache>\n+     *\n+     * The last case of interest is the process that is using PrestoDriver\n+     * calling prestoDriver.close(). This invalidates all of the cached\n+     * QueryExecutors regardless of their refcount by calling the cache's\n+     * close() method, and in doing so closes their HTTP clients. If anybody is\n+     * still trying to use a PrestoConnection created by the driver, they're\n+     * SOL.\n+     */\n+    private static class ReferenceCountingLoadingCache<K, V>\n+    {\n+        private class Holder\n+        {\n+            private final V value;\n+            private int refcount;\n+            private int cleanerCount;\n+            private ScheduledFuture oldCleanup;\n+\n+            private Holder(V value)\n+            {\n+                this.value = requireNonNull(value);\n+            }\n+\n+            private V get()\n+            {\n+                return value;\n+            }\n+\n+            private int getRefcount()\n+            {\n+                return refcount;\n+            }\n+\n+            private int getCleanerCount()\n+            {\n+                return cleanerCount;\n+            }\n+\n+            private int reference()\n+            {\n+                setCleanup(null);\n+                return ++refcount;\n+            }\n+\n+            private int dereference()\n+            {\n+                return --refcount;\n+            }\n+\n+            private void setCleanup(ScheduledFuture newCleanup)\n+            {\n+                if (oldCleanup != null && oldCleanup.cancel(false)) {\n+                    --cleanerCount;\n+                }\n+                oldCleanup = newCleanup;\n+            }\n+\n+            private void scheduleCleanup(\n+                    ScheduledExecutorService cleanupService,\n+                    Runnable cleanupTask,\n+                    long delay,\n+                    TimeUnit unit)\n+            {\n+                checkState(refcount == 0, \"non-zero refcount in scheduleCleanup\");\n+                ++cleanerCount;\n+                ScheduledFuture newCleanup = cleanupService.schedule(cleanupTask, delay, unit);\n+                setCleanup(newCleanup);\n+            }\n+\n+            private void releaseForCleaning()\n+            {\n+                --cleanerCount;\n+            }\n+        }\n+\n+        private final ScheduledExecutorService valueCleanupService;\n+        private final LoadingCache<K, Holder> backingCache;\n+        private final AtomicBoolean closed = new AtomicBoolean(false);\n+\n+        private ReferenceCountingLoadingCache(CacheLoader<K, V> loader, Consumer<V> disposer)\n+        {\n+            this.valueCleanupService = new ScheduledThreadPoolExecutor(1, daemonThreadsNamed(\"cache-cleanup-%s\"));\n+            this.backingCache = CacheBuilder.newBuilder()\n+                    .removalListener(new RemovalListener<K, Holder>() {\n+                        @Override\n+                        public void onRemoval(RemovalNotification<K, Holder> notification)\n+                        {\n+                            Holder holder = notification.getValue();\n+                            /*\n+                             * The docs say that both the key and value may be\n+                             * null if they've already been garbage collected.\n+                             * We aren't using weak or soft keys or values, so\n+                             * this shouldn't apply.\n+                             */\n+                            requireNonNull(holder, format(\"holder is null while removing key %s\", notification.getKey()));\n+                            checkState(holder.getRefcount() == 0, \"Non-zero refcount disposing %s\", notification.getKey());\n+                            checkState(holder.getCleanerCount() == 0, \"Non-zero refcount disposing %s\", notification.getKey());\n+                            disposer.accept(holder.get());\n+                        }\n+                    })\n+                    .build(new CacheLoader<K, Holder>() {\n+                        @Override\n+                        public Holder load(K key)\n+                                throws Exception\n+                        {\n+                            return new Holder(loader.load(key));\n+                        }\n+                    });\n+        }\n+\n+        private void close()\n+        {\n+            if (closed.compareAndSet(false, true)) {\n+                valueCleanupService.shutdownNow();\n+                backingCache.invalidateAll();\n+            }\n+        }\n+\n+        private V acquire(K key)\n+        {\n+            synchronized (this) {\n+                Holder holder = backingCache.getUnchecked(key);\n+                holder.reference();\n+                return holder.get();\n+            }\n+        }\n+\n+        private void release(K key)\n+        {\n+            /*\n+             * Access through the Map interface to avoid creating a Holder and immediately\n+             * scheduling it for cleanup.\n+             */\n+            Holder holder = backingCache.asMap().get(key);\n+            if (holder == null) {\n+                return;\n+            }\n+\n+            Runnable deferredRelease = () -> {\n+                synchronized (this) {\n+                    holder.releaseForCleaning();\n+                    if (holder.getCleanerCount() ==  0 && holder.getRefcount() == 0) {","path":"presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoDriver.java","position":305,"original_position":305,"commit_id":"cc737d2ef18b016b6ec7fde89f41e78b705ef8cb","original_commit_id":"cc737d2ef18b016b6ec7fde89f41e78b705ef8cb","user":{"login":"arhimondr","id":5570988,"avatar_url":"https://avatars.githubusercontent.com/u/5570988?v=3","gravatar_id":"","url":"https://api.github.com/users/arhimondr","html_url":"https://github.com/arhimondr","followers_url":"https://api.github.com/users/arhimondr/followers","following_url":"https://api.github.com/users/arhimondr/following{/other_user}","gists_url":"https://api.github.com/users/arhimondr/gists{/gist_id}","starred_url":"https://api.github.com/users/arhimondr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arhimondr/subscriptions","organizations_url":"https://api.github.com/users/arhimondr/orgs","repos_url":"https://api.github.com/users/arhimondr/repos","events_url":"https://api.github.com/users/arhimondr/events{/privacy}","received_events_url":"https://api.github.com/users/arhimondr/received_events","type":"User","site_admin":false},"body":"I think `lastRecentUse` timestamp is more easier to understand than the `cleanerCount` concept. ","created_at":"2016-10-07T14:04:46Z","updated_at":"2016-10-07T14:04:46Z","html_url":"https://github.com/Teradata/presto/pull/366#discussion_r82395140","pull_request_url":"https://api.github.com/repos/Teradata/presto/pulls/366","_links":{"self":{"href":"https://api.github.com/repos/Teradata/presto/pulls/comments/82395140"},"html":{"href":"https://github.com/Teradata/presto/pull/366#discussion_r82395140"},"pull_request":{"href":"https://api.github.com/repos/Teradata/presto/pulls/366"}}},"pull_request":{"url":"https://api.github.com/repos/Teradata/presto/pulls/366","id":87572631,"html_url":"https://github.com/Teradata/presto/pull/366","diff_url":"https://github.com/Teradata/presto/pull/366.diff","patch_url":"https://github.com/Teradata/presto/pull/366.patch","issue_url":"https://api.github.com/repos/Teradata/presto/issues/366","number":366,"state":"open","locked":false,"title":"Create QueryExecutor per client-specfic Properties","user":{"login":"ebd2","id":12751788,"avatar_url":"https://avatars.githubusercontent.com/u/12751788?v=3","gravatar_id":"","url":"https://api.github.com/users/ebd2","html_url":"https://github.com/ebd2","followers_url":"https://api.github.com/users/ebd2/followers","following_url":"https://api.github.com/users/ebd2/following{/other_user}","gists_url":"https://api.github.com/users/ebd2/gists{/gist_id}","starred_url":"https://api.github.com/users/ebd2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ebd2/subscriptions","organizations_url":"https://api.github.com/users/ebd2/orgs","repos_url":"https://api.github.com/users/ebd2/repos","events_url":"https://api.github.com/users/ebd2/events{/privacy}","received_events_url":"https://api.github.com/users/ebd2/received_events","type":"User","site_admin":false},"body":"See the monster comment for more details:\r\nhttps://github.com/Teradata/presto/pull/366/files#diff-309bc5b7d1d3088c40161acfd320a8d4R69\r\n\r\n@maciejgrzybek ","created_at":"2016-09-30T21:56:46Z","updated_at":"2016-10-07T14:04:46Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6af3161539a5a7bd110fc8b28a8fef8b2b888623","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/Teradata/presto/pulls/366/commits","review_comments_url":"https://api.github.com/repos/Teradata/presto/pulls/366/comments","review_comment_url":"https://api.github.com/repos/Teradata/presto/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Teradata/presto/issues/366/comments","statuses_url":"https://api.github.com/repos/Teradata/presto/statuses/cc737d2ef18b016b6ec7fde89f41e78b705ef8cb","head":{"label":"Teradata:httpclient","ref":"httpclient","sha":"cc737d2ef18b016b6ec7fde89f41e78b705ef8cb","user":{"login":"Teradata","id":1615979,"avatar_url":"https://avatars.githubusercontent.com/u/1615979?v=3","gravatar_id":"","url":"https://api.github.com/users/Teradata","html_url":"https://github.com/Teradata","followers_url":"https://api.github.com/users/Teradata/followers","following_url":"https://api.github.com/users/Teradata/following{/other_user}","gists_url":"https://api.github.com/users/Teradata/gists{/gist_id}","starred_url":"https://api.github.com/users/Teradata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Teradata/subscriptions","organizations_url":"https://api.github.com/users/Teradata/orgs","repos_url":"https://api.github.com/users/Teradata/repos","events_url":"https://api.github.com/users/Teradata/events{/privacy}","received_events_url":"https://api.github.com/users/Teradata/received_events","type":"Organization","site_admin":false},"repo":{"id":36903368,"name":"presto","full_name":"Teradata/presto","owner":{"login":"Teradata","id":1615979,"avatar_url":"https://avatars.githubusercontent.com/u/1615979?v=3","gravatar_id":"","url":"https://api.github.com/users/Teradata","html_url":"https://github.com/Teradata","followers_url":"https://api.github.com/users/Teradata/followers","following_url":"https://api.github.com/users/Teradata/following{/other_user}","gists_url":"https://api.github.com/users/Teradata/gists{/gist_id}","starred_url":"https://api.github.com/users/Teradata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Teradata/subscriptions","organizations_url":"https://api.github.com/users/Teradata/orgs","repos_url":"https://api.github.com/users/Teradata/repos","events_url":"https://api.github.com/users/Teradata/events{/privacy}","received_events_url":"https://api.github.com/users/Teradata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Teradata/presto","description":"Teradata Distribution of Presto -- A Distributed SQL Query Engine for Big Data","fork":true,"url":"https://api.github.com/repos/Teradata/presto","forks_url":"https://api.github.com/repos/Teradata/presto/forks","keys_url":"https://api.github.com/repos/Teradata/presto/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Teradata/presto/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Teradata/presto/teams","hooks_url":"https://api.github.com/repos/Teradata/presto/hooks","issue_events_url":"https://api.github.com/repos/Teradata/presto/issues/events{/number}","events_url":"https://api.github.com/repos/Teradata/presto/events","assignees_url":"https://api.github.com/repos/Teradata/presto/assignees{/user}","branches_url":"https://api.github.com/repos/Teradata/presto/branches{/branch}","tags_url":"https://api.github.com/repos/Teradata/presto/tags","blobs_url":"https://api.github.com/repos/Teradata/presto/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Teradata/presto/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Teradata/presto/git/refs{/sha}","trees_url":"https://api.github.com/repos/Teradata/presto/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Teradata/presto/statuses/{sha}","languages_url":"https://api.github.com/repos/Teradata/presto/languages","stargazers_url":"https://api.github.com/repos/Teradata/presto/stargazers","contributors_url":"https://api.github.com/repos/Teradata/presto/contributors","subscribers_url":"https://api.github.com/repos/Teradata/presto/subscribers","subscription_url":"https://api.github.com/repos/Teradata/presto/subscription","commits_url":"https://api.github.com/repos/Teradata/presto/commits{/sha}","git_commits_url":"https://api.github.com/repos/Teradata/presto/git/commits{/sha}","comments_url":"https://api.github.com/repos/Teradata/presto/comments{/number}","issue_comment_url":"https://api.github.com/repos/Teradata/presto/issues/comments{/number}","contents_url":"https://api.github.com/repos/Teradata/presto/contents/{+path}","compare_url":"https://api.github.com/repos/Teradata/presto/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Teradata/presto/merges","archive_url":"https://api.github.com/repos/Teradata/presto/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Teradata/presto/downloads","issues_url":"https://api.github.com/repos/Teradata/presto/issues{/number}","pulls_url":"https://api.github.com/repos/Teradata/presto/pulls{/number}","milestones_url":"https://api.github.com/repos/Teradata/presto/milestones{/number}","notifications_url":"https://api.github.com/repos/Teradata/presto/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Teradata/presto/labels{/name}","releases_url":"https://api.github.com/repos/Teradata/presto/releases{/id}","deployments_url":"https://api.github.com/repos/Teradata/presto/deployments","created_at":"2015-06-05T00:38:37Z","updated_at":"2016-10-07T04:15:04Z","pushed_at":"2016-10-07T13:43:31Z","git_url":"git://github.com/Teradata/presto.git","ssh_url":"git@github.com:Teradata/presto.git","clone_url":"https://github.com/Teradata/presto.git","svn_url":"https://github.com/Teradata/presto","homepage":"http://www.teradata.com/presto","size":165280,"stargazers_count":58,"watchers_count":58,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":11,"mirror_url":null,"open_issues_count":18,"forks":11,"open_issues":18,"watchers":58,"default_branch":"master"}},"base":{"label":"Teradata:master","ref":"master","sha":"8428d4d1b0c9a2639f75ac5799b2e97526ef1890","user":{"login":"Teradata","id":1615979,"avatar_url":"https://avatars.githubusercontent.com/u/1615979?v=3","gravatar_id":"","url":"https://api.github.com/users/Teradata","html_url":"https://github.com/Teradata","followers_url":"https://api.github.com/users/Teradata/followers","following_url":"https://api.github.com/users/Teradata/following{/other_user}","gists_url":"https://api.github.com/users/Teradata/gists{/gist_id}","starred_url":"https://api.github.com/users/Teradata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Teradata/subscriptions","organizations_url":"https://api.github.com/users/Teradata/orgs","repos_url":"https://api.github.com/users/Teradata/repos","events_url":"https://api.github.com/users/Teradata/events{/privacy}","received_events_url":"https://api.github.com/users/Teradata/received_events","type":"Organization","site_admin":false},"repo":{"id":36903368,"name":"presto","full_name":"Teradata/presto","owner":{"login":"Teradata","id":1615979,"avatar_url":"https://avatars.githubusercontent.com/u/1615979?v=3","gravatar_id":"","url":"https://api.github.com/users/Teradata","html_url":"https://github.com/Teradata","followers_url":"https://api.github.com/users/Teradata/followers","following_url":"https://api.github.com/users/Teradata/following{/other_user}","gists_url":"https://api.github.com/users/Teradata/gists{/gist_id}","starred_url":"https://api.github.com/users/Teradata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Teradata/subscriptions","organizations_url":"https://api.github.com/users/Teradata/orgs","repos_url":"https://api.github.com/users/Teradata/repos","events_url":"https://api.github.com/users/Teradata/events{/privacy}","received_events_url":"https://api.github.com/users/Teradata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Teradata/presto","description":"Teradata Distribution of Presto -- A Distributed SQL Query Engine for Big Data","fork":true,"url":"https://api.github.com/repos/Teradata/presto","forks_url":"https://api.github.com/repos/Teradata/presto/forks","keys_url":"https://api.github.com/repos/Teradata/presto/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Teradata/presto/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Teradata/presto/teams","hooks_url":"https://api.github.com/repos/Teradata/presto/hooks","issue_events_url":"https://api.github.com/repos/Teradata/presto/issues/events{/number}","events_url":"https://api.github.com/repos/Teradata/presto/events","assignees_url":"https://api.github.com/repos/Teradata/presto/assignees{/user}","branches_url":"https://api.github.com/repos/Teradata/presto/branches{/branch}","tags_url":"https://api.github.com/repos/Teradata/presto/tags","blobs_url":"https://api.github.com/repos/Teradata/presto/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Teradata/presto/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Teradata/presto/git/refs{/sha}","trees_url":"https://api.github.com/repos/Teradata/presto/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Teradata/presto/statuses/{sha}","languages_url":"https://api.github.com/repos/Teradata/presto/languages","stargazers_url":"https://api.github.com/repos/Teradata/presto/stargazers","contributors_url":"https://api.github.com/repos/Teradata/presto/contributors","subscribers_url":"https://api.github.com/repos/Teradata/presto/subscribers","subscription_url":"https://api.github.com/repos/Teradata/presto/subscription","commits_url":"https://api.github.com/repos/Teradata/presto/commits{/sha}","git_commits_url":"https://api.github.com/repos/Teradata/presto/git/commits{/sha}","comments_url":"https://api.github.com/repos/Teradata/presto/comments{/number}","issue_comment_url":"https://api.github.com/repos/Teradata/presto/issues/comments{/number}","contents_url":"https://api.github.com/repos/Teradata/presto/contents/{+path}","compare_url":"https://api.github.com/repos/Teradata/presto/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Teradata/presto/merges","archive_url":"https://api.github.com/repos/Teradata/presto/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Teradata/presto/downloads","issues_url":"https://api.github.com/repos/Teradata/presto/issues{/number}","pulls_url":"https://api.github.com/repos/Teradata/presto/pulls{/number}","milestones_url":"https://api.github.com/repos/Teradata/presto/milestones{/number}","notifications_url":"https://api.github.com/repos/Teradata/presto/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Teradata/presto/labels{/name}","releases_url":"https://api.github.com/repos/Teradata/presto/releases{/id}","deployments_url":"https://api.github.com/repos/Teradata/presto/deployments","created_at":"2015-06-05T00:38:37Z","updated_at":"2016-10-07T04:15:04Z","pushed_at":"2016-10-07T13:43:31Z","git_url":"git://github.com/Teradata/presto.git","ssh_url":"git@github.com:Teradata/presto.git","clone_url":"https://github.com/Teradata/presto.git","svn_url":"https://github.com/Teradata/presto","homepage":"http://www.teradata.com/presto","size":165280,"stargazers_count":58,"watchers_count":58,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":11,"mirror_url":null,"open_issues_count":18,"forks":11,"open_issues":18,"watchers":58,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Teradata/presto/pulls/366"},"html":{"href":"https://github.com/Teradata/presto/pull/366"},"issue":{"href":"https://api.github.com/repos/Teradata/presto/issues/366"},"comments":{"href":"https://api.github.com/repos/Teradata/presto/issues/366/comments"},"review_comments":{"href":"https://api.github.com/repos/Teradata/presto/pulls/366/comments"},"review_comment":{"href":"https://api.github.com/repos/Teradata/presto/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Teradata/presto/pulls/366/commits"},"statuses":{"href":"https://api.github.com/repos/Teradata/presto/statuses/cc737d2ef18b016b6ec7fde89f41e78b705ef8cb"}}}}
{"actor":{"display_login":"arhimondr"}}