{"action":"created","comment":{"url":"https://api.github.com/repos/prestodb/presto/pulls/comments/58602562","id":58602562,"diff_hunk":"@@ -0,0 +1,242 @@\n+/*\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may 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, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+package com.facebook.presto.server.remotetask;\n+\n+import com.facebook.presto.execution.StateMachine;\n+import com.facebook.presto.execution.TaskId;\n+import com.facebook.presto.execution.TaskStatus;\n+import com.facebook.presto.spi.PrestoException;\n+import com.google.common.util.concurrent.Futures;\n+import com.google.common.util.concurrent.ListenableFuture;\n+import io.airlift.concurrent.SetThreadName;\n+import io.airlift.http.client.FullJsonResponseHandler;\n+import io.airlift.http.client.HttpClient;\n+import io.airlift.http.client.Request;\n+import io.airlift.json.JsonCodec;\n+import io.airlift.log.Logger;\n+import io.airlift.units.Duration;\n+\n+import javax.annotation.concurrent.GuardedBy;\n+\n+import java.util.concurrent.CompletableFuture;\n+import java.util.concurrent.Executor;\n+import java.util.concurrent.ScheduledExecutorService;\n+import java.util.concurrent.atomic.AtomicBoolean;\n+\n+import static com.facebook.presto.client.PrestoHeaders.PRESTO_CURRENT_STATE;\n+import static com.facebook.presto.client.PrestoHeaders.PRESTO_MAX_WAIT;\n+import static com.facebook.presto.spi.StandardErrorCode.REMOTE_TASK_MISMATCH;\n+import static com.facebook.presto.util.Failures.REMOTE_TASK_MISMATCH_ERROR;\n+import static com.google.common.base.Strings.isNullOrEmpty;\n+import static com.google.common.net.HttpHeaders.CONTENT_TYPE;\n+import static com.google.common.net.MediaType.JSON_UTF_8;\n+import static io.airlift.http.client.FullJsonResponseHandler.createFullJsonResponseHandler;\n+import static io.airlift.http.client.HttpUriBuilder.uriBuilderFrom;\n+import static io.airlift.http.client.Request.Builder.prepareGet;\n+import static java.util.Objects.requireNonNull;\n+\n+class ContinuousTaskStatusFetcher\n+        implements SimpleHttpResponseCallback<TaskStatus>\n+{\n+    private static final Logger log = Logger.get(ContinuousTaskStatusFetcher.class);\n+\n+    private final TaskId taskId;\n+    private final HttpRemoteTask task;\n+    private final StateMachine<TaskStatus> taskStatus;\n+    private final JsonCodec<TaskStatus> taskStatusCodec;\n+\n+    private final Duration refreshMaxWait;\n+    private final Executor executor;\n+    private final HttpClient httpClient;\n+    private final RequestErrorTracker errorTracker;\n+\n+    @GuardedBy(\"this\")\n+    private boolean running;\n+\n+    @GuardedBy(\"this\")\n+    private ListenableFuture<FullJsonResponseHandler.JsonResponse<TaskStatus>> future;\n+\n+    public ContinuousTaskStatusFetcher(\n+            HttpRemoteTask task,\n+            TaskStatus initialTaskStatus,\n+            Duration refreshMaxWait,\n+            JsonCodec<TaskStatus> taskStatusCodec,\n+            Executor executor,\n+            HttpClient httpClient,\n+            Duration minErrorDuration,\n+            ScheduledExecutorService errorScheduledExecutor)\n+    {\n+        requireNonNull(initialTaskStatus, \"initialTaskStatus is null\");\n+\n+        this.task = requireNonNull(task, \"task is null\");\n+        this.taskId = initialTaskStatus.getTaskId();\n+        this.taskStatus = new StateMachine<>(\"task \" + taskId, executor, initialTaskStatus);\n+\n+        this.refreshMaxWait = requireNonNull(refreshMaxWait, \"refreshMaxWait is null\");\n+        this.taskStatusCodec = requireNonNull(taskStatusCodec, \"taskStatusCodec is null\");\n+\n+        this.executor = requireNonNull(executor, \"executor is null\");\n+        this.httpClient = requireNonNull(httpClient, \"httpClient is null\");\n+\n+        this.errorTracker = new RequestErrorTracker(taskId, initialTaskStatus.getSelf(), minErrorDuration, errorScheduledExecutor, \"getting task status\");\n+    }\n+\n+    public synchronized void start()\n+    {\n+        if (running) {\n+            // already running\n+            return;\n+        }\n+        running = true;\n+        scheduleNextRequest();\n+    }\n+\n+    public synchronized void stop()\n+    {\n+        running = false;\n+        if (future != null) {\n+            future.cancel(true);\n+            future = null;\n+        }\n+    }\n+\n+    private synchronized void scheduleNextRequest()\n+    {\n+        // stopped or done?\n+        TaskStatus taskStatus = getTaskStatus();\n+        if (!running || taskStatus.getState().isDone()) {\n+            return;\n+        }\n+\n+        // outstanding request?\n+        if (future != null && !future.isDone()) {\n+            // this should never happen\n+            log.error(\"Can not reschedule update because an update is already running\");\n+            return;\n+        }\n+\n+        // if throttled due to error, asynchronously wait for timeout and try again\n+        ListenableFuture<?> errorRateLimit = errorTracker.acquireRequestPermit();\n+        if (!errorRateLimit.isDone()) {\n+            errorRateLimit.addListener(this::scheduleNextRequest, executor);\n+            return;\n+        }\n+\n+        Request request = prepareGet()\n+                .setUri(uriBuilderFrom(taskStatus.getSelf()).appendPath(\"/status\").build())\n+                .setHeader(CONTENT_TYPE, JSON_UTF_8.toString())\n+                .setHeader(PRESTO_CURRENT_STATE, taskStatus.getState().toString())\n+                .setHeader(PRESTO_MAX_WAIT, refreshMaxWait.toString())\n+                .build();\n+\n+        errorTracker.startRequest();\n+        future = httpClient.executeAsync(request, createFullJsonResponseHandler(taskStatusCodec));\n+        Futures.addCallback(future, new SimpleHttpResponseHandler<>(this, request.getUri()), executor);\n+    }\n+\n+    TaskStatus getTaskStatus()\n+    {\n+        return taskStatus.get();\n+    }\n+\n+    @Override\n+    public void success(TaskStatus value)\n+    {\n+        try (SetThreadName ignored = new SetThreadName(\"ContinuousTaskInfoFetcher-%s\", taskId)) {\n+            try {\n+                updateTaskStatus(value);\n+                errorTracker.requestSucceeded();\n+            }\n+            finally {\n+                scheduleNextRequest();\n+            }\n+        }\n+    }\n+\n+    @Override\n+    public void failed(Throwable cause)\n+    {\n+        try (SetThreadName ignored = new SetThreadName(\"ContinuousTaskInfoFetcher-%s\", taskId)) {\n+            try {\n+                // if task not already done, record error\n+                TaskStatus taskStatus = getTaskStatus();\n+                if (!taskStatus.getState().isDone()) {\n+                    errorTracker.requestFailed(cause);\n+                }\n+            }\n+            catch (Error e) {\n+                task.failTask(e);\n+                throw e;\n+            }\n+            catch (RuntimeException e) {\n+                task.failTask(e);\n+            }\n+            finally {\n+                // there is no back off here so we can get a lot of error messages when a server spins\n+                // down, but it typically goes away quickly because the queries get canceled","path":"presto-main/src/main/java/com/facebook/presto/server/remotetask/ContinuousTaskStatusFetcher.java","position":187,"original_position":187,"commit_id":"9da8649bea4c809c5c7fc69bacafb56a5834df5b","original_commit_id":"9da8649bea4c809c5c7fc69bacafb56a5834df5b","user":{"login":"dain","id":58298,"avatar_url":"https://avatars.githubusercontent.com/u/58298?v=3","gravatar_id":"","url":"https://api.github.com/users/dain","html_url":"https://github.com/dain","followers_url":"https://api.github.com/users/dain/followers","following_url":"https://api.github.com/users/dain/following{/other_user}","gists_url":"https://api.github.com/users/dain/gists{/gist_id}","starred_url":"https://api.github.com/users/dain/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dain/subscriptions","organizations_url":"https://api.github.com/users/dain/orgs","repos_url":"https://api.github.com/users/dain/repos","events_url":"https://api.github.com/users/dain/events{/privacy}","received_events_url":"https://api.github.com/users/dain/received_events","type":"User","site_admin":false},"body":"I don't think this isn't true anymore.  The error tracker will be ticked if the task is still running and will get back off.  In the other cases, the task is failed or finished, so the request doesn't get scheduled.","created_at":"2016-04-05T19:42:06Z","updated_at":"2016-04-05T19:42:06Z","html_url":"https://github.com/prestodb/presto/pull/4941#discussion_r58602562","pull_request_url":"https://api.github.com/repos/prestodb/presto/pulls/4941","_links":{"self":{"href":"https://api.github.com/repos/prestodb/presto/pulls/comments/58602562"},"html":{"href":"https://github.com/prestodb/presto/pull/4941#discussion_r58602562"},"pull_request":{"href":"https://api.github.com/repos/prestodb/presto/pulls/4941"}}},"pull_request":{"url":"https://api.github.com/repos/prestodb/presto/pulls/4941","id":65312524,"html_url":"https://github.com/prestodb/presto/pull/4941","diff_url":"https://github.com/prestodb/presto/pull/4941.diff","patch_url":"https://github.com/prestodb/presto/pull/4941.patch","issue_url":"https://api.github.com/repos/prestodb/presto/issues/4941","number":4941,"state":"open","locked":false,"title":"Update HttpRemoteTask to fetch only task status continuously","user":{"login":"nileema","id":1034143,"avatar_url":"https://avatars.githubusercontent.com/u/1034143?v=3","gravatar_id":"","url":"https://api.github.com/users/nileema","html_url":"https://github.com/nileema","followers_url":"https://api.github.com/users/nileema/followers","following_url":"https://api.github.com/users/nileema/following{/other_user}","gists_url":"https://api.github.com/users/nileema/gists{/gist_id}","starred_url":"https://api.github.com/users/nileema/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nileema/subscriptions","organizations_url":"https://api.github.com/users/nileema/orgs","repos_url":"https://api.github.com/users/nileema/repos","events_url":"https://api.github.com/users/nileema/events{/privacy}","received_events_url":"https://api.github.com/users/nileema/received_events","type":"User","site_admin":false},"body":"Earlier, the remote task had a ContinuousTaskInfoFetcher, which would\r\ncontinuously ask each task for it's info object which could be huge. For\r\na coordinator that is managing hundreds of tasks the json\r\ndeserialization can be quite an overhead.\r\n\r\nExtract the TaskStatus from the TaskInfo object and only update the\r\nTaskStatus continuously. TaskStatus will propogate any state changes\r\nin the task as quickly as possible. TaskInfo is updated independently\r\nand used only for stats. The rate at which TaskInfo is updated is configurable.","created_at":"2016-04-05T14:19:56Z","updated_at":"2016-04-05T19:42:06Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6ad0dd7241a5a79f66d4ad9d2863366a3b3ee871","assignee":{"login":"dain","id":58298,"avatar_url":"https://avatars.githubusercontent.com/u/58298?v=3","gravatar_id":"","url":"https://api.github.com/users/dain","html_url":"https://github.com/dain","followers_url":"https://api.github.com/users/dain/followers","following_url":"https://api.github.com/users/dain/following{/other_user}","gists_url":"https://api.github.com/users/dain/gists{/gist_id}","starred_url":"https://api.github.com/users/dain/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dain/subscriptions","organizations_url":"https://api.github.com/users/dain/orgs","repos_url":"https://api.github.com/users/dain/repos","events_url":"https://api.github.com/users/dain/events{/privacy}","received_events_url":"https://api.github.com/users/dain/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/prestodb/presto/pulls/4941/commits","review_comments_url":"https://api.github.com/repos/prestodb/presto/pulls/4941/comments","review_comment_url":"https://api.github.com/repos/prestodb/presto/pulls/comments{/number}","comments_url":"https://api.github.com/repos/prestodb/presto/issues/4941/comments","statuses_url":"https://api.github.com/repos/prestodb/presto/statuses/9da8649bea4c809c5c7fc69bacafb56a5834df5b","head":{"label":"nileema:remote-task-refactor","ref":"remote-task-refactor","sha":"9da8649bea4c809c5c7fc69bacafb56a5834df5b","user":{"login":"nileema","id":1034143,"avatar_url":"https://avatars.githubusercontent.com/u/1034143?v=3","gravatar_id":"","url":"https://api.github.com/users/nileema","html_url":"https://github.com/nileema","followers_url":"https://api.github.com/users/nileema/followers","following_url":"https://api.github.com/users/nileema/following{/other_user}","gists_url":"https://api.github.com/users/nileema/gists{/gist_id}","starred_url":"https://api.github.com/users/nileema/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nileema/subscriptions","organizations_url":"https://api.github.com/users/nileema/orgs","repos_url":"https://api.github.com/users/nileema/repos","events_url":"https://api.github.com/users/nileema/events{/privacy}","received_events_url":"https://api.github.com/users/nileema/received_events","type":"User","site_admin":false},"repo":{"id":12574341,"name":"presto","full_name":"nileema/presto","owner":{"login":"nileema","id":1034143,"avatar_url":"https://avatars.githubusercontent.com/u/1034143?v=3","gravatar_id":"","url":"https://api.github.com/users/nileema","html_url":"https://github.com/nileema","followers_url":"https://api.github.com/users/nileema/followers","following_url":"https://api.github.com/users/nileema/following{/other_user}","gists_url":"https://api.github.com/users/nileema/gists{/gist_id}","starred_url":"https://api.github.com/users/nileema/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nileema/subscriptions","organizations_url":"https://api.github.com/users/nileema/orgs","repos_url":"https://api.github.com/users/nileema/repos","events_url":"https://api.github.com/users/nileema/events{/privacy}","received_events_url":"https://api.github.com/users/nileema/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/nileema/presto","description":"","fork":true,"url":"https://api.github.com/repos/nileema/presto","forks_url":"https://api.github.com/repos/nileema/presto/forks","keys_url":"https://api.github.com/repos/nileema/presto/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nileema/presto/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nileema/presto/teams","hooks_url":"https://api.github.com/repos/nileema/presto/hooks","issue_events_url":"https://api.github.com/repos/nileema/presto/issues/events{/number}","events_url":"https://api.github.com/repos/nileema/presto/events","assignees_url":"https://api.github.com/repos/nileema/presto/assignees{/user}","branches_url":"https://api.github.com/repos/nileema/presto/branches{/branch}","tags_url":"https://api.github.com/repos/nileema/presto/tags","blobs_url":"https://api.github.com/repos/nileema/presto/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nileema/presto/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nileema/presto/git/refs{/sha}","trees_url":"https://api.github.com/repos/nileema/presto/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nileema/presto/statuses/{sha}","languages_url":"https://api.github.com/repos/nileema/presto/languages","stargazers_url":"https://api.github.com/repos/nileema/presto/stargazers","contributors_url":"https://api.github.com/repos/nileema/presto/contributors","subscribers_url":"https://api.github.com/repos/nileema/presto/subscribers","subscription_url":"https://api.github.com/repos/nileema/presto/subscription","commits_url":"https://api.github.com/repos/nileema/presto/commits{/sha}","git_commits_url":"https://api.github.com/repos/nileema/presto/git/commits{/sha}","comments_url":"https://api.github.com/repos/nileema/presto/comments{/number}","issue_comment_url":"https://api.github.com/repos/nileema/presto/issues/comments{/number}","contents_url":"https://api.github.com/repos/nileema/presto/contents/{+path}","compare_url":"https://api.github.com/repos/nileema/presto/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nileema/presto/merges","archive_url":"https://api.github.com/repos/nileema/presto/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nileema/presto/downloads","issues_url":"https://api.github.com/repos/nileema/presto/issues{/number}","pulls_url":"https://api.github.com/repos/nileema/presto/pulls{/number}","milestones_url":"https://api.github.com/repos/nileema/presto/milestones{/number}","notifications_url":"https://api.github.com/repos/nileema/presto/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nileema/presto/labels{/name}","releases_url":"https://api.github.com/repos/nileema/presto/releases{/id}","deployments_url":"https://api.github.com/repos/nileema/presto/deployments","created_at":"2013-09-03T20:40:19Z","updated_at":"2014-08-13T18:36:54Z","pushed_at":"2016-04-05T18:41:47Z","git_url":"git://github.com/nileema/presto.git","ssh_url":"git@github.com:nileema/presto.git","clone_url":"https://github.com/nileema/presto.git","svn_url":"https://github.com/nileema/presto","homepage":null,"size":53428,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"prestodb:master","ref":"master","sha":"83430b4584c9eac2409734232bb9745dec1b9cf8","user":{"login":"prestodb","id":6882181,"avatar_url":"https://avatars.githubusercontent.com/u/6882181?v=3","gravatar_id":"","url":"https://api.github.com/users/prestodb","html_url":"https://github.com/prestodb","followers_url":"https://api.github.com/users/prestodb/followers","following_url":"https://api.github.com/users/prestodb/following{/other_user}","gists_url":"https://api.github.com/users/prestodb/gists{/gist_id}","starred_url":"https://api.github.com/users/prestodb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/prestodb/subscriptions","organizations_url":"https://api.github.com/users/prestodb/orgs","repos_url":"https://api.github.com/users/prestodb/repos","events_url":"https://api.github.com/users/prestodb/events{/privacy}","received_events_url":"https://api.github.com/users/prestodb/received_events","type":"Organization","site_admin":false},"repo":{"id":5349565,"name":"presto","full_name":"prestodb/presto","owner":{"login":"prestodb","id":6882181,"avatar_url":"https://avatars.githubusercontent.com/u/6882181?v=3","gravatar_id":"","url":"https://api.github.com/users/prestodb","html_url":"https://github.com/prestodb","followers_url":"https://api.github.com/users/prestodb/followers","following_url":"https://api.github.com/users/prestodb/following{/other_user}","gists_url":"https://api.github.com/users/prestodb/gists{/gist_id}","starred_url":"https://api.github.com/users/prestodb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/prestodb/subscriptions","organizations_url":"https://api.github.com/users/prestodb/orgs","repos_url":"https://api.github.com/users/prestodb/repos","events_url":"https://api.github.com/users/prestodb/events{/privacy}","received_events_url":"https://api.github.com/users/prestodb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/prestodb/presto","description":"Distributed SQL query engine for big data","fork":false,"url":"https://api.github.com/repos/prestodb/presto","forks_url":"https://api.github.com/repos/prestodb/presto/forks","keys_url":"https://api.github.com/repos/prestodb/presto/keys{/key_id}","collaborators_url":"https://api.github.com/repos/prestodb/presto/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/prestodb/presto/teams","hooks_url":"https://api.github.com/repos/prestodb/presto/hooks","issue_events_url":"https://api.github.com/repos/prestodb/presto/issues/events{/number}","events_url":"https://api.github.com/repos/prestodb/presto/events","assignees_url":"https://api.github.com/repos/prestodb/presto/assignees{/user}","branches_url":"https://api.github.com/repos/prestodb/presto/branches{/branch}","tags_url":"https://api.github.com/repos/prestodb/presto/tags","blobs_url":"https://api.github.com/repos/prestodb/presto/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/prestodb/presto/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/prestodb/presto/git/refs{/sha}","trees_url":"https://api.github.com/repos/prestodb/presto/git/trees{/sha}","statuses_url":"https://api.github.com/repos/prestodb/presto/statuses/{sha}","languages_url":"https://api.github.com/repos/prestodb/presto/languages","stargazers_url":"https://api.github.com/repos/prestodb/presto/stargazers","contributors_url":"https://api.github.com/repos/prestodb/presto/contributors","subscribers_url":"https://api.github.com/repos/prestodb/presto/subscribers","subscription_url":"https://api.github.com/repos/prestodb/presto/subscription","commits_url":"https://api.github.com/repos/prestodb/presto/commits{/sha}","git_commits_url":"https://api.github.com/repos/prestodb/presto/git/commits{/sha}","comments_url":"https://api.github.com/repos/prestodb/presto/comments{/number}","issue_comment_url":"https://api.github.com/repos/prestodb/presto/issues/comments{/number}","contents_url":"https://api.github.com/repos/prestodb/presto/contents/{+path}","compare_url":"https://api.github.com/repos/prestodb/presto/compare/{base}...{head}","merges_url":"https://api.github.com/repos/prestodb/presto/merges","archive_url":"https://api.github.com/repos/prestodb/presto/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/prestodb/presto/downloads","issues_url":"https://api.github.com/repos/prestodb/presto/issues{/number}","pulls_url":"https://api.github.com/repos/prestodb/presto/pulls{/number}","milestones_url":"https://api.github.com/repos/prestodb/presto/milestones{/number}","notifications_url":"https://api.github.com/repos/prestodb/presto/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/prestodb/presto/labels{/name}","releases_url":"https://api.github.com/repos/prestodb/presto/releases{/id}","deployments_url":"https://api.github.com/repos/prestodb/presto/deployments","created_at":"2012-08-09T01:03:37Z","updated_at":"2016-04-05T16:52:13Z","pushed_at":"2016-04-05T19:41:46Z","git_url":"git://github.com/prestodb/presto.git","ssh_url":"git@github.com:prestodb/presto.git","clone_url":"https://github.com/prestodb/presto.git","svn_url":"https://github.com/prestodb/presto","homepage":"https://prestodb.io","size":145815,"stargazers_count":4630,"watchers_count":4630,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1337,"mirror_url":null,"open_issues_count":560,"forks":1337,"open_issues":560,"watchers":4630,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/prestodb/presto/pulls/4941"},"html":{"href":"https://github.com/prestodb/presto/pull/4941"},"issue":{"href":"https://api.github.com/repos/prestodb/presto/issues/4941"},"comments":{"href":"https://api.github.com/repos/prestodb/presto/issues/4941/comments"},"review_comments":{"href":"https://api.github.com/repos/prestodb/presto/pulls/4941/comments"},"review_comment":{"href":"https://api.github.com/repos/prestodb/presto/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/prestodb/presto/pulls/4941/commits"},"statuses":{"href":"https://api.github.com/repos/prestodb/presto/statuses/9da8649bea4c809c5c7fc69bacafb56a5834df5b"}}}}