@Override public HighAvailabilityMemberState masterIsAvailable( HighAvailabilityMemberContext context, InstanceId masterId, URI masterHaURI) { if (masterId.equals(context.getMyId())) { throw new RuntimeException( "i (" + context.getMyId() + ") am trying to become a slave but " + "someone said i am available as master"); } if (masterId.equals(context.getElectedMasterId())) { // A member joined and we all got the same event return this; } throw new RuntimeException( "my (" + context.getMyId() + ") current master is " + context.getAvailableHaMaster() + " (elected as " + context.getElectedMasterId() + " but i got a " + "masterIsAvailable event for " + masterHaURI); }
@Override public HighAvailabilityMemberState masterIsElected( HighAvailabilityMemberContext context, InstanceId masterId) { if (masterId.equals(context.getMyId())) { return TO_MASTER; } if (masterId.equals(context.getElectedMasterId())) { return this; } return PENDING; }
@Override public HighAvailabilityMemberState masterIsElected( HighAvailabilityMemberContext context, InstanceId masterId) { if (masterId.equals(context.getElectedMasterId())) { // A member joined and we all got the same event return this; } if (masterId.equals(context.getMyId())) { return TO_MASTER; } // This may mean the master changed from the time we transitioned here return PENDING; }
@Override public HighAvailabilityMemberState masterIsAvailable( HighAvailabilityMemberContext context, InstanceId masterId, URI masterHaURI) { if (masterId.equals(context.getMyId())) { throw new RuntimeException("Cannot transition to MASTER directly from SLAVE state"); } else if (masterId.equals(context.getElectedMasterId())) { // this is just someone else that joined the cluster return this; } throw new RuntimeException( "Received a MasterIsAvailable event for " + masterId + " which is different from the current master (" + context.getElectedMasterId() + ") while in the SLAVE state (probably missed a MasterIsElected event)"); }
@Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MemberIsAvailable that = (MemberIsAvailable) o; if (!clusterUri.equals(that.clusterUri)) { return false; } if (!instanceId.equals(that.instanceId)) { return false; } if (!role.equals(that.role)) { return false; } if (!roleUri.equals(that.roleUri)) { return false; } return true; }
@Override public HighAvailabilityMemberState slaveIsAvailable( HighAvailabilityMemberContext context, InstanceId slaveId, URI slaveUri) { if (slaveId.equals(context.getMyId())) { throw new RuntimeException("Cannot go from pending to slave"); } return this; }
@Override public HighAvailabilityMemberState slaveIsAvailable( HighAvailabilityMemberContext context, InstanceId slaveId, URI slaveUri) { if (slaveId.equals(context.getMyId())) { return SLAVE; } return this; }
@Override public int hashCode() { int result = role.hashCode(); result = 31 * result + instanceId.hashCode(); result = 31 * result + clusterUri.hashCode(); result = 31 * result + roleUri.hashCode(); return result; }
@Override public HighAvailabilityMemberState slaveIsAvailable( HighAvailabilityMemberContext context, InstanceId slaveId, URI slaveUri) { if (slaveId.equals(context.getMyId())) { throw new RuntimeException("Cannot be master and transition to slave at the same time"); } return this; }
@Override public HighAvailabilityMemberState masterIsElected( HighAvailabilityMemberContext context, InstanceId masterId) { assert context.getAvailableHaMaster() == null; if (masterId.equals(context.getMyId()) && !context.isSlaveOnly()) { return TO_MASTER; } return PENDING; }
@Override public HighAvailabilityMemberState masterIsElected( HighAvailabilityMemberContext context, InstanceId masterId) { assert context.getAvailableHaMaster() == null; if (masterId.equals(context.getMyId())) { return this; } return PENDING; // everything still goes }
@Override public HighAvailabilityMemberState masterIsAvailable( HighAvailabilityMemberContext context, InstanceId masterId, URI masterHaURI) { // assert context.getAvailableMaster() == null; if (masterId.equals(context.getMyId())) { throw new RuntimeException( "Received a MasterIsAvailable event for my InstanceId while in" + " PENDING state"); } return TO_SLAVE; }
@Override public HighAvailabilityMemberState masterIsElected( HighAvailabilityMemberContext context, InstanceId masterId) { if (masterId.equals(context.getMyId())) { return this; } // This means we (probably) were disconnected and got back in the cluster // and we find out that we are not the master anymore. return PENDING; }
@Override public HighAvailabilityMemberState masterIsAvailable( HighAvailabilityMemberContext context, InstanceId masterId, URI masterHaURI) { if (masterId.equals(context.getMyId())) { return MASTER; } throw new RuntimeException( "Received a MasterIsAvailable event for instance " + masterId + " while in TO_MASTER state"); }
@Override public HighAvailabilityMemberState masterIsAvailable( HighAvailabilityMemberContext context, InstanceId masterId, URI masterHaURI) { if (masterId.equals(context.getMyId())) { return this; } throw new RuntimeException( "I, " + context.getMyId() + " got a masterIsAvailable for " + masterHaURI + " (id is " + masterId + " ) while in MASTER state. Probably missed a " + "MasterIsElected event."); }
private ClusterClient newClusterClient(InstanceId id) { Map<String, String> configMap = MapUtil.stringMap( ClusterSettings.initial_hosts.name(), cluster.getInitialHostsConfigString(), ClusterSettings.server_id.name(), String.valueOf(id.toIntegerIndex()), ClusterSettings.cluster_server.name(), "0.0.0.0:8888"); Config config = new Config( configMap, InternalAbstractGraphDatabase.Configuration.class, GraphDatabaseSettings.class); return new ClusterClient( new Monitors(), ClusterClient.adapt(config), new DevNullLoggingService(), new NotElectableElectionCredentialsProvider(), new ObjectStreamFactory(), new ObjectStreamFactory()); }
@Override public HeartbeatState handle( HeartbeatContext context, Message<HeartbeatMessage> message, MessageHolder outgoing) throws Throwable { switch (message.getMessageType()) { case i_am_alive: { HeartbeatMessage.IAmAliveState state = message.getPayload(); if (context.getClusterContext().isMe(state.getServer())) { break; } context.getClusterContext().getLogger(HeartbeatState.class).debug("Received " + state); if (state.getServer() == null) { break; } if (context.alive(state.getServer())) { // Send suspicions messages to all non-failed servers for (InstanceId aliveServer : context.getAlive()) { if (!aliveServer.equals(context.getClusterContext().getMyId())) { URI aliveServerUri = context.getClusterContext().configuration.getUriForId(aliveServer); outgoing.offer( Message.to( HeartbeatMessage.suspicions, aliveServerUri, new HeartbeatMessage.SuspicionsState( context.getSuspicionsFor(context.getClusterContext().getMyId())))); } } } context .getClusterContext() .timeouts .cancelTimeout(HeartbeatMessage.i_am_alive + "-" + state.getServer()); context .getClusterContext() .timeouts .setTimeout( HeartbeatMessage.i_am_alive + "-" + state.getServer(), timeout(HeartbeatMessage.timed_out, message, state.getServer())); // Check if this server knows something that we don't if (message.hasHeader("last-learned")) { long lastLearned = Long.parseLong(message.getHeader("last-learned")); if (lastLearned > context.getLearnerContext().getLastKnownLearnedInstanceInCluster()) { outgoing.offer(internal(LearnerMessage.catchUp, lastLearned)); } } break; } case timed_out: { InstanceId server = message.getPayload(); context .getClusterContext() .getLogger(HeartbeatState.class) .debug("Received timed out for server " + server); // Check if this node is no longer a part of the cluster if (context.getClusterContext().getConfiguration().getMembers().containsKey(server)) { context.suspect(server); context .getClusterContext() .timeouts .setTimeout( HeartbeatMessage.i_am_alive + "-" + server, timeout(HeartbeatMessage.timed_out, message, server)); // Send suspicions messages to all non-failed servers for (InstanceId aliveServer : context.getAlive()) { if (!aliveServer.equals(context.getClusterContext().getMyId())) { URI sendTo = context.getClusterContext().getConfiguration().getUriForId(aliveServer); outgoing.offer( Message.to( HeartbeatMessage.suspicions, sendTo, new HeartbeatMessage.SuspicionsState( context.getSuspicionsFor(context.getClusterContext().getMyId())))); } } } else { // If no longer part of cluster, then don't bother context.serverLeftCluster(server); } break; } case sendHeartbeat: { InstanceId to = message.getPayload(); // Check if this node is no longer a part of the cluster if (!context.getClusterContext().isMe(to) && context.getClusterContext().getConfiguration().getMembers().containsKey(to)) { URI toSendTo = context.getClusterContext().getConfiguration().getUriForId(to); // Send heartbeat message to given server outgoing.offer( to( HeartbeatMessage.i_am_alive, toSendTo, new HeartbeatMessage.IAmAliveState(context.getClusterContext().getMyId())) .setHeader( "last-learned", context.getLearnerContext().getLastLearnedInstanceId() + "")); // Set new timeout to send heartbeat to this host context .getClusterContext() .timeouts .setTimeout( HeartbeatMessage.sendHeartbeat + "-" + to, timeout(HeartbeatMessage.sendHeartbeat, message, to)); } break; } case reset_send_heartbeat: { InstanceId to = message.getPayload(); if (!context.getClusterContext().isMe(to)) { String timeoutName = HeartbeatMessage.sendHeartbeat + "-" + to; context.getClusterContext().timeouts.cancelTimeout(timeoutName); context .getClusterContext() .timeouts .setTimeout(timeoutName, timeout(HeartbeatMessage.sendHeartbeat, message, to)); } break; } case suspicions: { HeartbeatMessage.SuspicionsState suspicions = message.getPayload(); context .getClusterContext() .getLogger(HeartbeatState.class) .debug("Received suspicions as " + suspicions); URI from = new URI(message.getHeader(Message.FROM)); InstanceId fromId = context.getClusterContext().getConfiguration().getServerId(from); /* * Remove ourselves from the suspicions received - we just received a message, * it's not normal to be considered failed. Whatever it was, it was transient and now it has * passed. */ suspicions.getSuspicions().remove(context.getClusterContext().getMyId()); context.suspicions(fromId, suspicions.getSuspicions()); break; } case leave: { context.getClusterContext().getLogger(HeartbeatState.class).debug("Received leave"); return start; } case addHeartbeatListener: { context.addHeartbeatListener((HeartbeatListener) message.getPayload()); break; } case removeHeartbeatListener: { context.removeHeartbeatListener((HeartbeatListener) message.getPayload()); break; } } return this; }
private void startMember(InstanceId serverId) throws URISyntaxException, IOException { Clusters.Member member = spec.getMembers().get(serverId.toIntegerIndex() - 1); StringBuilder initialHosts = new StringBuilder(spec.getMembers().get(0).getHost()); for (int i = 1; i < spec.getMembers().size(); i++) { initialHosts.append(",").append(spec.getMembers().get(i).getHost()); } File parent = new File(root, name); URI clusterUri = new URI("cluster://" + member.getHost()); if (member.isFullHaMember()) { int clusterPort = clusterUri.getPort(); int haPort = clusterUri.getPort() + 3000; File storeDir = new File(parent, "server" + serverId); if (storeDirInitializer != null) { storeDirInitializer.initializeStoreDir(serverId.toIntegerIndex(), storeDir); } GraphDatabaseBuilder builder = dbFactory.newHighlyAvailableDatabaseBuilder(storeDir.getAbsolutePath()); builder.setConfig(ClusterSettings.cluster_name, name); builder.setConfig(ClusterSettings.initial_hosts, initialHosts.toString()); builder.setConfig(ClusterSettings.server_id, serverId + ""); builder.setConfig(ClusterSettings.cluster_server, "0.0.0.0:" + clusterPort); builder.setConfig(HaSettings.ha_server, ":" + haPort); builder.setConfig(OnlineBackupSettings.online_backup_enabled, Settings.FALSE); builder.setConfig(commonConfig); if (instanceConfig.containsKey(serverId.toIntegerIndex())) { builder.setConfig(instanceConfig.get(serverId.toIntegerIndex())); } config(builder, name, serverId); final HighlyAvailableGraphDatabaseProxy graphDatabase = new HighlyAvailableGraphDatabaseProxy(builder); members.put(serverId, graphDatabase); life.add( new LifecycleAdapter() { @Override public void stop() throws Throwable { graphDatabase.get().shutdown(); } }); } else { Map<String, String> config = MapUtil.stringMap( ClusterSettings.cluster_name.name(), name, ClusterSettings.initial_hosts.name(), initialHosts.toString(), ClusterSettings.server_id.name(), serverId + "", ClusterSettings.cluster_server.name(), "0.0.0.0:" + clusterUri.getPort(), GraphDatabaseSettings.store_dir.name(), new File(parent, "arbiter" + serverId).getAbsolutePath()); Config config1 = new Config( config, InternalAbstractGraphDatabase.Configuration.class, GraphDatabaseSettings.class); ObjectStreamFactory objectStreamFactory = new ObjectStreamFactory(); ClusterClient clusterClient = new ClusterClient( new Monitors(), ClusterClient.adapt(config1), NullLogService.getInstance(), new NotElectableElectionCredentialsProvider(), objectStreamFactory, objectStreamFactory); arbiters.add( new ClusterMembers( clusterClient, clusterClient, new ClusterMemberEvents() { @Override public void addClusterMemberListener(ClusterMemberListener listener) { // noop } @Override public void removeClusterMemberListener(ClusterMemberListener listener) { // noop } }, clusterClient.getServerId())); life.add(new FutureLifecycleAdapter<>(clusterClient)); } }
@Override public String toString() { return String.format( "MemberIsAvailable[ Role: %s, InstanceId: %s, Role URI: %s, Cluster URI: %s]", role, instanceId.toString(), roleUri.toString(), clusterUri.toString()); }