{"action":"created","comment":{"url":"https://api.github.com/repos/VoltDB/voltdb/pulls/comments/23174150","id":23174150,"diff_hunk":"@@ -0,0 +1,1533 @@\n+package org.voltcore.zk;\n+\n+import java.nio.ByteBuffer;\n+import java.util.Arrays;\n+import java.util.Collections;\n+import java.util.HashMap;\n+import java.util.HashSet;\n+import java.util.List;\n+import java.util.ListIterator;\n+import java.util.Map;\n+import java.util.Set;\n+import java.util.TreeSet;\n+import java.util.concurrent.ExecutionException;\n+import java.util.concurrent.RejectedExecutionException;\n+import java.util.concurrent.atomic.AtomicBoolean;\n+import java.util.concurrent.locks.Lock;\n+import java.util.concurrent.locks.ReentrantLock;\n+\n+import org.apache.zookeeper_voltpatches.CreateMode;\n+import org.apache.zookeeper_voltpatches.KeeperException;\n+import org.apache.zookeeper_voltpatches.WatchedEvent;\n+import org.apache.zookeeper_voltpatches.Watcher;\n+import org.apache.zookeeper_voltpatches.ZooDefs.Ids;\n+import org.apache.zookeeper_voltpatches.ZooKeeper;\n+import org.apache.zookeeper_voltpatches.data.Stat;\n+import org.voltcore.logging.VoltLogger;\n+import org.voltcore.utils.CoreUtils;\n+import org.voltdb.VoltZK;\n+\n+import com.google_voltpatches.common.collect.ImmutableSet;\n+import com.google_voltpatches.common.collect.Sets;\n+import com.google_voltpatches.common.util.concurrent.ListenableFuture;\n+import com.google_voltpatches.common.util.concurrent.ListeningExecutorService;\n+\n+/*\n+ * This state machine coexists with other SynchronizedStateManagers by using different branches of the\n+ * ZooKeeper tree. This class provides a service to the StateMachineInstance class that reports changes\n+ * to the membership. The ZooKeeper tree hierarchy is (quoted are reserved node names):\n+ *     |- rootNode\n+ *     |\\\n+ *     | |- 'MEMBERS'\n+ *     |  \\\n+ *     |   |- memberId\n+ *     \\\n+ *      |- instanceName\n+ *      |\\\n+ *      | |- 'LOCK_CONTENDERS'\n+ *      |\\\n+ *      | |- 'BARRIER_PARTICIPANTS'\n+ *       \\\n+ *        |- 'BARRIER_RESULTS'\n+ *\n+ * This hierarchy supports multiple topologies so one SynchronizedStatesManager could be used to track\n+ * cluster membership while another could be used to track partition membership. Each SynchronizedStatesManager\n+ * instance has a memberId that must be unique to the member community it is participating in. As the\n+ * SynchronizedStatesManager is a shared service that can provide membership changes of other\n+ * StateMachineInstances joining or leaving the community, all instances are considered tightly bound to the\n+ * SynchronizedStatesManager. This means that the SynchronizedStatesManager will not join a community until\n+ * all instances using that manager have registered with the SynchronizedStatesManager. Thus when a\n+ * SynchronizedStatesManager informs its instances that a new member has joined the community (the set of\n+ * nodes under the 'MEMBERS' node), the instance can be assured that it's peer instance is also there. This\n+ * implies that StateMachineInstances can only coexist in the same SynchronizedStatesManager if all instances\n+ * can be initialized (registered) before any individual instance needs to access the shared state.\n+ */\n+public class SynchronizedStatesManager {\n+    private final AtomicBoolean m_done = new AtomicBoolean(false);\n+    private final static String m_memberNode = \"MEMBERS\";\n+    private Set<String> m_groupMembers = new HashSet<String>();\n+    private final StateMachineInstance m_registeredStateMachines [];\n+    private int m_registeredStateMachineInstances = 0;\n+    private static final ListeningExecutorService m_shared_es = CoreUtils.getListeningExecutorService(\"SMI Daemon\", 1);\n+    // We assume that we are far enough along that the HostMessenger is up and running. Otherwise add to constructor.\n+    private final ZooKeeper m_zk;\n+    private final String m_ssmRootNode;\n+    private final String m_stateMachineRoot;\n+    private final String m_stateMachineMemberPath;\n+    private final String m_memberId;\n+\n+    private enum REQUEST_TYPE {\n+        INITIALIZING,\n+        LAST_CHANGE_OUTCOME_REQUEST,\n+        STATE_CHANGE_REQUEST,\n+        CORRELATED_COORDINATED_TASK,\n+        UNCORRELATED_COORDINATED_TASK\n+    };\n+\n+    private enum RESULT_CONCENSUS {\n+        AGREE,\n+        DISAGREE,\n+        NO_QUORUM\n+    }\n+\n+    protected static boolean addIfMissing(ZooKeeper zk, String absolutePath, CreateMode createMode, byte[] data)\n+            throws KeeperException, InterruptedException {\n+        try {\n+            zk.create(absolutePath, data, Ids.OPEN_ACL_UNSAFE, createMode);\n+        } catch (KeeperException.NodeExistsException e) {\n+            return false;\n+        }\n+        return true;\n+    }\n+\n+    protected boolean addIfMissing(String absolutePath, CreateMode createMode, byte[] data)\n+            throws KeeperException, InterruptedException {\n+        return addIfMissing(m_zk, absolutePath, createMode, data);\n+    }\n+\n+    protected String getMemberId() {\n+        return m_memberId;\n+    }\n+\n+    /*\n+     * Rules for each StateMachineInstance:\n+     * 1. All access to the state machine is serialized by a per state machine local lock.\n+     * 2. State changes may only be initiated by the oldest ephemeral lock node under the\n+     *    LOCK_CONTENDERS directory. This represents the distributed lock used to\n+     *    synchronize proposals by the members of the state machine.\n+     * 3. When a new member joins an operational state machine, it must grab the lock node\n+     *    (2) before it can determine the current agreed state. However since it has to\n+     *    participate and benignly generate null results for all proposals, it can glean\n+     *    the state from the outcome of the next request it fully participates in. Note\n+     *    that an operation in progress during initialization of the new member is not\n+     *    used by the new member because the proposal initiator may have accepted the\n+     *    results and released the distributed lock before the new member can evaluate the\n+     *    results to determine the current state.\n+     * 4. A member holding the distributed lock can propose a state change by clearing out\n+     *    the reported result nodes under the BARRIER_RESULTS node, updating the\n+     *    BARRIER_RESULTS node with the old and proposed state, and adding an ephemeral\n+     *    unique node under BARRIER_PARTICIPANTS to trigger the state machine members.\n+     *    Each proposal in the BARRIER_RESULTS node has a version number that is used\n+     *    by other members to distinguish a new proposal from a proposal it has already\n+     *    processed.\n+     * 5. When a member detects a transition in the BARRIER_PARTICIPANTS node and a new\n+     *    version of the BARRIER_RESULTS node, it adds an ephemeral node for itself under\n+     *    BARRIER_PARTICIPANTS and copies the old and proposed state from the BARRIER_RESULTS\n+     *    node.\n+     * 6. Each member evaluates the proposed state and inserts a SUCCESS or FAILURE indication\n+     *    in a persistent unique node under the BARRIER_RESULTS path.\n+     * 7. A member may also request that an action be performed by all fully participating\n+     *    members (including itself) by assigning the existing state and a task proposal\n+     *    in the BARRIER_RESULTS node with a new version id and then adding a node under the\n+     *    BARRIER_PARTICIPANTS directory.\n+     * 8. All members detecting the task request perform the task specified in the task\n+     *    proposal and inserting a result node under the BARRIER_RESULTS directory.\n+     * 9. When the set of BARRIER_RESULTS nodes is a superset of all members currently in the\n+     *    state machine, all members are considered to have reported in and each member can\n+     *    independently determine the outcome. If the proposal was a state change request,\n+     *    and all BARRIER_RESULTS nodes report SUCCESS each member applies the new state\n+     *    and then removes itself from BARRIER_PARTICIPANTS. If the proposal was a task\n+     *    request, then members remove themselves from BARRIER_PARTICIPANTS to acknowledge\n+     *    receipt of all task results.\n+     *10. Membership in the state machine can change as members join or fail. Membership is\n+     *    monitored by the SynchronizedStatesManager and changes are reported to each state\n+     *    machine instance. While members have joined but have not determined the current\n+     *    state, they report a NULL result for each state change or task proposal made by\n+     *    other members.\n+     *11. If any member reports FAILURE node under the BARRIER_RESULTS directory after a\n+     *    state change request proposal, the new state is not applied by any member.\n+     *12. Each member that has processed BARRIER_RESULTS removes its ephemeral node from the\n+     *    BARRIER_PARTICIPANTS directory node.\n+     *13. The initiator of a state change or task releases the lock node when 9 is satisfied.\n+     *14. A member is not told they have the lock node until the last member releases the\n+     *    lock node AND all ephemeral nodes under the BARRIER_PARTICIPANTS node are gone,\n+     *    indicating that all members waiting on the outcome of the last state change or\n+     *    task results have processed the previous outcome\n+     */\n+\n+    public abstract class StateMachineInstance {\n+        protected final String m_stateMachineId;\n+        private Set<String> m_knownMembers;\n+        private boolean m_membershipChangePending = false;\n+        private final String m_statePath;\n+        private final String m_lockPath;\n+        private final String m_barrierResultsPath;\n+        private final String m_myResultPath;\n+        private final String m_barrierParticipantsPath;\n+        private final String m_myPartiticpantsPath;\n+        private boolean m_stateChangeInitiator = false;\n+        private String m_ourDistributedLockName = null;\n+        private String m_lockWaitingOn = null;\n+        private final VoltLogger m_log;\n+        private ByteBuffer m_requestedInitialState = ByteBuffer.allocate(0);\n+        private ByteBuffer m_synchronizedState = null;\n+        private ByteBuffer m_pendingProposal = null;\n+        private REQUEST_TYPE m_currentRequestType = REQUEST_TYPE.INITIALIZING;\n+        private int m_currentParticipants = 0;\n+        private Set<String> m_memberResults = null;\n+        private int m_lastProposalVersion = 0;\n+\n+        private final Lock m_mutex = new ReentrantLock();\n+        private int m_mutexLockedCnt = 0;\n+        private final ThreadLocal<Boolean> m_mutexLocked = new ThreadLocal<Boolean>() {\n+            @Override\n+            protected Boolean initialValue()\n+            {\n+                return new Boolean(false);\n+            }\n+        };\n+        private int m_mutexLine0 = 0;\n+        private int m_mutexLine1 = 0;\n+\n+        private void lockLocalState() {\n+            m_mutex.lock();\n+            assert(m_mutexLockedCnt == 0);\n+            m_mutexLockedCnt++;\n+            m_mutexLocked.set(new Boolean(true));\n+            m_mutexLine1 = m_mutexLine0;\n+            StackTraceElement l = new Exception().getStackTrace()[1];\n+            m_mutexLine0 = l.getLineNumber();\n+        }\n+\n+        // This wrapper resolves getStackTrace correctly when debugging locking\n+        private void lockLocalStateForLockRunner() {\n+            lockLocalState();\n+        }\n+\n+        // This wrapper resolves getStackTrace correctly when debugging locking\n+        private void lockLocalStateForResultsRunner() {\n+            lockLocalState();\n+        }\n+\n+        // This wrapper resolves getStackTrace correctly when debugging locking\n+        private void lockLocalStateForParticipantRunner() {\n+            lockLocalState();\n+        }\n+\n+        private void unlockLocalState() {\n+            assert(m_mutexLockedCnt == 1);\n+            m_mutexLockedCnt--;\n+            m_mutexLocked.set(new Boolean(false));\n+            m_mutex.unlock();\n+        }\n+\n+        protected boolean isLocalStateLocked() {\n+            return m_mutexLocked.get();\n+//            return true;\n+        }\n+\n+        protected boolean isLocalStateUnlocked() {\n+          return !m_mutexLocked.get();\n+//            return true;\n+        }\n+\n+        private class LockWatcher implements Watcher {\n+\n+            @Override\n+            public void process(final WatchedEvent event) {\n+                try {\n+                    if (!m_done.get()) {\n+                        m_shared_es.submit(HandlerForDistributedLockEvent);\n+                    }\n+                } catch (RejectedExecutionException e) {\n+                }\n+            }\n+        }\n+\n+        private final LockWatcher m_lockWatcher = new LockWatcher();\n+\n+        private class BarrierParticipantsWatcher implements Watcher {\n+\n+            @Override\n+            public void process(final WatchedEvent event) {\n+                try {\n+                    if (!m_done.get()) {\n+                        m_shared_es.submit(HandlerForBarrierPartiticpantsEvent);\n+                    }\n+                } catch (RejectedExecutionException e) {\n+                }\n+            }\n+        }\n+\n+        class StateChangeRequest {\n+            public REQUEST_TYPE m_requestType;\n+            public ByteBuffer m_previousState;\n+            public ByteBuffer m_proposal;\n+\n+            private StateChangeRequest(REQUEST_TYPE requestType, ByteBuffer previousState, ByteBuffer proposedState) {\n+                m_requestType = requestType;\n+                m_previousState = previousState;\n+                m_proposal = proposedState;\n+            }\n+        }\n+\n+        private final BarrierParticipantsWatcher m_barrierParticipantsWatcher = new BarrierParticipantsWatcher();\n+\n+        private class BarrierResultsWatcher implements Watcher {\n+\n+            @Override\n+            public void process(final WatchedEvent event) {\n+                try {\n+                    if (!m_done.get()) {\n+                        m_shared_es.submit(HandlerForBarrierResultsEvent);\n+                    }\n+                } catch (RejectedExecutionException e) {\n+                }\n+            }\n+        };\n+\n+        private final BarrierResultsWatcher m_barrierResultsWatcher = new BarrierResultsWatcher();\n+\n+        public StateMachineInstance(String instanceName, VoltLogger logger) {\n+            assert(!instanceName.equals(m_memberNode));","path":"src/frontend/org/voltcore/zk/SynchronizedStatesManager.java","position":306,"original_position":302,"commit_id":"14211f1bc97cf3ed6531927efeeb3abd9b6d8648","original_commit_id":"e778e70c4ccb06bc8cabf980cb8132e2476f87f8","user":{"login":"wweiss-voltdb","id":6462615,"avatar_url":"https://avatars.githubusercontent.com/u/6462615?v=3","gravatar_id":"","url":"https://api.github.com/users/wweiss-voltdb","html_url":"https://github.com/wweiss-voltdb","followers_url":"https://api.github.com/users/wweiss-voltdb/followers","following_url":"https://api.github.com/users/wweiss-voltdb/following{/other_user}","gists_url":"https://api.github.com/users/wweiss-voltdb/gists{/gist_id}","starred_url":"https://api.github.com/users/wweiss-voltdb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wweiss-voltdb/subscriptions","organizations_url":"https://api.github.com/users/wweiss-voltdb/orgs","repos_url":"https://api.github.com/users/wweiss-voltdb/repos","events_url":"https://api.github.com/users/wweiss-voltdb/events{/privacy}","received_events_url":"https://api.github.com/users/wweiss-voltdb/received_events","type":"User","site_admin":false},"body":"Done","created_at":"2015-01-19T17:27:27Z","updated_at":"2015-01-19T17:27:27Z","html_url":"https://github.com/VoltDB/voltdb/pull/1911#discussion_r23174150","pull_request_url":"https://api.github.com/repos/VoltDB/voltdb/pulls/1911","_links":{"self":{"href":"https://api.github.com/repos/VoltDB/voltdb/pulls/comments/23174150"},"html":{"href":"https://github.com/VoltDB/voltdb/pull/1911#discussion_r23174150"},"pull_request":{"href":"https://api.github.com/repos/VoltDB/voltdb/pulls/1911"}}},"pull_request":{"url":"https://api.github.com/repos/VoltDB/voltdb/pulls/1911","id":26488664,"html_url":"https://github.com/VoltDB/voltdb/pull/1911","diff_url":"https://github.com/VoltDB/voltdb/pull/1911.diff","patch_url":"https://github.com/VoltDB/voltdb/pull/1911.patch","issue_url":"https://api.github.com/repos/VoltDB/voltdb/issues/1911","number":1911,"state":"open","locked":false,"title":"Eng 7099 disable dr on primary","user":{"login":"wweiss-voltdb","id":6462615,"avatar_url":"https://avatars.githubusercontent.com/u/6462615?v=3","gravatar_id":"","url":"https://api.github.com/users/wweiss-voltdb","html_url":"https://github.com/wweiss-voltdb","followers_url":"https://api.github.com/users/wweiss-voltdb/followers","following_url":"https://api.github.com/users/wweiss-voltdb/following{/other_user}","gists_url":"https://api.github.com/users/wweiss-voltdb/gists{/gist_id}","starred_url":"https://api.github.com/users/wweiss-voltdb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wweiss-voltdb/subscriptions","organizations_url":"https://api.github.com/users/wweiss-voltdb/orgs","repos_url":"https://api.github.com/users/wweiss-voltdb/repos","events_url":"https://api.github.com/users/wweiss-voltdb/events{/privacy}","received_events_url":"https://api.github.com/users/wweiss-voltdb/received_events","type":"User","site_admin":false},"body":"","created_at":"2014-12-22T23:51:28Z","updated_at":"2015-01-19T17:27:27Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e42cdf68e3ed88e91a715c0799f3ec92e187e819","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/VoltDB/voltdb/pulls/1911/commits","review_comments_url":"https://api.github.com/repos/VoltDB/voltdb/pulls/1911/comments","review_comment_url":"https://api.github.com/repos/VoltDB/voltdb/pulls/comments/{number}","comments_url":"https://api.github.com/repos/VoltDB/voltdb/issues/1911/comments","statuses_url":"https://api.github.com/repos/VoltDB/voltdb/statuses/14211f1bc97cf3ed6531927efeeb3abd9b6d8648","head":{"label":"VoltDB:ENG-7099-DisableDrOnPrimary","ref":"ENG-7099-DisableDrOnPrimary","sha":"14211f1bc97cf3ed6531927efeeb3abd9b6d8648","user":{"login":"VoltDB","id":703910,"avatar_url":"https://avatars.githubusercontent.com/u/703910?v=3","gravatar_id":"","url":"https://api.github.com/users/VoltDB","html_url":"https://github.com/VoltDB","followers_url":"https://api.github.com/users/VoltDB/followers","following_url":"https://api.github.com/users/VoltDB/following{/other_user}","gists_url":"https://api.github.com/users/VoltDB/gists{/gist_id}","starred_url":"https://api.github.com/users/VoltDB/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/VoltDB/subscriptions","organizations_url":"https://api.github.com/users/VoltDB/orgs","repos_url":"https://api.github.com/users/VoltDB/repos","events_url":"https://api.github.com/users/VoltDB/events{/privacy}","received_events_url":"https://api.github.com/users/VoltDB/received_events","type":"Organization","site_admin":false},"repo":{"id":2445970,"name":"voltdb","full_name":"VoltDB/voltdb","owner":{"login":"VoltDB","id":703910,"avatar_url":"https://avatars.githubusercontent.com/u/703910?v=3","gravatar_id":"","url":"https://api.github.com/users/VoltDB","html_url":"https://github.com/VoltDB","followers_url":"https://api.github.com/users/VoltDB/followers","following_url":"https://api.github.com/users/VoltDB/following{/other_user}","gists_url":"https://api.github.com/users/VoltDB/gists{/gist_id}","starred_url":"https://api.github.com/users/VoltDB/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/VoltDB/subscriptions","organizations_url":"https://api.github.com/users/VoltDB/orgs","repos_url":"https://api.github.com/users/VoltDB/repos","events_url":"https://api.github.com/users/VoltDB/events{/privacy}","received_events_url":"https://api.github.com/users/VoltDB/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/VoltDB/voltdb","description":"VoltDB","fork":false,"url":"https://api.github.com/repos/VoltDB/voltdb","forks_url":"https://api.github.com/repos/VoltDB/voltdb/forks","keys_url":"https://api.github.com/repos/VoltDB/voltdb/keys{/key_id}","collaborators_url":"https://api.github.com/repos/VoltDB/voltdb/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/VoltDB/voltdb/teams","hooks_url":"https://api.github.com/repos/VoltDB/voltdb/hooks","issue_events_url":"https://api.github.com/repos/VoltDB/voltdb/issues/events{/number}","events_url":"https://api.github.com/repos/VoltDB/voltdb/events","assignees_url":"https://api.github.com/repos/VoltDB/voltdb/assignees{/user}","branches_url":"https://api.github.com/repos/VoltDB/voltdb/branches{/branch}","tags_url":"https://api.github.com/repos/VoltDB/voltdb/tags","blobs_url":"https://api.github.com/repos/VoltDB/voltdb/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/VoltDB/voltdb/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/VoltDB/voltdb/git/refs{/sha}","trees_url":"https://api.github.com/repos/VoltDB/voltdb/git/trees{/sha}","statuses_url":"https://api.github.com/repos/VoltDB/voltdb/statuses/{sha}","languages_url":"https://api.github.com/repos/VoltDB/voltdb/languages","stargazers_url":"https://api.github.com/repos/VoltDB/voltdb/stargazers","contributors_url":"https://api.github.com/repos/VoltDB/voltdb/contributors","subscribers_url":"https://api.github.com/repos/VoltDB/voltdb/subscribers","subscription_url":"https://api.github.com/repos/VoltDB/voltdb/subscription","commits_url":"https://api.github.com/repos/VoltDB/voltdb/commits{/sha}","git_commits_url":"https://api.github.com/repos/VoltDB/voltdb/git/commits{/sha}","comments_url":"https://api.github.com/repos/VoltDB/voltdb/comments{/number}","issue_comment_url":"https://api.github.com/repos/VoltDB/voltdb/issues/comments/{number}","contents_url":"https://api.github.com/repos/VoltDB/voltdb/contents/{+path}","compare_url":"https://api.github.com/repos/VoltDB/voltdb/compare/{base}...{head}","merges_url":"https://api.github.com/repos/VoltDB/voltdb/merges","archive_url":"https://api.github.com/repos/VoltDB/voltdb/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/VoltDB/voltdb/downloads","issues_url":"https://api.github.com/repos/VoltDB/voltdb/issues{/number}","pulls_url":"https://api.github.com/repos/VoltDB/voltdb/pulls{/number}","milestones_url":"https://api.github.com/repos/VoltDB/voltdb/milestones{/number}","notifications_url":"https://api.github.com/repos/VoltDB/voltdb/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/VoltDB/voltdb/labels{/name}","releases_url":"https://api.github.com/repos/VoltDB/voltdb/releases{/id}","created_at":"2011-09-23T18:46:12Z","updated_at":"2015-01-19T16:14:44Z","pushed_at":"2015-01-19T17:25:10Z","git_url":"git://github.com/VoltDB/voltdb.git","ssh_url":"git@github.com:VoltDB/voltdb.git","clone_url":"https://github.com/VoltDB/voltdb.git","svn_url":"https://github.com/VoltDB/voltdb","homepage":"http://community.voltdb.com/","size":581065,"stargazers_count":428,"watchers_count":428,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":125,"mirror_url":null,"open_issues_count":22,"forks":125,"open_issues":22,"watchers":428,"default_branch":"master"}},"base":{"label":"VoltDB:dr-integration-prime","ref":"dr-integration-prime","sha":"8b0813a88b545939306f3e36ac8649542f23b6cb","user":{"login":"VoltDB","id":703910,"avatar_url":"https://avatars.githubusercontent.com/u/703910?v=3","gravatar_id":"","url":"https://api.github.com/users/VoltDB","html_url":"https://github.com/VoltDB","followers_url":"https://api.github.com/users/VoltDB/followers","following_url":"https://api.github.com/users/VoltDB/following{/other_user}","gists_url":"https://api.github.com/users/VoltDB/gists{/gist_id}","starred_url":"https://api.github.com/users/VoltDB/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/VoltDB/subscriptions","organizations_url":"https://api.github.com/users/VoltDB/orgs","repos_url":"https://api.github.com/users/VoltDB/repos","events_url":"https://api.github.com/users/VoltDB/events{/privacy}","received_events_url":"https://api.github.com/users/VoltDB/received_events","type":"Organization","site_admin":false},"repo":{"id":2445970,"name":"voltdb","full_name":"VoltDB/voltdb","owner":{"login":"VoltDB","id":703910,"avatar_url":"https://avatars.githubusercontent.com/u/703910?v=3","gravatar_id":"","url":"https://api.github.com/users/VoltDB","html_url":"https://github.com/VoltDB","followers_url":"https://api.github.com/users/VoltDB/followers","following_url":"https://api.github.com/users/VoltDB/following{/other_user}","gists_url":"https://api.github.com/users/VoltDB/gists{/gist_id}","starred_url":"https://api.github.com/users/VoltDB/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/VoltDB/subscriptions","organizations_url":"https://api.github.com/users/VoltDB/orgs","repos_url":"https://api.github.com/users/VoltDB/repos","events_url":"https://api.github.com/users/VoltDB/events{/privacy}","received_events_url":"https://api.github.com/users/VoltDB/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/VoltDB/voltdb","description":"VoltDB","fork":false,"url":"https://api.github.com/repos/VoltDB/voltdb","forks_url":"https://api.github.com/repos/VoltDB/voltdb/forks","keys_url":"https://api.github.com/repos/VoltDB/voltdb/keys{/key_id}","collaborators_url":"https://api.github.com/repos/VoltDB/voltdb/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/VoltDB/voltdb/teams","hooks_url":"https://api.github.com/repos/VoltDB/voltdb/hooks","issue_events_url":"https://api.github.com/repos/VoltDB/voltdb/issues/events{/number}","events_url":"https://api.github.com/repos/VoltDB/voltdb/events","assignees_url":"https://api.github.com/repos/VoltDB/voltdb/assignees{/user}","branches_url":"https://api.github.com/repos/VoltDB/voltdb/branches{/branch}","tags_url":"https://api.github.com/repos/VoltDB/voltdb/tags","blobs_url":"https://api.github.com/repos/VoltDB/voltdb/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/VoltDB/voltdb/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/VoltDB/voltdb/git/refs{/sha}","trees_url":"https://api.github.com/repos/VoltDB/voltdb/git/trees{/sha}","statuses_url":"https://api.github.com/repos/VoltDB/voltdb/statuses/{sha}","languages_url":"https://api.github.com/repos/VoltDB/voltdb/languages","stargazers_url":"https://api.github.com/repos/VoltDB/voltdb/stargazers","contributors_url":"https://api.github.com/repos/VoltDB/voltdb/contributors","subscribers_url":"https://api.github.com/repos/VoltDB/voltdb/subscribers","subscription_url":"https://api.github.com/repos/VoltDB/voltdb/subscription","commits_url":"https://api.github.com/repos/VoltDB/voltdb/commits{/sha}","git_commits_url":"https://api.github.com/repos/VoltDB/voltdb/git/commits{/sha}","comments_url":"https://api.github.com/repos/VoltDB/voltdb/comments{/number}","issue_comment_url":"https://api.github.com/repos/VoltDB/voltdb/issues/comments/{number}","contents_url":"https://api.github.com/repos/VoltDB/voltdb/contents/{+path}","compare_url":"https://api.github.com/repos/VoltDB/voltdb/compare/{base}...{head}","merges_url":"https://api.github.com/repos/VoltDB/voltdb/merges","archive_url":"https://api.github.com/repos/VoltDB/voltdb/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/VoltDB/voltdb/downloads","issues_url":"https://api.github.com/repos/VoltDB/voltdb/issues{/number}","pulls_url":"https://api.github.com/repos/VoltDB/voltdb/pulls{/number}","milestones_url":"https://api.github.com/repos/VoltDB/voltdb/milestones{/number}","notifications_url":"https://api.github.com/repos/VoltDB/voltdb/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/VoltDB/voltdb/labels{/name}","releases_url":"https://api.github.com/repos/VoltDB/voltdb/releases{/id}","created_at":"2011-09-23T18:46:12Z","updated_at":"2015-01-19T16:14:44Z","pushed_at":"2015-01-19T17:25:10Z","git_url":"git://github.com/VoltDB/voltdb.git","ssh_url":"git@github.com:VoltDB/voltdb.git","clone_url":"https://github.com/VoltDB/voltdb.git","svn_url":"https://github.com/VoltDB/voltdb","homepage":"http://community.voltdb.com/","size":581065,"stargazers_count":428,"watchers_count":428,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":125,"mirror_url":null,"open_issues_count":22,"forks":125,"open_issues":22,"watchers":428,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/VoltDB/voltdb/pulls/1911"},"html":{"href":"https://github.com/VoltDB/voltdb/pull/1911"},"issue":{"href":"https://api.github.com/repos/VoltDB/voltdb/issues/1911"},"comments":{"href":"https://api.github.com/repos/VoltDB/voltdb/issues/1911/comments"},"review_comments":{"href":"https://api.github.com/repos/VoltDB/voltdb/pulls/1911/comments"},"review_comment":{"href":"https://api.github.com/repos/VoltDB/voltdb/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/VoltDB/voltdb/pulls/1911/commits"},"statuses":{"href":"https://api.github.com/repos/VoltDB/voltdb/statuses/14211f1bc97cf3ed6531927efeeb3abd9b6d8648"}}}}