{"action":"created","comment":{"url":"https://api.github.com/repos/grpc/grpc-java/pulls/comments/59202498","id":59202498,"diff_hunk":"@@ -0,0 +1,522 @@\n+/*\n+ * Copyright 2016, Google Inc. All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or without\n+ * modification, are permitted provided that the following conditions are\n+ * met:\n+ *\n+ *    * Redistributions of source code must retain the above copyright\n+ * notice, this list of conditions and the following disclaimer.\n+ *    * Redistributions in binary form must reproduce the above\n+ * copyright notice, this list of conditions and the following disclaimer\n+ * in the documentation and/or other materials provided with the\n+ * distribution.\n+ *\n+ *    * Neither the name of Google Inc. nor the names of its\n+ * contributors may be used to endorse or promote products derived from\n+ * this software without specific prior written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n+ * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package io.grpc.benchmarks.driver;\n+\n+import com.sun.management.OperatingSystemMXBean;\n+\n+import io.grpc.CallOptions;\n+import io.grpc.Channel;\n+import io.grpc.ClientCall;\n+import io.grpc.ManagedChannel;\n+import io.grpc.Metadata;\n+import io.grpc.Status;\n+import io.grpc.benchmarks.Transport;\n+import io.grpc.benchmarks.Utils;\n+import io.grpc.benchmarks.proto.BenchmarkServiceGrpc;\n+import io.grpc.benchmarks.proto.Control;\n+import io.grpc.benchmarks.proto.Messages;\n+import io.grpc.benchmarks.proto.Payloads;\n+import io.grpc.benchmarks.proto.Stats;\n+import io.grpc.internal.ManagedChannelImpl;\n+import io.grpc.stub.ClientCalls;\n+import io.grpc.stub.StreamObserver;\n+import io.netty.buffer.ByteBuf;\n+import io.netty.buffer.PooledByteBufAllocator;\n+import io.netty.channel.epoll.Epoll;\n+import io.netty.util.concurrent.DefaultThreadFactory;\n+\n+import org.HdrHistogram.Histogram;\n+import org.HdrHistogram.LogarithmicIterator;\n+import org.HdrHistogram.Recorder;\n+import org.apache.commons.math3.distribution.AbstractIntegerDistribution;\n+import org.apache.commons.math3.distribution.EnumeratedIntegerDistribution;\n+import org.apache.commons.math3.distribution.PoissonDistribution;\n+\n+import java.lang.management.ManagementFactory;\n+\n+import java.util.List;\n+import java.util.concurrent.ExecutorService;\n+import java.util.concurrent.Executors;\n+import java.util.concurrent.Semaphore;\n+import java.util.concurrent.TimeUnit;\n+import java.util.concurrent.atomic.AtomicReference;\n+import java.util.concurrent.locks.LockSupport;\n+import java.util.logging.Level;\n+import java.util.logging.Logger;\n+\n+/**\n+ * Implements the client-side contract for the load testing scenarios.\n+ */\n+class LoadClient {\n+\n+  private static final Logger LOG = Logger.getLogger(LoadClient.class.getName());\n+  private ByteBuf genericRequest;\n+\n+  private final Control.ClientConfig config;\n+  private final AbstractIntegerDistribution distribution;\n+  private volatile boolean shutdown;\n+  private final int threadCount;\n+\n+  ManagedChannel[] channels;\n+  BenchmarkServiceGrpc.BenchmarkServiceBlockingStub[] blockingStubs;\n+  BenchmarkServiceGrpc.BenchmarkServiceStub[] asyncStubs;\n+  Recorder recorder;\n+  private ExecutorService fixedThreadPool;\n+  private Messages.SimpleRequest simpleRequest;\n+  private final OperatingSystemMXBean osBean;\n+  private long lastMarkCpuTime;\n+\n+  LoadClient(Control.ClientConfig config) throws Exception {\n+    LOG.log(Level.INFO, \"Client Config \\n\" + config.toString());\n+    this.config = config;\n+    // Create the channels\n+    channels = new ManagedChannelImpl[config.getClientChannels()];\n+    for (int i = 0; i < config.getClientChannels(); i++) {\n+      channels[i] =\n+          Utils.newClientChannel(\n+              Epoll.isAvailable() ?  Transport.NETTY_EPOLL : Transport.NETTY_NIO,\n+              Utils.parseSocketAddress(config.getServerTargets(i % config.getServerTargetsCount())),\n+              config.hasSecurityParams(),\n+              config.hasSecurityParams() && config.getSecurityParams().getUseTestCa(),\n+              config.hasSecurityParams()\n+                  ? config.getSecurityParams().getServerHostOverride() :\n+                  null,\n+              true,\n+              Utils.DEFAULT_FLOW_CONTROL_WINDOW,\n+              false);\n+    }\n+\n+    // Create a stub per channel\n+    if (config.getClientType() == Control.ClientType.ASYNC_CLIENT) {\n+      asyncStubs = new BenchmarkServiceGrpc.BenchmarkServiceStub[channels.length];\n+      for (int i = 0; i < channels.length; i++) {\n+        asyncStubs[i] = BenchmarkServiceGrpc.newStub(channels[i]);\n+      }\n+    } else {\n+      blockingStubs = new BenchmarkServiceGrpc.BenchmarkServiceBlockingStub[channels.length];\n+      for (int i = 0; i < channels.length; i++) {\n+        blockingStubs[i] = BenchmarkServiceGrpc.newBlockingStub(channels[i]);\n+      }\n+    }\n+\n+    // Determine no of threads\n+    if (config.getClientType() == Control.ClientType.SYNC_CLIENT) {\n+      threadCount = config.getOutstandingRpcsPerChannel() * config.getClientChannels();\n+    } else {\n+      threadCount = config.getAsyncClientThreads() == 0\n+          ? Runtime.getRuntime().availableProcessors()\n+          : config.getAsyncClientThreads();\n+    }\n+    // Use a fixed sized pool of daemon threads.\n+    fixedThreadPool = Executors.newFixedThreadPool(threadCount,\n+        new DefaultThreadFactory(\"client-worker\", true));\n+\n+    // Create the load distribution\n+    if (config.getLoadParams().getClosedLoop() != null) {\n+      distribution = new EnumeratedIntegerDistribution(new int[]{0}, new double[]{1});\n+    } else if (config.getLoadParams().getPoisson() != null) {\n+      distribution =  new PoissonDistribution(config.getLoadParams().getPoisson().getOfferedLoad()\n+          / threadCount);\n+    } else  {\n+      throw new UnsupportedOperationException();\n+    }\n+\n+    // Create payloads\n+    switch (config.getPayloadConfig().getPayloadCase()) {\n+      case SIMPLE_PARAMS: {\n+        Payloads.SimpleProtoParams simpleParams = config.getPayloadConfig().getSimpleParams();\n+        simpleRequest = Utils.makeRequest(Messages.PayloadType.COMPRESSABLE,\n+            simpleParams.getReqSize(), simpleParams.getRespSize());\n+        break;\n+      }\n+      case BYTEBUF_PARAMS: {\n+        PooledByteBufAllocator alloc = PooledByteBufAllocator.DEFAULT;\n+        genericRequest = alloc.buffer(config.getPayloadConfig().getBytebufParams().getRespSize());\n+        if (genericRequest.capacity() > 0) {\n+          genericRequest.writerIndex(genericRequest.capacity() - 1);\n+        }\n+        break;\n+      }\n+      default: {\n+        // Not implemented yet\n+        throw new IllegalArgumentException(\"Scenario not implemented\");\n+      }\n+    }\n+\n+    List<OperatingSystemMXBean> beans =\n+        ManagementFactory.getPlatformMXBeans(OperatingSystemMXBean.class);\n+    if (!beans.isEmpty()) {\n+      osBean = beans.get(0);\n+    } else {\n+      osBean = null;\n+    }\n+\n+    // Create the histogram recorder\n+    recorder = new Recorder((long) config.getHistogramParams().getMaxPossible(), 3);\n+  }\n+\n+  /**\n+   * Start the load scenario.\n+   */\n+  void start() {\n+    Runnable r;\n+    for (int i = 0; i < threadCount; i++) {\n+      r = null;\n+      switch (config.getPayloadConfig().getPayloadCase()) {\n+        case SIMPLE_PARAMS: {\n+          if (config.getClientType() == Control.ClientType.SYNC_CLIENT) {\n+            if (config.getRpcType() == Control.RpcType.UNARY) {\n+              r = new BlockingUnaryWorker(blockingStubs[i % blockingStubs.length]);\n+            }\n+          } else if (config.getClientType() == Control.ClientType.ASYNC_CLIENT) {\n+            if (config.getRpcType() == Control.RpcType.UNARY) {\n+              r = new AsyncUnaryWorker(asyncStubs[i % asyncStubs.length]);\n+            } else if (config.getRpcType() == Control.RpcType.STREAMING) {\n+              r = new AsyncPingPongWorker(asyncStubs[i % asyncStubs.length]);\n+            }\n+          }\n+          break;\n+        }\n+        case BYTEBUF_PARAMS: {\n+          if (config.getClientType() == Control.ClientType.SYNC_CLIENT) {\n+            if (config.getRpcType() == Control.RpcType.UNARY) {\n+              r = new GenericBlockingUnaryWorker(channels[i % channels.length]);\n+            }\n+          } else if (config.getClientType() == Control.ClientType.ASYNC_CLIENT) {\n+            if (config.getRpcType() == Control.RpcType.UNARY) {\n+              r = new GenericAsyncUnaryWorker(channels[i % channels.length]);\n+            } else if (config.getRpcType() == Control.RpcType.STREAMING) {\n+              r = new GenericAsyncPingPongWorker(channels[i % channels.length]);\n+            }\n+          }\n+\n+          break;\n+        }\n+        default: {\n+          throw Status.UNIMPLEMENTED.asRuntimeException();\n+        }\n+      }\n+      if (r == null) {\n+        throw new IllegalStateException(config.getRpcType().name()\n+            + \" not supported for client type \"\n+            + config.getClientType());\n+      }\n+      fixedThreadPool.execute(r);\n+    }\n+    if (osBean != null) {\n+      lastMarkCpuTime = osBean.getProcessCpuTime();\n+    }\n+  }\n+\n+  /**\n+   * Take a snapshot of the statistics which can be returned to the driver.\n+   */\n+  Stats.ClientStats getStats() {\n+    Histogram intervalHistogram = recorder.getIntervalHistogram();\n+\n+    Stats.ClientStats.Builder statsBuilder = Stats.ClientStats.newBuilder();\n+    Stats.HistogramData.Builder latenciesBuilder = statsBuilder.getLatenciesBuilder();\n+    double resolution = 1.0 + Math.max(config.getHistogramParams().getResolution(), 0.01);\n+    LogarithmicIterator logIterator = new LogarithmicIterator(intervalHistogram, 1,\n+        resolution);\n+    double base = 1;\n+    while (logIterator.hasNext()) {\n+      latenciesBuilder.addBucket((int) logIterator.next().getCountAddedInThisIterationStep());\n+      base = base * resolution;\n+    }\n+    // Driver expects values for all buckets in the range, not just the range of buckets that\n+    // have values.\n+    while (base < config.getHistogramParams().getMaxPossible()) {\n+      latenciesBuilder.addBucket(0);\n+      base = base * resolution;\n+    }\n+    latenciesBuilder.setMaxSeen((double) intervalHistogram.getMaxValue());\n+    latenciesBuilder.setMinSeen((double) intervalHistogram.getMinNonZeroValue());\n+    latenciesBuilder.setCount(intervalHistogram.getTotalCount());\n+    latenciesBuilder.setSum(intervalHistogram.getMean()\n+        * intervalHistogram.getTotalCount());\n+    // TODO: No support for sum of squares\n+\n+    statsBuilder.setTimeElapsed(((double)(intervalHistogram.getEndTimeStamp()\n+        - intervalHistogram.getStartTimeStamp())) / 1000.0);\n+    if (osBean != null) {\n+      // Report all the CPU time as user-time  (which is intentionally incorrect)\n+      long nowCpu = osBean.getProcessCpuTime();\n+      statsBuilder.setTimeUser(((double) nowCpu - lastMarkCpuTime) / 1000000000.0);\n+      lastMarkCpuTime = nowCpu;\n+    }\n+    return statsBuilder.build();\n+  }\n+\n+  /**\n+   * Shutdown the scenario as cleanly as possible.\n+   */\n+  void shutdownNow() {\n+    shutdown = true;\n+    for (int i = 0; i < channels.length; i++) {\n+      // Initiate channel shutdown\n+      channels[i].shutdown();\n+    }\n+    for (int i = 0; i < channels.length; i++) {\n+      try {\n+        // Wait for channel termination\n+        channels[i].awaitTermination(1, TimeUnit.SECONDS);\n+      } catch (InterruptedException ie) {\n+        channels[i].shutdownNow();\n+      }\n+    }\n+    fixedThreadPool.shutdownNow();\n+  }\n+\n+  /**\n+   * Record the event elapsed time to the histogram and delay initiation of the next event based\n+   * on the load distribution.\n+   */\n+  private void delay(long alreadyElapsed) {\n+    long nextPermitted = distribution.sample();\n+    recorder.recordValue(alreadyElapsed);\n+    if (nextPermitted > alreadyElapsed) {\n+      LockSupport.parkNanos(nextPermitted - alreadyElapsed);\n+    }\n+  }\n+\n+  /**\n+   * Worker which executes blocking unary calls. Event timing is the duration between sending the\n+   * request and receiving the response.\n+   */\n+  class BlockingUnaryWorker implements Runnable {\n+    final BenchmarkServiceGrpc.BenchmarkServiceBlockingStub stub;\n+\n+    private BlockingUnaryWorker(BenchmarkServiceGrpc.BenchmarkServiceBlockingStub stub) {\n+      this.stub = stub;\n+    }\n+\n+    public void run() {\n+      while (!shutdown) {\n+        long now = System.nanoTime();\n+        stub.unaryCall(simpleRequest);\n+        delay(System.nanoTime() - now);","path":"benchmarks/src/main/java/io/grpc/benchmarks/driver/LoadClient.java","position":327,"original_position":327,"commit_id":"71267ba12315f58c0076e1ffd4e647a2d6b939ff","original_commit_id":"71267ba12315f58c0076e1ffd4e647a2d6b939ff","user":{"login":"buchgr","id":1388179,"avatar_url":"https://avatars.githubusercontent.com/u/1388179?v=3","gravatar_id":"","url":"https://api.github.com/users/buchgr","html_url":"https://github.com/buchgr","followers_url":"https://api.github.com/users/buchgr/followers","following_url":"https://api.github.com/users/buchgr/following{/other_user}","gists_url":"https://api.github.com/users/buchgr/gists{/gist_id}","starred_url":"https://api.github.com/users/buchgr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/buchgr/subscriptions","organizations_url":"https://api.github.com/users/buchgr/orgs","repos_url":"https://api.github.com/users/buchgr/repos","events_url":"https://api.github.com/users/buchgr/events{/privacy}","received_events_url":"https://api.github.com/users/buchgr/received_events","type":"User","site_admin":false},"body":"this might be problematic, as chances are that the `delay` is much smaller than the latency of the blocking call. Not sure whether delay is useful here.","created_at":"2016-04-11T13:24:24Z","updated_at":"2016-04-11T13:24:24Z","html_url":"https://github.com/grpc/grpc-java/pull/1583#discussion_r59202498","pull_request_url":"https://api.github.com/repos/grpc/grpc-java/pulls/1583","_links":{"self":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/comments/59202498"},"html":{"href":"https://github.com/grpc/grpc-java/pull/1583#discussion_r59202498"},"pull_request":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/1583"}}},"pull_request":{"url":"https://api.github.com/repos/grpc/grpc-java/pulls/1583","id":63946764,"html_url":"https://github.com/grpc/grpc-java/pull/1583","diff_url":"https://github.com/grpc/grpc-java/pull/1583.diff","patch_url":"https://github.com/grpc/grpc-java/pull/1583.patch","issue_url":"https://api.github.com/repos/grpc/grpc-java/issues/1583","number":1583,"state":"open","locked":false,"title":"Implement the load worker for continuous benchmarking","user":{"login":"louiscryan","id":9046588,"avatar_url":"https://avatars.githubusercontent.com/u/9046588?v=3","gravatar_id":"","url":"https://api.github.com/users/louiscryan","html_url":"https://github.com/louiscryan","followers_url":"https://api.github.com/users/louiscryan/followers","following_url":"https://api.github.com/users/louiscryan/following{/other_user}","gists_url":"https://api.github.com/users/louiscryan/gists{/gist_id}","starred_url":"https://api.github.com/users/louiscryan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/louiscryan/subscriptions","organizations_url":"https://api.github.com/users/louiscryan/orgs","repos_url":"https://api.github.com/users/louiscryan/repos","events_url":"https://api.github.com/users/louiscryan/events{/privacy}","received_events_url":"https://api.github.com/users/louiscryan/received_events","type":"User","site_admin":false},"body":"Implement the load worker that can receive control events from the load driver and initiate load testing scenarios.\r\n\r\nWill be used for GRPC's continuous load testing process.\r\nCurrently only implements async-unary, blocking-unary & streaming-ping-pong","created_at":"2016-03-23T20:23:12Z","updated_at":"2016-04-11T13:24:24Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c2fac54a23330e691c541bfdb4af39d8266fb0f0","assignee":{"login":"buchgr","id":1388179,"avatar_url":"https://avatars.githubusercontent.com/u/1388179?v=3","gravatar_id":"","url":"https://api.github.com/users/buchgr","html_url":"https://github.com/buchgr","followers_url":"https://api.github.com/users/buchgr/followers","following_url":"https://api.github.com/users/buchgr/following{/other_user}","gists_url":"https://api.github.com/users/buchgr/gists{/gist_id}","starred_url":"https://api.github.com/users/buchgr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/buchgr/subscriptions","organizations_url":"https://api.github.com/users/buchgr/orgs","repos_url":"https://api.github.com/users/buchgr/repos","events_url":"https://api.github.com/users/buchgr/events{/privacy}","received_events_url":"https://api.github.com/users/buchgr/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/grpc/grpc-java/pulls/1583/commits","review_comments_url":"https://api.github.com/repos/grpc/grpc-java/pulls/1583/comments","review_comment_url":"https://api.github.com/repos/grpc/grpc-java/pulls/comments{/number}","comments_url":"https://api.github.com/repos/grpc/grpc-java/issues/1583/comments","statuses_url":"https://api.github.com/repos/grpc/grpc-java/statuses/71267ba12315f58c0076e1ffd4e647a2d6b939ff","head":{"label":"louiscryan:BenchmarksAndDrivers","ref":"BenchmarksAndDrivers","sha":"71267ba12315f58c0076e1ffd4e647a2d6b939ff","user":{"login":"louiscryan","id":9046588,"avatar_url":"https://avatars.githubusercontent.com/u/9046588?v=3","gravatar_id":"","url":"https://api.github.com/users/louiscryan","html_url":"https://github.com/louiscryan","followers_url":"https://api.github.com/users/louiscryan/followers","following_url":"https://api.github.com/users/louiscryan/following{/other_user}","gists_url":"https://api.github.com/users/louiscryan/gists{/gist_id}","starred_url":"https://api.github.com/users/louiscryan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/louiscryan/subscriptions","organizations_url":"https://api.github.com/users/louiscryan/orgs","repos_url":"https://api.github.com/users/louiscryan/repos","events_url":"https://api.github.com/users/louiscryan/events{/privacy}","received_events_url":"https://api.github.com/users/louiscryan/received_events","type":"User","site_admin":false},"repo":{"id":31382190,"name":"grpc-java","full_name":"louiscryan/grpc-java","owner":{"login":"louiscryan","id":9046588,"avatar_url":"https://avatars.githubusercontent.com/u/9046588?v=3","gravatar_id":"","url":"https://api.github.com/users/louiscryan","html_url":"https://github.com/louiscryan","followers_url":"https://api.github.com/users/louiscryan/followers","following_url":"https://api.github.com/users/louiscryan/following{/other_user}","gists_url":"https://api.github.com/users/louiscryan/gists{/gist_id}","starred_url":"https://api.github.com/users/louiscryan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/louiscryan/subscriptions","organizations_url":"https://api.github.com/users/louiscryan/orgs","repos_url":"https://api.github.com/users/louiscryan/repos","events_url":"https://api.github.com/users/louiscryan/events{/privacy}","received_events_url":"https://api.github.com/users/louiscryan/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/louiscryan/grpc-java","description":"The Java gRPC implementation. HTTP/2 based RPC","fork":true,"url":"https://api.github.com/repos/louiscryan/grpc-java","forks_url":"https://api.github.com/repos/louiscryan/grpc-java/forks","keys_url":"https://api.github.com/repos/louiscryan/grpc-java/keys{/key_id}","collaborators_url":"https://api.github.com/repos/louiscryan/grpc-java/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/louiscryan/grpc-java/teams","hooks_url":"https://api.github.com/repos/louiscryan/grpc-java/hooks","issue_events_url":"https://api.github.com/repos/louiscryan/grpc-java/issues/events{/number}","events_url":"https://api.github.com/repos/louiscryan/grpc-java/events","assignees_url":"https://api.github.com/repos/louiscryan/grpc-java/assignees{/user}","branches_url":"https://api.github.com/repos/louiscryan/grpc-java/branches{/branch}","tags_url":"https://api.github.com/repos/louiscryan/grpc-java/tags","blobs_url":"https://api.github.com/repos/louiscryan/grpc-java/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/louiscryan/grpc-java/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/louiscryan/grpc-java/git/refs{/sha}","trees_url":"https://api.github.com/repos/louiscryan/grpc-java/git/trees{/sha}","statuses_url":"https://api.github.com/repos/louiscryan/grpc-java/statuses/{sha}","languages_url":"https://api.github.com/repos/louiscryan/grpc-java/languages","stargazers_url":"https://api.github.com/repos/louiscryan/grpc-java/stargazers","contributors_url":"https://api.github.com/repos/louiscryan/grpc-java/contributors","subscribers_url":"https://api.github.com/repos/louiscryan/grpc-java/subscribers","subscription_url":"https://api.github.com/repos/louiscryan/grpc-java/subscription","commits_url":"https://api.github.com/repos/louiscryan/grpc-java/commits{/sha}","git_commits_url":"https://api.github.com/repos/louiscryan/grpc-java/git/commits{/sha}","comments_url":"https://api.github.com/repos/louiscryan/grpc-java/comments{/number}","issue_comment_url":"https://api.github.com/repos/louiscryan/grpc-java/issues/comments{/number}","contents_url":"https://api.github.com/repos/louiscryan/grpc-java/contents/{+path}","compare_url":"https://api.github.com/repos/louiscryan/grpc-java/compare/{base}...{head}","merges_url":"https://api.github.com/repos/louiscryan/grpc-java/merges","archive_url":"https://api.github.com/repos/louiscryan/grpc-java/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/louiscryan/grpc-java/downloads","issues_url":"https://api.github.com/repos/louiscryan/grpc-java/issues{/number}","pulls_url":"https://api.github.com/repos/louiscryan/grpc-java/pulls{/number}","milestones_url":"https://api.github.com/repos/louiscryan/grpc-java/milestones{/number}","notifications_url":"https://api.github.com/repos/louiscryan/grpc-java/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/louiscryan/grpc-java/labels{/name}","releases_url":"https://api.github.com/repos/louiscryan/grpc-java/releases{/id}","deployments_url":"https://api.github.com/repos/louiscryan/grpc-java/deployments","created_at":"2015-02-26T18:33:49Z","updated_at":"2016-02-17T18:58:23Z","pushed_at":"2016-04-07T22:08:12Z","git_url":"git://github.com/louiscryan/grpc-java.git","ssh_url":"git@github.com:louiscryan/grpc-java.git","clone_url":"https://github.com/louiscryan/grpc-java.git","svn_url":"https://github.com/louiscryan/grpc-java","homepage":"","size":6047,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"grpc:master","ref":"master","sha":"996f30f43dee9f7dc10e2f7ef714944af24e7bcd","user":{"login":"grpc","id":7802525,"avatar_url":"https://avatars.githubusercontent.com/u/7802525?v=3","gravatar_id":"","url":"https://api.github.com/users/grpc","html_url":"https://github.com/grpc","followers_url":"https://api.github.com/users/grpc/followers","following_url":"https://api.github.com/users/grpc/following{/other_user}","gists_url":"https://api.github.com/users/grpc/gists{/gist_id}","starred_url":"https://api.github.com/users/grpc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grpc/subscriptions","organizations_url":"https://api.github.com/users/grpc/orgs","repos_url":"https://api.github.com/users/grpc/repos","events_url":"https://api.github.com/users/grpc/events{/privacy}","received_events_url":"https://api.github.com/users/grpc/received_events","type":"Organization","site_admin":false},"repo":{"id":27729926,"name":"grpc-java","full_name":"grpc/grpc-java","owner":{"login":"grpc","id":7802525,"avatar_url":"https://avatars.githubusercontent.com/u/7802525?v=3","gravatar_id":"","url":"https://api.github.com/users/grpc","html_url":"https://github.com/grpc","followers_url":"https://api.github.com/users/grpc/followers","following_url":"https://api.github.com/users/grpc/following{/other_user}","gists_url":"https://api.github.com/users/grpc/gists{/gist_id}","starred_url":"https://api.github.com/users/grpc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grpc/subscriptions","organizations_url":"https://api.github.com/users/grpc/orgs","repos_url":"https://api.github.com/users/grpc/repos","events_url":"https://api.github.com/users/grpc/events{/privacy}","received_events_url":"https://api.github.com/users/grpc/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/grpc/grpc-java","description":"The Java gRPC implementation. HTTP/2 based RPC","fork":false,"url":"https://api.github.com/repos/grpc/grpc-java","forks_url":"https://api.github.com/repos/grpc/grpc-java/forks","keys_url":"https://api.github.com/repos/grpc/grpc-java/keys{/key_id}","collaborators_url":"https://api.github.com/repos/grpc/grpc-java/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/grpc/grpc-java/teams","hooks_url":"https://api.github.com/repos/grpc/grpc-java/hooks","issue_events_url":"https://api.github.com/repos/grpc/grpc-java/issues/events{/number}","events_url":"https://api.github.com/repos/grpc/grpc-java/events","assignees_url":"https://api.github.com/repos/grpc/grpc-java/assignees{/user}","branches_url":"https://api.github.com/repos/grpc/grpc-java/branches{/branch}","tags_url":"https://api.github.com/repos/grpc/grpc-java/tags","blobs_url":"https://api.github.com/repos/grpc/grpc-java/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/grpc/grpc-java/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/grpc/grpc-java/git/refs{/sha}","trees_url":"https://api.github.com/repos/grpc/grpc-java/git/trees{/sha}","statuses_url":"https://api.github.com/repos/grpc/grpc-java/statuses/{sha}","languages_url":"https://api.github.com/repos/grpc/grpc-java/languages","stargazers_url":"https://api.github.com/repos/grpc/grpc-java/stargazers","contributors_url":"https://api.github.com/repos/grpc/grpc-java/contributors","subscribers_url":"https://api.github.com/repos/grpc/grpc-java/subscribers","subscription_url":"https://api.github.com/repos/grpc/grpc-java/subscription","commits_url":"https://api.github.com/repos/grpc/grpc-java/commits{/sha}","git_commits_url":"https://api.github.com/repos/grpc/grpc-java/git/commits{/sha}","comments_url":"https://api.github.com/repos/grpc/grpc-java/comments{/number}","issue_comment_url":"https://api.github.com/repos/grpc/grpc-java/issues/comments{/number}","contents_url":"https://api.github.com/repos/grpc/grpc-java/contents/{+path}","compare_url":"https://api.github.com/repos/grpc/grpc-java/compare/{base}...{head}","merges_url":"https://api.github.com/repos/grpc/grpc-java/merges","archive_url":"https://api.github.com/repos/grpc/grpc-java/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/grpc/grpc-java/downloads","issues_url":"https://api.github.com/repos/grpc/grpc-java/issues{/number}","pulls_url":"https://api.github.com/repos/grpc/grpc-java/pulls{/number}","milestones_url":"https://api.github.com/repos/grpc/grpc-java/milestones{/number}","notifications_url":"https://api.github.com/repos/grpc/grpc-java/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/grpc/grpc-java/labels{/name}","releases_url":"https://api.github.com/repos/grpc/grpc-java/releases{/id}","deployments_url":"https://api.github.com/repos/grpc/grpc-java/deployments","created_at":"2014-12-08T19:00:00Z","updated_at":"2016-04-11T04:18:11Z","pushed_at":"2016-04-11T11:45:25Z","git_url":"git://github.com/grpc/grpc-java.git","ssh_url":"git@github.com:grpc/grpc-java.git","clone_url":"https://github.com/grpc/grpc-java.git","svn_url":"https://github.com/grpc/grpc-java","homepage":"http://grpc.io","size":21984,"stargazers_count":1250,"watchers_count":1250,"language":"Java","has_issues":true,"has_downloads":false,"has_wiki":false,"has_pages":true,"forks_count":324,"mirror_url":null,"open_issues_count":150,"forks":324,"open_issues":150,"watchers":1250,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/1583"},"html":{"href":"https://github.com/grpc/grpc-java/pull/1583"},"issue":{"href":"https://api.github.com/repos/grpc/grpc-java/issues/1583"},"comments":{"href":"https://api.github.com/repos/grpc/grpc-java/issues/1583/comments"},"review_comments":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/1583/comments"},"review_comment":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/grpc/grpc-java/pulls/1583/commits"},"statuses":{"href":"https://api.github.com/repos/grpc/grpc-java/statuses/71267ba12315f58c0076e1ffd4e647a2d6b939ff"}}}}