{"action":"created","comment":{"url":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/comments/132499054","pull_request_review_id":55586690,"id":132499054,"diff_hunk":"@@ -0,0 +1,642 @@\n+package org.corfudb.runtime.collections;\n+\n+import com.google.common.collect.ArrayListMultimap;\n+import com.google.common.collect.ImmutableList;\n+import com.google.common.collect.ImmutableMap;\n+import com.google.common.collect.ImmutableSet;\n+import com.google.common.collect.Multimap;\n+import com.google.common.reflect.TypeToken;\n+\n+import java.util.AbstractMap;\n+import java.util.ArrayList;\n+import java.util.Collection;\n+import java.util.Collections;\n+import java.util.ConcurrentModificationException;\n+import java.util.EnumSet;\n+import java.util.HashMap;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.Objects;\n+import java.util.Set;\n+import java.util.function.BiConsumer;\n+import java.util.function.BiFunction;\n+import java.util.function.Function;\n+import java.util.function.Predicate;\n+import java.util.stream.Collectors;\n+import java.util.stream.Stream;\n+\n+import javax.annotation.Nonnull;\n+\n+import lombok.Getter;\n+import org.corfudb.annotations.Accessor;\n+import org.corfudb.annotations.ConflictParameter;\n+import org.corfudb.annotations.CorfuObject;\n+import org.corfudb.annotations.DontInstrument;\n+import org.corfudb.annotations.Mutator;\n+import org.corfudb.annotations.MutatorAccessor;\n+import org.corfudb.annotations.TransactionalMethod;\n+\n+/** The CorfuTable implements a simple key-value store.\n+ *\n+ * <p>The primary interface to the CorfuTable is a Map, where the keys must be unique and\n+ * each key is mapped to exactly one value. Null values are not permitted.\n+ *\n+ * <p>The CorfuTable also supports an unlimited number of secondary indexes, which\n+ * the user provides at construction time as an enum which implements the IndexSpecificaiton\n+ * interface. An index specification consists of a IndexFunction, which returns a set of secondary\n+ * keys (indexes) the value should be mapped to. Secondary indexes are many-to-many: values\n+ * can be mapped to multiple indexes, and indexes can be mapped to multiples values. Each\n+ * IndexSpecification also specifies a projection function, which specifies a transformation\n+ * that can be done on a retrieval on the index. A common projection is to emit only the\n+ * values.\n+ *\n+ * @param <K>   The type of the primary key.\n+ * @param <V>   The type of the values to be mapped.\n+ * @param <F>   The type of the index specification enumeration.\n+ * @param <I>   The type of the index.\n+ */\n+@CorfuObject\n+public class CorfuTable<K ,V, F extends Enum<F> & CorfuTable.IndexSpecification, I>\n+        implements ICorfuMap<K, V> {\n+\n+    /** Helper function to get a map (non-secondary index) Corfu table.\n+     *\n+     * @param <K>           Key type\n+     * @param <V>           Value type\n+     * @return              A type token to pass to the builder.\n+     */\n+    static <K, V> TypeToken<CorfuTable<K, V, NoSecondaryIndex, Void>>\n+        getMapType() {\n+        return new TypeToken<CorfuTable<K, V, NoSecondaryIndex, Void>>() {};\n+    }\n+\n+    /** Helper function to get a Corfu Table.\n+     *\n+     * @param <K>                   Key type\n+     * @param <V>                   Value type\n+     * @param <F>                   Function enum type\n+     * @param <I>                   Index type\n+     * @return                      A type token to pass to the builder.\n+     */\n+    static <K, V, F extends Enum<F> & CorfuTable.IndexSpecification, I>\n+    TypeToken<CorfuTable<K, V, F, I>>\n+    getTableType() {\n+        return new TypeToken<CorfuTable<K, V, F, I>>() {};\n+    }\n+\n+    /**\n+     * The interface for an indexing function.\n+     * @param <K> The type of the key used in indexing.\n+     * @param <V> The type of the value used in indexing.\n+     * @param <I> The type of index key.\n+     */\n+    @FunctionalInterface\n+    public interface IndexFunction<K, V, I> {\n+        @Nonnull Collection<I> generateIndex(K k, V v);\n+    }\n+\n+    /**\n+     * The interface for a projection function.\n+     *\n+     * <p> NOTE: The projection function MUST return a new (preferably immutable) collection,\n+     * the collection of entries passed during this function are NOT safe to use\n+     * outside the context of this function.\n+     *\n+     * @param <K>   The type of the key used in projection.\n+     * @param <V>   The type of the value used in projection.\n+     * @param <I>   The type of the index used in projection.\n+     * @param <P>   The type of the projection returned.\n+     */\n+    @FunctionalInterface\n+    public interface ProjectionFunction<K, V, I, P> {\n+        @Nonnull\n+        Stream<P> generateProjection(I index,\n+                                     @Nonnull Stream<Map.Entry<K, V>> entryStream);\n+    }\n+\n+    /**\n+     * The interface for a index specification, which consists of a indexing function\n+     * and a projection functino.\n+     * @param <K>   The type of the primary key on the map.\n+     * @param <V>   The type of the value on the map.\n+     * @param <I>   The type of the index.\n+     * @param <P>   The type returned by the projection function.\n+     */\n+    public interface IndexSpecification<K, V, I, P> {\n+        IndexFunction<K, V, I> getIndexFunction();\n+        ProjectionFunction<K, V, I, P> getProjectionFunction();\n+    }\n+\n+    /** An index specification enumeration which has no index specifications.\n+     *  Using this enumeration effectively disables secondary indexes.\n+     */\n+    enum NoSecondaryIndex implements IndexSpecification {\n+        ;\n+\n+        @Override\n+        public IndexFunction getIndexFunction() {\n+            throw new UnsupportedOperationException();\n+        }\n+\n+        @Override\n+        public ProjectionFunction getProjectionFunction() {\n+            throw new UnsupportedOperationException();\n+        }\n+    }\n+\n+    /** The \"main\" map which contains the primary key-value mappings. */\n+    protected final Map<K,V> mainMap = new HashMap<>();\n+\n+    protected Set<F> indexFunctions;\n+\n+    protected Map<F, Multimap<I, Map.Entry<K,V>>> indexMap;\n+\n+    /** Generate a table with the given set of indexes. */\n+    public CorfuTable(Class<F> indexFunctionEnumClass) {\n+        indexerClass = indexFunctionEnumClass;\n+        indexMap = new HashMap<>();\n+        indexFunctions = EnumSet.allOf(indexFunctionEnumClass);\n+        indexFunctions.forEach(f -> indexMap.put(f, ArrayListMultimap.create()));\n+    }\n+\n+    /** Default constructor. Generates a table without any secondary indexes. */\n+    public CorfuTable() {\n+        indexFunctions = Collections.emptySet();\n+        indexMap = Collections.emptyMap();\n+    }\n+\n+    /** {@inheritDoc} */\n+    @Override\n+    @Accessor\n+    public int size() {\n+        return mainMap.size();\n+    }\n+\n+    /** {@inheritDoc} */\n+    @Override\n+    @Accessor\n+    public boolean isEmpty() {\n+        return mainMap.isEmpty();\n+    }\n+\n+    /** Return whether this table has secondary indexes or not.\n+     *\n+     * @return  True, if secondary indexes are present. False otherwise.\n+     */\n+    @Accessor\n+    public boolean hasSecondaryIndices() {\n+        return !indexFunctions.isEmpty();\n+    }\n+\n+    @Getter\n+    public Class indexerClass;\n+\n+    /** {@inheritDoc} */\n+    @Override\n+    @Accessor\n+    public boolean containsKey(@ConflictParameter Object key) {\n+        return mainMap.containsKey(key);\n+    }\n+\n+    /** {@inheritDoc} */\n+    @Override\n+    @Accessor\n+    public boolean containsValue(Object value) {\n+        return mainMap.containsValue(value);\n+    }\n+\n+    /** {@inheritDoc} */\n+    @Override\n+    @Accessor\n+    public V get(@ConflictParameter Object key) {\n+        return mainMap.get(key);\n+    }\n+\n+\n+\n+    /** Get a mapping using the specified index function.\n+     *\n+     * @param indexFunction The index function to use.\n+     * @param index         The index\n+     * @return\n+     */\n+    @SuppressWarnings(\"unchecked\")\n+    @DontInstrument\n+    public @Nonnull Collection<Object> getByIndex(@Nonnull F indexFunction, I index) {\n+        return getByIndex(indexFunction, indexFunction.getProjectionFunction(), index);\n+    }\n+\n+    /** Get a mapping using the specified index function.\n+     *\n+     * @param indexFunction The index function to use.\n+     * @param projectionFunction    The function to project results with.\n+     * @param index         The index\n+     * @param <P>                   The projection return type.\n+     * @return\n+     */\n+    @DontInstrument\n+    public @Nonnull <P> Collection<P> getByIndex(@Nonnull F indexFunction,\n+                                @Nonnull ProjectionFunction<K, V, I, P> projectionFunction,\n+                                I index) {\n+        return getByIndexAndFilter(indexFunction, projectionFunction, e -> true, index);\n+    }\n+\n+    /** Scan and filter using the specified index function and projection.\n+     *\n+     * @param indexFunction         An indexing function.\n+     * @param projectionFunction    The function to project results with.\n+     * @param entryPredicate        The predicate to scan and filter with.\n+     * @param index                 The index to lookup.\n+     * @param <P>                   The projection return type.\n+     * @return\n+     */\n+    @Accessor\n+    public @Nonnull <P> Collection<P> getByIndexAndFilter(@Nonnull F indexFunction,\n+                                 @Nonnull ProjectionFunction<K, V, I, P> projectionFunction,\n+                                 @Nonnull Predicate<? super Map.Entry<K, V>>\n+                                                                  entryPredicate,\n+                                 I index) {\n+        return projectionFunction\n+                .generateProjection(index, indexMap\n+                        .get(indexFunction).get(index).parallelStream()\n+                        .filter(entryPredicate))\n+                .collect(Collectors.toList());\n+    }\n+\n+\n+    /**\n+     * Register new index class\n+     *\n+     * This replaces the current index.\n+     *\n+     * @param indexFunctionEnumClass\n+     */\n+    public void registerIndex(Class indexFunctionEnumClass) {\n+        indexerClass = indexFunctionEnumClass;\n+        indexMap = new HashMap<>();\n+        indexFunctions = EnumSet.allOf(indexFunctionEnumClass);\n+        indexFunctions.forEach(f -> indexMap.put(f, ArrayListMultimap.create()));\n+        mainMap.forEach(this::mapSecondaryIndexes);\n+    }\n+\n+    /** {@inheritDoc} */","path":"runtime/src/main/java/org/corfudb/runtime/collections/CorfuTable.java","position":282,"original_position":282,"commit_id":"c34cb9f6079f9a0a346573bce3597a81955101a0","original_commit_id":"c34cb9f6079f9a0a346573bce3597a81955101a0","user":{"login":"corfu-repo","id":28743226,"avatar_url":"https://avatars2.githubusercontent.com/u/28743226?v=4","gravatar_id":"","url":"https://api.github.com/users/corfu-repo","html_url":"https://github.com/corfu-repo","followers_url":"https://api.github.com/users/corfu-repo/followers","following_url":"https://api.github.com/users/corfu-repo/following{/other_user}","gists_url":"https://api.github.com/users/corfu-repo/gists{/gist_id}","starred_url":"https://api.github.com/users/corfu-repo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/corfu-repo/subscriptions","organizations_url":"https://api.github.com/users/corfu-repo/orgs","repos_url":"https://api.github.com/users/corfu-repo/repos","events_url":"https://api.github.com/users/corfu-repo/events{/privacy}","received_events_url":"https://api.github.com/users/corfu-repo/received_events","type":"User","site_admin":false},"body":"![MINOR](https://sonarsource.github.io/sonar-github/severity-minor.png 'Severity: MINOR') Use isEmpty() to check whether the collection is empty or not. [![rule](https://sonarsource.github.io/sonar-github/rule.png)](https://sonarcloud.io/coding_rules#rule_key=squid%3AS1155)\n","created_at":"2017-08-10T16:11:09Z","updated_at":"2017-08-10T16:11:09Z","html_url":"https://github.com/CorfuDB/CorfuDB/pull/897#discussion_r132499054","pull_request_url":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/897","_links":{"self":{"href":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/comments/132499054"},"html":{"href":"https://github.com/CorfuDB/CorfuDB/pull/897#discussion_r132499054"},"pull_request":{"href":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/897"}}},"pull_request":{"url":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/897","id":134376398,"html_url":"https://github.com/CorfuDB/CorfuDB/pull/897","diff_url":"https://github.com/CorfuDB/CorfuDB/pull/897.diff","patch_url":"https://github.com/CorfuDB/CorfuDB/pull/897.patch","issue_url":"https://api.github.com/repos/CorfuDB/CorfuDB/issues/897","number":897,"state":"open","locked":false,"title":"Add CorfuTable, which supports multiple indexes","user":{"login":"no2chem","id":6496984,"avatar_url":"https://avatars0.githubusercontent.com/u/6496984?v=4","gravatar_id":"","url":"https://api.github.com/users/no2chem","html_url":"https://github.com/no2chem","followers_url":"https://api.github.com/users/no2chem/followers","following_url":"https://api.github.com/users/no2chem/following{/other_user}","gists_url":"https://api.github.com/users/no2chem/gists{/gist_id}","starred_url":"https://api.github.com/users/no2chem/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/no2chem/subscriptions","organizations_url":"https://api.github.com/users/no2chem/orgs","repos_url":"https://api.github.com/users/no2chem/repos","events_url":"https://api.github.com/users/no2chem/events{/privacy}","received_events_url":"https://api.github.com/users/no2chem/received_events","type":"User","site_admin":false},"body":"This PR introduces a new class, CorfuTable, which is intended to provide table-like support in Corfu.\r\n\r\nUsers of CorfuTable optionally provide an enum of indexing/projection functions (see CorfuTableTest) which define how to generate secondary indexes and how to project them when returning results.\r\n\r\nThe normal map functions (put, get, remove, putAll) operate on the primary index. A new function, getByIndex takes the index and index function, and returns a collection of values mapped to the secondary index projected according to the projection function for that secondary index. Multiple values can be mapped to a single secondary index.","created_at":"2017-08-07T07:24:17Z","updated_at":"2017-08-10T16:11:09Z","closed_at":null,"merged_at":null,"merge_commit_sha":"fe65570c3b9ff7cf648f6e91a3957673189d600e","assignee":null,"assignees":[],"requested_reviewers":[{"login":"Maithem","id":7818642,"avatar_url":"https://avatars2.githubusercontent.com/u/7818642?v=4","gravatar_id":"","url":"https://api.github.com/users/Maithem","html_url":"https://github.com/Maithem","followers_url":"https://api.github.com/users/Maithem/followers","following_url":"https://api.github.com/users/Maithem/following{/other_user}","gists_url":"https://api.github.com/users/Maithem/gists{/gist_id}","starred_url":"https://api.github.com/users/Maithem/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Maithem/subscriptions","organizations_url":"https://api.github.com/users/Maithem/orgs","repos_url":"https://api.github.com/users/Maithem/repos","events_url":"https://api.github.com/users/Maithem/events{/privacy}","received_events_url":"https://api.github.com/users/Maithem/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/897/commits","review_comments_url":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/897/comments","review_comment_url":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/comments{/number}","comments_url":"https://api.github.com/repos/CorfuDB/CorfuDB/issues/897/comments","statuses_url":"https://api.github.com/repos/CorfuDB/CorfuDB/statuses/c34cb9f6079f9a0a346573bce3597a81955101a0","head":{"label":"CorfuDB:CorfuTable","ref":"CorfuTable","sha":"c34cb9f6079f9a0a346573bce3597a81955101a0","user":{"login":"CorfuDB","id":10199273,"avatar_url":"https://avatars0.githubusercontent.com/u/10199273?v=4","gravatar_id":"","url":"https://api.github.com/users/CorfuDB","html_url":"https://github.com/CorfuDB","followers_url":"https://api.github.com/users/CorfuDB/followers","following_url":"https://api.github.com/users/CorfuDB/following{/other_user}","gists_url":"https://api.github.com/users/CorfuDB/gists{/gist_id}","starred_url":"https://api.github.com/users/CorfuDB/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CorfuDB/subscriptions","organizations_url":"https://api.github.com/users/CorfuDB/orgs","repos_url":"https://api.github.com/users/CorfuDB/repos","events_url":"https://api.github.com/users/CorfuDB/events{/privacy}","received_events_url":"https://api.github.com/users/CorfuDB/received_events","type":"Organization","site_admin":false},"repo":{"id":12960439,"name":"CorfuDB","full_name":"CorfuDB/CorfuDB","owner":{"login":"CorfuDB","id":10199273,"avatar_url":"https://avatars0.githubusercontent.com/u/10199273?v=4","gravatar_id":"","url":"https://api.github.com/users/CorfuDB","html_url":"https://github.com/CorfuDB","followers_url":"https://api.github.com/users/CorfuDB/followers","following_url":"https://api.github.com/users/CorfuDB/following{/other_user}","gists_url":"https://api.github.com/users/CorfuDB/gists{/gist_id}","starred_url":"https://api.github.com/users/CorfuDB/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CorfuDB/subscriptions","organizations_url":"https://api.github.com/users/CorfuDB/orgs","repos_url":"https://api.github.com/users/CorfuDB/repos","events_url":"https://api.github.com/users/CorfuDB/events{/privacy}","received_events_url":"https://api.github.com/users/CorfuDB/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/CorfuDB/CorfuDB","description":"A cluster consistency platform","fork":false,"url":"https://api.github.com/repos/CorfuDB/CorfuDB","forks_url":"https://api.github.com/repos/CorfuDB/CorfuDB/forks","keys_url":"https://api.github.com/repos/CorfuDB/CorfuDB/keys{/key_id}","collaborators_url":"https://api.github.com/repos/CorfuDB/CorfuDB/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/CorfuDB/CorfuDB/teams","hooks_url":"https://api.github.com/repos/CorfuDB/CorfuDB/hooks","issue_events_url":"https://api.github.com/repos/CorfuDB/CorfuDB/issues/events{/number}","events_url":"https://api.github.com/repos/CorfuDB/CorfuDB/events","assignees_url":"https://api.github.com/repos/CorfuDB/CorfuDB/assignees{/user}","branches_url":"https://api.github.com/repos/CorfuDB/CorfuDB/branches{/branch}","tags_url":"https://api.github.com/repos/CorfuDB/CorfuDB/tags","blobs_url":"https://api.github.com/repos/CorfuDB/CorfuDB/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/CorfuDB/CorfuDB/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/CorfuDB/CorfuDB/git/refs{/sha}","trees_url":"https://api.github.com/repos/CorfuDB/CorfuDB/git/trees{/sha}","statuses_url":"https://api.github.com/repos/CorfuDB/CorfuDB/statuses/{sha}","languages_url":"https://api.github.com/repos/CorfuDB/CorfuDB/languages","stargazers_url":"https://api.github.com/repos/CorfuDB/CorfuDB/stargazers","contributors_url":"https://api.github.com/repos/CorfuDB/CorfuDB/contributors","subscribers_url":"https://api.github.com/repos/CorfuDB/CorfuDB/subscribers","subscription_url":"https://api.github.com/repos/CorfuDB/CorfuDB/subscription","commits_url":"https://api.github.com/repos/CorfuDB/CorfuDB/commits{/sha}","git_commits_url":"https://api.github.com/repos/CorfuDB/CorfuDB/git/commits{/sha}","comments_url":"https://api.github.com/repos/CorfuDB/CorfuDB/comments{/number}","issue_comment_url":"https://api.github.com/repos/CorfuDB/CorfuDB/issues/comments{/number}","contents_url":"https://api.github.com/repos/CorfuDB/CorfuDB/contents/{+path}","compare_url":"https://api.github.com/repos/CorfuDB/CorfuDB/compare/{base}...{head}","merges_url":"https://api.github.com/repos/CorfuDB/CorfuDB/merges","archive_url":"https://api.github.com/repos/CorfuDB/CorfuDB/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/CorfuDB/CorfuDB/downloads","issues_url":"https://api.github.com/repos/CorfuDB/CorfuDB/issues{/number}","pulls_url":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls{/number}","milestones_url":"https://api.github.com/repos/CorfuDB/CorfuDB/milestones{/number}","notifications_url":"https://api.github.com/repos/CorfuDB/CorfuDB/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/CorfuDB/CorfuDB/labels{/name}","releases_url":"https://api.github.com/repos/CorfuDB/CorfuDB/releases{/id}","deployments_url":"https://api.github.com/repos/CorfuDB/CorfuDB/deployments","created_at":"2013-09-19T22:02:36Z","updated_at":"2017-08-09T18:35:17Z","pushed_at":"2017-08-10T16:10:19Z","git_url":"git://github.com/CorfuDB/CorfuDB.git","ssh_url":"git@github.com:CorfuDB/CorfuDB.git","clone_url":"https://github.com/CorfuDB/CorfuDB.git","svn_url":"https://github.com/CorfuDB/CorfuDB","homepage":"","size":356044,"stargazers_count":285,"watchers_count":285,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":58,"mirror_url":null,"open_issues_count":102,"forks":58,"open_issues":102,"watchers":285,"default_branch":"master"}},"base":{"label":"CorfuDB:master","ref":"master","sha":"43e4c53c00b85192e7de1779d35d8e6b31e04c87","user":{"login":"CorfuDB","id":10199273,"avatar_url":"https://avatars0.githubusercontent.com/u/10199273?v=4","gravatar_id":"","url":"https://api.github.com/users/CorfuDB","html_url":"https://github.com/CorfuDB","followers_url":"https://api.github.com/users/CorfuDB/followers","following_url":"https://api.github.com/users/CorfuDB/following{/other_user}","gists_url":"https://api.github.com/users/CorfuDB/gists{/gist_id}","starred_url":"https://api.github.com/users/CorfuDB/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CorfuDB/subscriptions","organizations_url":"https://api.github.com/users/CorfuDB/orgs","repos_url":"https://api.github.com/users/CorfuDB/repos","events_url":"https://api.github.com/users/CorfuDB/events{/privacy}","received_events_url":"https://api.github.com/users/CorfuDB/received_events","type":"Organization","site_admin":false},"repo":{"id":12960439,"name":"CorfuDB","full_name":"CorfuDB/CorfuDB","owner":{"login":"CorfuDB","id":10199273,"avatar_url":"https://avatars0.githubusercontent.com/u/10199273?v=4","gravatar_id":"","url":"https://api.github.com/users/CorfuDB","html_url":"https://github.com/CorfuDB","followers_url":"https://api.github.com/users/CorfuDB/followers","following_url":"https://api.github.com/users/CorfuDB/following{/other_user}","gists_url":"https://api.github.com/users/CorfuDB/gists{/gist_id}","starred_url":"https://api.github.com/users/CorfuDB/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CorfuDB/subscriptions","organizations_url":"https://api.github.com/users/CorfuDB/orgs","repos_url":"https://api.github.com/users/CorfuDB/repos","events_url":"https://api.github.com/users/CorfuDB/events{/privacy}","received_events_url":"https://api.github.com/users/CorfuDB/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/CorfuDB/CorfuDB","description":"A cluster consistency platform","fork":false,"url":"https://api.github.com/repos/CorfuDB/CorfuDB","forks_url":"https://api.github.com/repos/CorfuDB/CorfuDB/forks","keys_url":"https://api.github.com/repos/CorfuDB/CorfuDB/keys{/key_id}","collaborators_url":"https://api.github.com/repos/CorfuDB/CorfuDB/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/CorfuDB/CorfuDB/teams","hooks_url":"https://api.github.com/repos/CorfuDB/CorfuDB/hooks","issue_events_url":"https://api.github.com/repos/CorfuDB/CorfuDB/issues/events{/number}","events_url":"https://api.github.com/repos/CorfuDB/CorfuDB/events","assignees_url":"https://api.github.com/repos/CorfuDB/CorfuDB/assignees{/user}","branches_url":"https://api.github.com/repos/CorfuDB/CorfuDB/branches{/branch}","tags_url":"https://api.github.com/repos/CorfuDB/CorfuDB/tags","blobs_url":"https://api.github.com/repos/CorfuDB/CorfuDB/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/CorfuDB/CorfuDB/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/CorfuDB/CorfuDB/git/refs{/sha}","trees_url":"https://api.github.com/repos/CorfuDB/CorfuDB/git/trees{/sha}","statuses_url":"https://api.github.com/repos/CorfuDB/CorfuDB/statuses/{sha}","languages_url":"https://api.github.com/repos/CorfuDB/CorfuDB/languages","stargazers_url":"https://api.github.com/repos/CorfuDB/CorfuDB/stargazers","contributors_url":"https://api.github.com/repos/CorfuDB/CorfuDB/contributors","subscribers_url":"https://api.github.com/repos/CorfuDB/CorfuDB/subscribers","subscription_url":"https://api.github.com/repos/CorfuDB/CorfuDB/subscription","commits_url":"https://api.github.com/repos/CorfuDB/CorfuDB/commits{/sha}","git_commits_url":"https://api.github.com/repos/CorfuDB/CorfuDB/git/commits{/sha}","comments_url":"https://api.github.com/repos/CorfuDB/CorfuDB/comments{/number}","issue_comment_url":"https://api.github.com/repos/CorfuDB/CorfuDB/issues/comments{/number}","contents_url":"https://api.github.com/repos/CorfuDB/CorfuDB/contents/{+path}","compare_url":"https://api.github.com/repos/CorfuDB/CorfuDB/compare/{base}...{head}","merges_url":"https://api.github.com/repos/CorfuDB/CorfuDB/merges","archive_url":"https://api.github.com/repos/CorfuDB/CorfuDB/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/CorfuDB/CorfuDB/downloads","issues_url":"https://api.github.com/repos/CorfuDB/CorfuDB/issues{/number}","pulls_url":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls{/number}","milestones_url":"https://api.github.com/repos/CorfuDB/CorfuDB/milestones{/number}","notifications_url":"https://api.github.com/repos/CorfuDB/CorfuDB/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/CorfuDB/CorfuDB/labels{/name}","releases_url":"https://api.github.com/repos/CorfuDB/CorfuDB/releases{/id}","deployments_url":"https://api.github.com/repos/CorfuDB/CorfuDB/deployments","created_at":"2013-09-19T22:02:36Z","updated_at":"2017-08-09T18:35:17Z","pushed_at":"2017-08-10T16:10:19Z","git_url":"git://github.com/CorfuDB/CorfuDB.git","ssh_url":"git@github.com:CorfuDB/CorfuDB.git","clone_url":"https://github.com/CorfuDB/CorfuDB.git","svn_url":"https://github.com/CorfuDB/CorfuDB","homepage":"","size":356044,"stargazers_count":285,"watchers_count":285,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":58,"mirror_url":null,"open_issues_count":102,"forks":58,"open_issues":102,"watchers":285,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/897"},"html":{"href":"https://github.com/CorfuDB/CorfuDB/pull/897"},"issue":{"href":"https://api.github.com/repos/CorfuDB/CorfuDB/issues/897"},"comments":{"href":"https://api.github.com/repos/CorfuDB/CorfuDB/issues/897/comments"},"review_comments":{"href":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/897/comments"},"review_comment":{"href":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/CorfuDB/CorfuDB/pulls/897/commits"},"statuses":{"href":"https://api.github.com/repos/CorfuDB/CorfuDB/statuses/c34cb9f6079f9a0a346573bce3597a81955101a0"}}}}
{"actor":{"display_login":"corfu-repo"}}