{"action":"created","comment":{"url":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/comments/43075297","id":43075297,"diff_hunk":"@@ -1,167 +1,292 @@\n /**\n  * Copyright 2015 Confluent Inc.\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+ * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n+ * in compliance with the License. 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+ * Unless required by applicable law or agreed to in writing, software distributed under the License\n+ * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n+ * or implied. See the License for the specific language governing permissions and limitations under\n+ * the License.\n  **/\n \n package io.confluent.copycat.hdfs;\n \n import org.apache.hadoop.conf.Configuration;\n import org.apache.hadoop.fs.FileStatus;\n-import org.apache.hadoop.fs.FileSystem;\n-import org.apache.hadoop.fs.Path;\n import org.apache.hadoop.fs.PathFilter;\n+import org.apache.kafka.common.TopicPartition;\n+import org.apache.kafka.copycat.errors.CopycatException;\n+import org.apache.kafka.copycat.errors.IllegalWorkerStateException;\n+import org.apache.kafka.copycat.sink.SinkRecord;\n+import org.apache.kafka.copycat.sink.SinkTaskContext;\n import org.slf4j.Logger;\n import org.slf4j.LoggerFactory;\n \n import java.io.IOException;\n-import java.net.URI;\n+import java.util.ArrayList;\n+import java.util.Collection;\n import java.util.HashMap;\n+import java.util.HashSet;\n+import java.util.LinkedList;\n+import java.util.List;\n import java.util.Map;\n+import java.util.Queue;\n+import java.util.Set;\n \n-import io.confluent.copycat.connector.TopicPartition;\n-import io.confluent.copycat.errors.CopycatException;\n-import io.confluent.copycat.errors.CopycatRuntimeException;\n-import io.confluent.copycat.sink.SinkRecord;\n-\n+import io.confluent.copycat.avro.AvroData;\n \n public class HdfsWriter {\n+\n   private static final Logger log = LoggerFactory.getLogger(HdfsWriter.class);\n   private Map<TopicPartition, RecordWriter<Long, SinkRecord>> writers = null;\n+  private Map<TopicPartition, WAL> wals = null;\n+  private Map<TopicPartition, String> tempFileNames = null;\n   private Map<TopicPartition, Long> offsets = null;\n   private Map<TopicPartition, Integer> recordCounters = null;\n   private Configuration conf;\n-  private FileSystem fs;\n-  private Class<? extends RecordWriterProvider> writerProviderClass;\n+  private RecordWriterProvider writerProvider;\n   private String url;\n   private int flushSize;\n   private String topicsDir;\n+  private Set<TopicPartition> assignment;\n+  private AvroData avroData;\n+  private SinkTaskContext context;\n+  private Map<TopicPartition, State> states;\n+  private Set<TopicPartition> recovered;\n+  private Map<TopicPartition, Queue<SinkRecord>> buffer;\n+  private Storage storage;\n+  private long backOffMs;\n+\n+  private enum State {\n+    RECOVERY_STARTED,\n+    RECOVERY_PARTITION_PAUSED,\n+    WAL_CREATED,\n+    WAL_APPLIED,\n+    WAL_TRUNCATED,\n+    OFFSET_RESET,\n+    WRITE_STARTED,\n+    WRITE_PARTITION_PAUSED,\n+    SHOULD_ROTATE,\n+    TEMP_FILE_CLOSED,\n+    WAL_APPENDED,\n+    FILE_COMMITTED;\n+\n+    private static State[] vals = values();\n+    public State next() {\n+      return vals[(this.ordinal() + 1) % vals.length];\n+    }\n+  }\n+\n \n   @SuppressWarnings(\"unchecked\")\n-  public HdfsWriter(HdfsSinkConnectorConfig connectorConfig) {\n+  public HdfsWriter(HdfsSinkConnectorConfig connectorConfig, SinkTaskContext context, AvroData avroData) {\n     try {\n-      writers = new HashMap<TopicPartition, RecordWriter<Long, SinkRecord>>();\n-      recordCounters = new HashMap<TopicPartition, Integer>();\n-      flushSize = connectorConfig.getInt(HdfsSinkConnectorConfig.FLUSH_SIZE_CONFIG);\n+      writers = new HashMap<>();\n+      wals = new HashMap<>();\n+      offsets = new HashMap<>();\n+      recordCounters = new HashMap<>();\n+      tempFileNames = new HashMap<>();\n \n+      flushSize = connectorConfig.getInt(HdfsSinkConnectorConfig.FLUSH_SIZE_CONFIG);\n+      backOffMs = connectorConfig.getLong(HdfsSinkConnectorConfig.RETRY_BACKOFF_CONFIG);\n       url = connectorConfig.getString(HdfsSinkConnectorConfig.HDFS_URL_CONFIG);\n       topicsDir = connectorConfig.getString(HdfsSinkConnectorConfig.TOPIC_DIR_CONFIG);\n       conf = new Configuration();\n-      fs = FileSystem.newInstance(URI.create(url), conf);\n-      Path path = new Path(url + \"/\" + topicsDir);\n-      if (!fs.exists(path)) {\n-        fs.mkdirs(path);\n+\n+      Class<? extends Storage> storageClass =\n+          (Class<? extends Storage>) Class.forName(connectorConfig.getString(HdfsSinkConnectorConfig.STORAGE_CLASS_CONFIG));\n+      this.storage = StorageFactory.createStorage(storageClass, conf, url);\n+      createTopicsDir();\n+      writerProvider = ((Class<RecordWriterProvider>) Class.forName(connectorConfig.getString(\n+              HdfsSinkConnectorConfig.RECORD_WRITER_PROVIDER_CLASS_CONFIG))).newInstance();\n+\n+      this.context = context;\n+      this.assignment = context.assignment();\n+      this.avroData = avroData;\n+\n+      states = new HashMap<>();\n+      for (TopicPartition tp: assignment) {\n+        states.put(tp, State.RECOVERY_STARTED);\n+      }\n+      buffer = new HashMap<>();\n+      for (TopicPartition tp: assignment) {\n+        buffer.put(tp, new LinkedList<SinkRecord>());\n       }\n-      offsets = readOffsets();\n-      writerProviderClass = (Class<RecordWriterProvider>)\n-          Class.forName(connectorConfig.getString(HdfsSinkConnectorConfig.RECORD_WRITER_PROVIDER_CLASS_CONFIG));\n+      recovered = new HashSet<>();\n+\n     } catch (IOException e) {\n-      throw new CopycatRuntimeException(e);\n-    } catch (ClassNotFoundException e) {\n-      throw new CopycatRuntimeException(\"RecordWriterProvider class not found\", e);\n+      throw new CopycatException(e);\n+    } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {\n+      throw new CopycatException(\"RecordWriterProvider class not found\", e);\n     }\n   }\n \n-\n-  public Map<TopicPartition, Long> getPreviousOffsets() {\n+  /**\n+   * @return get committed offsets of previous task runs.\n+   */\n+  public Map<TopicPartition, Long> getCommittedOffsets() {\n     return offsets;\n   }\n \n-  public void writeRecord(TopicPartition topicPart, SinkRecord record)\n-      throws CopycatException{\n+  public void write(Collection<SinkRecord> records) throws IOException {\n+    String topic;\n+    int partition;\n+    for (SinkRecord record: records) {\n+      topic = record.topic();\n+      partition = record.kafkaPartition();\n+      TopicPartition topicPart = new TopicPartition(topic, partition);\n+      buffer.get(topicPart).add(record);\n+    }\n+    for (TopicPartition topicPart: assignment) {\n+      execute(topicPart);\n+    }\n+  }\n+\n+  public void recover(TopicPartition topicPart) {\n     try {\n-      RecordWriter<Long, SinkRecord> writer = getWriter(topicPart, record);\n-      writer.write(System.currentTimeMillis(), record);\n-      if (!offsets.containsKey(topicPart)) {\n-        offsets.put(topicPart, record.getOffset() - 1);\n-      }\n-      updateRecordCounter(topicPart);\n-      if (shouldRotate(topicPart)) {\n-        rotate(topicPart);\n+      switch (states.get(topicPart)) {\n+        case RECOVERY_STARTED:\n+          pause(topicPart);\n+          nextState(topicPart);\n+        case RECOVERY_PARTITION_PAUSED:\n+          createWAL(topicPart);\n+          nextState(topicPart);\n+        case WAL_CREATED:\n+          applyWAL(topicPart);\n+          nextState(topicPart);\n+        case WAL_APPLIED:\n+          truncateWAL(topicPart);\n+          nextState(topicPart);\n+        case WAL_TRUNCATED:\n+          resetOffsets(topicPart);\n+          nextState(topicPart);\n+        case OFFSET_RESET:\n+          resume(topicPart);\n+          nextState(topicPart);\n       }\n-    } catch (IOException e) {\n-      throw new CopycatRuntimeException(e);\n+    } catch (CopycatException e) {\n+      log.error(\"Recovery failed.\");\n+      setRetryBackoff(backOffMs);\n     }\n   }\n \n-  public void close() throws IOException, CopycatException {\n-    for (TopicPartition topicPart: writers.keySet()) {\n-      rotate(topicPart);\n+  private void execute(TopicPartition topicPart) throws IOException {\n+    Queue<SinkRecord> records = buffer.get(topicPart);\n+    while(!records.isEmpty()) {\n+      try {\n+        switch (states.get(topicPart)) {","path":"src/main/java/io/confluent/copycat/hdfs/HdfsWriter.java","position":229,"original_position":229,"commit_id":"eb4b7d260d6add87c62e735963add265ae17df18","original_commit_id":"eb4b7d260d6add87c62e735963add265ae17df18","user":{"login":"ewencp","id":47366,"avatar_url":"https://avatars.githubusercontent.com/u/47366?v=3","gravatar_id":"","url":"https://api.github.com/users/ewencp","html_url":"https://github.com/ewencp","followers_url":"https://api.github.com/users/ewencp/followers","following_url":"https://api.github.com/users/ewencp/following{/other_user}","gists_url":"https://api.github.com/users/ewencp/gists{/gist_id}","starred_url":"https://api.github.com/users/ewencp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ewencp/subscriptions","organizations_url":"https://api.github.com/users/ewencp/orgs","repos_url":"https://api.github.com/users/ewencp/repos","events_url":"https://api.github.com/users/ewencp/events{/privacy}","received_events_url":"https://api.github.com/users/ewencp/received_events","type":"User","site_admin":false},"body":"Should we avoid redundancy by reusing the `recover()` method for some of this? Not sure if it ends up being messier to reuse it or not -- I'll leave it to you to decide.","created_at":"2015-10-27T01:18:19Z","updated_at":"2015-10-27T01:18:19Z","html_url":"https://github.com/confluentinc/copycat-hdfs/pull/6#discussion_r43075297","pull_request_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/6","_links":{"self":{"href":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/comments/43075297"},"html":{"href":"https://github.com/confluentinc/copycat-hdfs/pull/6#discussion_r43075297"},"pull_request":{"href":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/6"}}},"pull_request":{"url":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/6","id":43351247,"html_url":"https://github.com/confluentinc/copycat-hdfs/pull/6","diff_url":"https://github.com/confluentinc/copycat-hdfs/pull/6.diff","patch_url":"https://github.com/confluentinc/copycat-hdfs/pull/6.patch","issue_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/issues/6","number":6,"state":"open","locked":false,"title":"Exactly once delivery with WAL","user":{"login":"Ishiihara","id":1189848,"avatar_url":"https://avatars.githubusercontent.com/u/1189848?v=3","gravatar_id":"","url":"https://api.github.com/users/Ishiihara","html_url":"https://github.com/Ishiihara","followers_url":"https://api.github.com/users/Ishiihara/followers","following_url":"https://api.github.com/users/Ishiihara/following{/other_user}","gists_url":"https://api.github.com/users/Ishiihara/gists{/gist_id}","starred_url":"https://api.github.com/users/Ishiihara/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Ishiihara/subscriptions","organizations_url":"https://api.github.com/users/Ishiihara/orgs","repos_url":"https://api.github.com/users/Ishiihara/repos","events_url":"https://api.github.com/users/Ishiihara/events{/privacy}","received_events_url":"https://api.github.com/users/Ishiihara/received_events","type":"User","site_admin":false},"body":"@ewencp  Can you review it and provide some feedback? Thanks!\r\nThis PR adds the following functionality:\r\n1. Time based file rotate using ScheduledExecutorService. \r\n2. WAL implementation to ensure exactly once delivery in case of failure and zombie writes.\r\n\r\nTODO:\r\n1. Replace SequenceFile class with a customized format. This PR uses the SequenceFile  implementation in hadoop trunk. \r\n2. WAL truncation. Currently, to make sure that we don't commit a file in WAL with a certain starting offset if there is already a committed file with the same start offset to ensure that data is not written to HDFS more than once.  This operation is relatively expensive if there are lots of committed files under a topic partition directory.  Truncating the WAL can reduce the number of such operations.  \r\n3. Create WAL factory class to enable different implementation of WAL and some refactoring of WAL api. \r\n4. Currently, HBase uses disruptor to perform sync to HDFS asynchronously. And we call fsync appending each entry to WAL? Given that our frequency in writing to WAL is much smaller than HBase, do you think it is worth exploring this option? ","created_at":"2015-08-26T00:05:17Z","updated_at":"2015-10-27T01:18:19Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c3bc4d6589ed2248304e48bae08791cc092d1de1","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/6/commits","review_comments_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/6/comments","review_comment_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/issues/6/comments","statuses_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/statuses/eb4b7d260d6add87c62e735963add265ae17df18","head":{"label":"confluentinc:exactly-once","ref":"exactly-once","sha":"eb4b7d260d6add87c62e735963add265ae17df18","user":{"login":"confluentinc","id":9439498,"avatar_url":"https://avatars.githubusercontent.com/u/9439498?v=3","gravatar_id":"","url":"https://api.github.com/users/confluentinc","html_url":"https://github.com/confluentinc","followers_url":"https://api.github.com/users/confluentinc/followers","following_url":"https://api.github.com/users/confluentinc/following{/other_user}","gists_url":"https://api.github.com/users/confluentinc/gists{/gist_id}","starred_url":"https://api.github.com/users/confluentinc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/confluentinc/subscriptions","organizations_url":"https://api.github.com/users/confluentinc/orgs","repos_url":"https://api.github.com/users/confluentinc/repos","events_url":"https://api.github.com/users/confluentinc/events{/privacy}","received_events_url":"https://api.github.com/users/confluentinc/received_events","type":"Organization","site_admin":false},"repo":{"id":35967803,"name":"copycat-hdfs","full_name":"confluentinc/copycat-hdfs","owner":{"login":"confluentinc","id":9439498,"avatar_url":"https://avatars.githubusercontent.com/u/9439498?v=3","gravatar_id":"","url":"https://api.github.com/users/confluentinc","html_url":"https://github.com/confluentinc","followers_url":"https://api.github.com/users/confluentinc/followers","following_url":"https://api.github.com/users/confluentinc/following{/other_user}","gists_url":"https://api.github.com/users/confluentinc/gists{/gist_id}","starred_url":"https://api.github.com/users/confluentinc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/confluentinc/subscriptions","organizations_url":"https://api.github.com/users/confluentinc/orgs","repos_url":"https://api.github.com/users/confluentinc/repos","events_url":"https://api.github.com/users/confluentinc/events{/privacy}","received_events_url":"https://api.github.com/users/confluentinc/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/confluentinc/copycat-hdfs","description":"Copycat HDFS connector","fork":false,"url":"https://api.github.com/repos/confluentinc/copycat-hdfs","forks_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/forks","keys_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/teams","hooks_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/hooks","issue_events_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/issues/events{/number}","events_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/events","assignees_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/assignees{/user}","branches_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/branches{/branch}","tags_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/tags","blobs_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/git/refs{/sha}","trees_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/statuses/{sha}","languages_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/languages","stargazers_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/stargazers","contributors_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/contributors","subscribers_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/subscribers","subscription_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/subscription","commits_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/commits{/sha}","git_commits_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/git/commits{/sha}","comments_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/comments{/number}","issue_comment_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/issues/comments{/number}","contents_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/contents/{+path}","compare_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/merges","archive_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/downloads","issues_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/issues{/number}","pulls_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls{/number}","milestones_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/milestones{/number}","notifications_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/labels{/name}","releases_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/releases{/id}","created_at":"2015-05-20T18:54:34Z","updated_at":"2015-09-30T20:12:47Z","pushed_at":"2015-10-26T21:11:23Z","git_url":"git://github.com/confluentinc/copycat-hdfs.git","ssh_url":"git@github.com:confluentinc/copycat-hdfs.git","clone_url":"https://github.com/confluentinc/copycat-hdfs.git","svn_url":"https://github.com/confluentinc/copycat-hdfs","homepage":null,"size":301,"stargazers_count":2,"watchers_count":2,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":6,"forks":0,"open_issues":6,"watchers":2,"default_branch":"master"}},"base":{"label":"confluentinc:master","ref":"master","sha":"e802567234b9b934548fba17a6addb555b0317a6","user":{"login":"confluentinc","id":9439498,"avatar_url":"https://avatars.githubusercontent.com/u/9439498?v=3","gravatar_id":"","url":"https://api.github.com/users/confluentinc","html_url":"https://github.com/confluentinc","followers_url":"https://api.github.com/users/confluentinc/followers","following_url":"https://api.github.com/users/confluentinc/following{/other_user}","gists_url":"https://api.github.com/users/confluentinc/gists{/gist_id}","starred_url":"https://api.github.com/users/confluentinc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/confluentinc/subscriptions","organizations_url":"https://api.github.com/users/confluentinc/orgs","repos_url":"https://api.github.com/users/confluentinc/repos","events_url":"https://api.github.com/users/confluentinc/events{/privacy}","received_events_url":"https://api.github.com/users/confluentinc/received_events","type":"Organization","site_admin":false},"repo":{"id":35967803,"name":"copycat-hdfs","full_name":"confluentinc/copycat-hdfs","owner":{"login":"confluentinc","id":9439498,"avatar_url":"https://avatars.githubusercontent.com/u/9439498?v=3","gravatar_id":"","url":"https://api.github.com/users/confluentinc","html_url":"https://github.com/confluentinc","followers_url":"https://api.github.com/users/confluentinc/followers","following_url":"https://api.github.com/users/confluentinc/following{/other_user}","gists_url":"https://api.github.com/users/confluentinc/gists{/gist_id}","starred_url":"https://api.github.com/users/confluentinc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/confluentinc/subscriptions","organizations_url":"https://api.github.com/users/confluentinc/orgs","repos_url":"https://api.github.com/users/confluentinc/repos","events_url":"https://api.github.com/users/confluentinc/events{/privacy}","received_events_url":"https://api.github.com/users/confluentinc/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/confluentinc/copycat-hdfs","description":"Copycat HDFS connector","fork":false,"url":"https://api.github.com/repos/confluentinc/copycat-hdfs","forks_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/forks","keys_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/teams","hooks_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/hooks","issue_events_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/issues/events{/number}","events_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/events","assignees_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/assignees{/user}","branches_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/branches{/branch}","tags_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/tags","blobs_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/git/refs{/sha}","trees_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/statuses/{sha}","languages_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/languages","stargazers_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/stargazers","contributors_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/contributors","subscribers_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/subscribers","subscription_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/subscription","commits_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/commits{/sha}","git_commits_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/git/commits{/sha}","comments_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/comments{/number}","issue_comment_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/issues/comments{/number}","contents_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/contents/{+path}","compare_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/merges","archive_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/downloads","issues_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/issues{/number}","pulls_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls{/number}","milestones_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/milestones{/number}","notifications_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/labels{/name}","releases_url":"https://api.github.com/repos/confluentinc/copycat-hdfs/releases{/id}","created_at":"2015-05-20T18:54:34Z","updated_at":"2015-09-30T20:12:47Z","pushed_at":"2015-10-26T21:11:23Z","git_url":"git://github.com/confluentinc/copycat-hdfs.git","ssh_url":"git@github.com:confluentinc/copycat-hdfs.git","clone_url":"https://github.com/confluentinc/copycat-hdfs.git","svn_url":"https://github.com/confluentinc/copycat-hdfs","homepage":null,"size":301,"stargazers_count":2,"watchers_count":2,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":6,"forks":0,"open_issues":6,"watchers":2,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/6"},"html":{"href":"https://github.com/confluentinc/copycat-hdfs/pull/6"},"issue":{"href":"https://api.github.com/repos/confluentinc/copycat-hdfs/issues/6"},"comments":{"href":"https://api.github.com/repos/confluentinc/copycat-hdfs/issues/6/comments"},"review_comments":{"href":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/6/comments"},"review_comment":{"href":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/confluentinc/copycat-hdfs/pulls/6/commits"},"statuses":{"href":"https://api.github.com/repos/confluentinc/copycat-hdfs/statuses/eb4b7d260d6add87c62e735963add265ae17df18"}}}}