#comment 测试git仓库
parent
68f9799cad
commit
511c88009e
@ -0,0 +1,131 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<artifactId>jraft-parent</artifactId>
|
||||||
|
<groupId>com.alipay.sofa</groupId>
|
||||||
|
<version>1.3.10.bugfix</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>jraft-core</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<name>jraft-core ${project.version}</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- junit -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit-dep</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.findbugs</groupId>
|
||||||
|
<artifactId>jsr305</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- rocksdb-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.rocksdb</groupId>
|
||||||
|
<artifactId>rocksdbjni</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- jna -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.java.dev.jna</groupId>
|
||||||
|
<artifactId>jna</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- jctools -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jctools</groupId>
|
||||||
|
<artifactId>jctools-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- mock -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-all</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.powermock</groupId>
|
||||||
|
<artifactId>powermock-api-mockito</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.powermock</groupId>
|
||||||
|
<artifactId>powermock-module-junit4</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- log -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-api</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-slf4j-impl</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-jcl</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.lmax</groupId>
|
||||||
|
<artifactId>disruptor</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.protobuf</groupId>
|
||||||
|
<artifactId>protobuf-java</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- commons -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-lang</groupId>
|
||||||
|
<artifactId>commons-lang</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- bolt -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alipay.sofa</groupId>
|
||||||
|
<artifactId>bolt</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alipay.sofa</groupId>
|
||||||
|
<artifactId>hessian</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- metrics -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.dropwizard.metrics</groupId>
|
||||||
|
<artifactId>metrics-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- benchmark -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjdk.jmh</groupId>
|
||||||
|
<artifactId>jmh-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjdk.jmh</groupId>
|
||||||
|
<artifactId>jmh-generator-annprocess</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.option.CliOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client command-line service
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-09 4:05:35 PM
|
||||||
|
*/
|
||||||
|
public interface CliService extends Lifecycle<CliOptions> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new peer into the replicating group which consists of |conf|.
|
||||||
|
* return OK status when success.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf current configuration
|
||||||
|
* @param peer peer to add
|
||||||
|
* @return operation status
|
||||||
|
*/
|
||||||
|
Status addPeer(final String groupId, final Configuration conf, final PeerId peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a peer from the replicating group which consists of |conf|.
|
||||||
|
* return OK status when success.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf current configuration
|
||||||
|
* @param peer peer to remove
|
||||||
|
* @return operation status
|
||||||
|
*/
|
||||||
|
Status removePeer(final String groupId, final Configuration conf, final PeerId peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gracefully change the peers of the replication group.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf current configuration
|
||||||
|
* @param newPeers new peers to change
|
||||||
|
* @return operation status
|
||||||
|
*/
|
||||||
|
Status changePeers(final String groupId, final Configuration conf, final Configuration newPeers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the peer set of the target peer.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param peer target peer
|
||||||
|
* @param newPeers new peers to reset
|
||||||
|
* @return operation status
|
||||||
|
*/
|
||||||
|
Status resetPeer(final String groupId, final PeerId peer, final Configuration newPeers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add some new learners into the replicating group which consists of |conf|.
|
||||||
|
* return OK status when success.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf current configuration
|
||||||
|
* @param learners learner peers to add
|
||||||
|
* @return operation status
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Status addLearners(final String groupId, final Configuration conf, final List<PeerId> learners);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove some learners from the replicating group which consists of |conf|.
|
||||||
|
* return OK status when success.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf current configuration
|
||||||
|
* @param learners learner peers to remove
|
||||||
|
* @return operation status
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Status removeLearners(final String groupId, final Configuration conf, final List<PeerId> learners);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the specified learner to follower of |conf|.
|
||||||
|
* return OK status when success.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf current configuration
|
||||||
|
* @param learner learner peer
|
||||||
|
* @return operation status
|
||||||
|
* @since 1.3.8
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Status learner2Follower(final String groupId, final Configuration conf, final PeerId learner);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update learners set in the replicating group which consists of |conf|.
|
||||||
|
* return OK status when success.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf current configuration
|
||||||
|
* @param learners learner peers to set
|
||||||
|
* @return operation status
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Status resetLearners(final String groupId, final Configuration conf, final List<PeerId> learners);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfer the leader of the replication group to the target peer
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf current configuration
|
||||||
|
* @param peer target peer of new leader
|
||||||
|
* @return operation status
|
||||||
|
*/
|
||||||
|
Status transferLeader(final String groupId, final Configuration conf, final PeerId peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask the peer to dump a snapshot immediately.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param peer target peer
|
||||||
|
* @return operation status
|
||||||
|
*/
|
||||||
|
Status snapshot(final String groupId, final PeerId peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the leader of the replication group.
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf configuration
|
||||||
|
* @param leaderId id of leader
|
||||||
|
* @return operation status
|
||||||
|
*/
|
||||||
|
Status getLeader(final String groupId, final Configuration conf, final PeerId leaderId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask all peers of the replication group.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf target peers configuration
|
||||||
|
* @return all peers of the replication group
|
||||||
|
*/
|
||||||
|
List<PeerId> getPeers(final String groupId, final Configuration conf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask all alive peers of the replication group.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf target peers configuration
|
||||||
|
* @return all alive peers of the replication group
|
||||||
|
*/
|
||||||
|
List<PeerId> getAlivePeers(final String groupId, final Configuration conf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask all learners of the replication group.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf target peers configuration
|
||||||
|
* @return all learners of the replication group
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
List<PeerId> getLearners(final String groupId, final Configuration conf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask all alive learners of the replication group.
|
||||||
|
*
|
||||||
|
* @param groupId the raft group id
|
||||||
|
* @param conf target peers configuration
|
||||||
|
* @return all alive learners of the replication group
|
||||||
|
*/
|
||||||
|
List<PeerId> getAliveLearners(final String groupId, final Configuration conf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Balance the number of leaders.
|
||||||
|
*
|
||||||
|
* @param balanceGroupIds all raft group ids to balance
|
||||||
|
* @param conf configuration of all nodes
|
||||||
|
* @param balancedLeaderIds the result of all balanced leader ids
|
||||||
|
* @return operation status
|
||||||
|
*/
|
||||||
|
Status rebalance(final Set<String> balanceGroupIds, final Configuration conf,
|
||||||
|
final Map<String, PeerId> balancedLeaderIds);
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback closure.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-03 11:07:05 AM
|
||||||
|
*/
|
||||||
|
public interface Closure {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when task is done.
|
||||||
|
*
|
||||||
|
* @param status the task status.
|
||||||
|
*/
|
||||||
|
void run(final Status status);
|
||||||
|
}
|
||||||
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.closure.LoadSnapshotClosure;
|
||||||
|
import com.alipay.sofa.jraft.closure.SaveSnapshotClosure;
|
||||||
|
import com.alipay.sofa.jraft.entity.LeaderChangeContext;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftException;
|
||||||
|
import com.alipay.sofa.jraft.option.FSMCallerOptions;
|
||||||
|
import com.alipay.sofa.jraft.util.Describer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finite state machine caller.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-03 11:07:52 AM
|
||||||
|
*/
|
||||||
|
public interface FSMCaller extends Lifecycle<FSMCallerOptions>, Describer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on lastAppliedLogIndex update events.
|
||||||
|
*
|
||||||
|
* @author dennis
|
||||||
|
*/
|
||||||
|
interface LastAppliedLogIndexListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when lastAppliedLogIndex updated.
|
||||||
|
*
|
||||||
|
* @param lastAppliedLogIndex the log index of last applied
|
||||||
|
*/
|
||||||
|
void onApplied(final long lastAppliedLogIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a LastAppliedLogIndexListener.
|
||||||
|
*/
|
||||||
|
void addLastAppliedLogIndexListener(final LastAppliedLogIndexListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when log entry committed
|
||||||
|
*
|
||||||
|
* @param committedIndex committed log index
|
||||||
|
*/
|
||||||
|
boolean onCommitted(final long committedIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after loading snapshot.
|
||||||
|
*
|
||||||
|
* @param done callback
|
||||||
|
*/
|
||||||
|
boolean onSnapshotLoad(final LoadSnapshotClosure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after saving snapshot.
|
||||||
|
*
|
||||||
|
* @param done callback
|
||||||
|
*/
|
||||||
|
boolean onSnapshotSave(final SaveSnapshotClosure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the leader stops.
|
||||||
|
*
|
||||||
|
* @param status status info
|
||||||
|
*/
|
||||||
|
boolean onLeaderStop(final Status status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the leader starts.
|
||||||
|
*
|
||||||
|
* @param term current term
|
||||||
|
*/
|
||||||
|
boolean onLeaderStart(final long term);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when start following a leader.
|
||||||
|
*
|
||||||
|
* @param ctx context of leader change
|
||||||
|
*/
|
||||||
|
boolean onStartFollowing(final LeaderChangeContext ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when stop following a leader.
|
||||||
|
*
|
||||||
|
* @param ctx context of leader change
|
||||||
|
*/
|
||||||
|
boolean onStopFollowing(final LeaderChangeContext ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when error happens.
|
||||||
|
*
|
||||||
|
* @param error error info
|
||||||
|
*/
|
||||||
|
boolean onError(final RaftException error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last log entry index to apply state machine.
|
||||||
|
*/
|
||||||
|
long getLastAppliedIndex();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after shutdown to wait it terminates.
|
||||||
|
*
|
||||||
|
* @throws InterruptedException if the current thread is interrupted
|
||||||
|
* while waiting
|
||||||
|
*/
|
||||||
|
void join() throws InterruptedException;
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterator over a batch of committed tasks.
|
||||||
|
* @see StateMachine#onApply(Iterator)
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-03 3:20:15 PM
|
||||||
|
*/
|
||||||
|
public interface Iterator extends java.util.Iterator<ByteBuffer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the data whose content is the same as what was passed to
|
||||||
|
* Node#apply(Task) in the leader node.
|
||||||
|
*/
|
||||||
|
ByteBuffer getData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a unique and monotonically increasing identifier of the current task:
|
||||||
|
* - Uniqueness guarantees that committed tasks in different peers with
|
||||||
|
* the same index are always the same and kept unchanged.
|
||||||
|
* - Monotonicity guarantees that for any index pair i, j (i < j), task
|
||||||
|
* at index |i| must be applied before task at index |j| in all the
|
||||||
|
* peers from the group.
|
||||||
|
*/
|
||||||
|
long getIndex();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the term of the leader which to task was applied to.
|
||||||
|
*/
|
||||||
|
long getTerm();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If done() is non-NULL, you must call done()->Run() after applying this
|
||||||
|
* task no matter this operation succeeds or fails, otherwise the
|
||||||
|
* corresponding resources would leak.
|
||||||
|
*
|
||||||
|
* If this task is proposed by this Node when it was the leader of this
|
||||||
|
* group and the leadership has not changed before this point, done() is
|
||||||
|
* exactly what was passed to Node#apply(Task) which may stand for some
|
||||||
|
* continuation (such as respond to the client) after updating the
|
||||||
|
* StateMachine with the given task. Otherwise done() must be NULL.
|
||||||
|
* */
|
||||||
|
Closure done();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when some critical error occurred. And we will consider the last
|
||||||
|
* |ntail| tasks (starting from the last iterated one) as not applied. After
|
||||||
|
* this point, no further changes on the StateMachine as well as the Node
|
||||||
|
* would be allowed and you should try to repair this replica or just drop it.
|
||||||
|
*
|
||||||
|
* @param ntail the number of tasks (starting from the last iterated one) considered as not to be applied.
|
||||||
|
* @param st Status to describe the detail of the error.
|
||||||
|
*/
|
||||||
|
void setErrorAndRollback(final long ntail, final Status st);
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryCodecFactory;
|
||||||
|
import com.alipay.sofa.jraft.option.NodeOptions;
|
||||||
|
import com.alipay.sofa.jraft.option.RaftOptions;
|
||||||
|
import com.alipay.sofa.jraft.storage.LogStorage;
|
||||||
|
import com.alipay.sofa.jraft.storage.RaftMetaStorage;
|
||||||
|
import com.alipay.sofa.jraft.storage.SnapshotStorage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract factory to create services for SOFAJRaft.
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
* @since 1.2.6
|
||||||
|
*/
|
||||||
|
public interface JRaftServiceFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a raft log storage.
|
||||||
|
* @param uri The log storage uri from {@link NodeOptions#getSnapshotUri()}
|
||||||
|
* @param raftOptions the raft options.
|
||||||
|
* @return storage to store raft log entries.
|
||||||
|
*/
|
||||||
|
LogStorage createLogStorage(final String uri, final RaftOptions raftOptions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a raft snapshot storage
|
||||||
|
* @param uri The snapshot storage uri from {@link NodeOptions#getSnapshotUri()}
|
||||||
|
* @param raftOptions the raft options.
|
||||||
|
* @return storage to store state machine snapshot.
|
||||||
|
*/
|
||||||
|
SnapshotStorage createSnapshotStorage(final String uri, final RaftOptions raftOptions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a raft meta storage.
|
||||||
|
* @param uri The meta storage uri from {@link NodeOptions#getRaftMetaUri()}
|
||||||
|
* @param raftOptions the raft options.
|
||||||
|
* @return meta storage to store raft meta info.
|
||||||
|
*/
|
||||||
|
RaftMetaStorage createRaftMetaStorage(final String uri, final RaftOptions raftOptions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a log entry codec factory.
|
||||||
|
* @return a codec factory to create encoder/decoder for raft log entry.
|
||||||
|
*/
|
||||||
|
LogEntryCodecFactory createLogEntryCodecFactory();
|
||||||
|
}
|
||||||
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.SynchronousQueue;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
import com.alipay.sofa.jraft.core.NodeImpl;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.option.BootstrapOptions;
|
||||||
|
import com.alipay.sofa.jraft.util.Endpoint;
|
||||||
|
import com.alipay.sofa.jraft.util.JRaftServiceLoader;
|
||||||
|
import com.alipay.sofa.jraft.util.NamedThreadFactory;
|
||||||
|
import com.alipay.sofa.jraft.util.ThreadPoolUtil;
|
||||||
|
import com.alipay.sofa.jraft.util.timer.RaftTimerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some helper methods for jraft usage.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-23 3:48:45 PM
|
||||||
|
*/
|
||||||
|
public final class JRaftUtils {
|
||||||
|
|
||||||
|
private final static RaftTimerFactory TIMER_FACTORY = JRaftServiceLoader.load(RaftTimerFactory.class) //
|
||||||
|
.first();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get raft timer factory.
|
||||||
|
*
|
||||||
|
* @return {@link RaftTimerFactory}
|
||||||
|
*/
|
||||||
|
public static RaftTimerFactory raftTimerFactory() {
|
||||||
|
return TIMER_FACTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrap a non-empty raft node.
|
||||||
|
*
|
||||||
|
* @param opts options of bootstrap
|
||||||
|
* @return true if bootstrap success
|
||||||
|
*/
|
||||||
|
public static boolean bootstrap(final BootstrapOptions opts) throws InterruptedException {
|
||||||
|
final NodeImpl node = new NodeImpl();
|
||||||
|
final boolean ret = node.bootstrap(opts);
|
||||||
|
node.shutdown();
|
||||||
|
node.join();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a executor with size.
|
||||||
|
*
|
||||||
|
* @param prefix thread name prefix
|
||||||
|
* @param number thread number
|
||||||
|
* @return a new {@link ThreadPoolExecutor} instance
|
||||||
|
*/
|
||||||
|
public static Executor createExecutor(final String prefix, final int number) {
|
||||||
|
if (number <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ThreadPoolUtil.newBuilder() //
|
||||||
|
.poolName(prefix) //
|
||||||
|
.enableMetric(true) //
|
||||||
|
.coreThreads(number) //
|
||||||
|
.maximumThreads(number) //
|
||||||
|
.keepAliveSeconds(60L) //
|
||||||
|
.workQueue(new SynchronousQueue<>()) //
|
||||||
|
.threadFactory(createThreadFactory(prefix)) //
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a thread factory.
|
||||||
|
*
|
||||||
|
* @param prefixName the prefix name of thread
|
||||||
|
* @return a new {@link ThreadFactory} instance
|
||||||
|
*
|
||||||
|
* @since 0.0.3
|
||||||
|
*/
|
||||||
|
public static ThreadFactory createThreadFactory(final String prefixName) {
|
||||||
|
return new NamedThreadFactory(prefixName, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a configuration from a string in the form of "host1:port1[:idx],host2:port2[:idx]......",
|
||||||
|
* returns a empty configuration when string is blank.
|
||||||
|
*/
|
||||||
|
public static Configuration getConfiguration(final String s) {
|
||||||
|
final Configuration conf = new Configuration();
|
||||||
|
if (StringUtils.isBlank(s)) {
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
|
if (conf.parse(s)) {
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Invalid conf str:" + s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a peer from a string in the form of "host:port[:idx]",
|
||||||
|
* returns a empty peer when string is blank.
|
||||||
|
*/
|
||||||
|
public static PeerId getPeerId(final String s) {
|
||||||
|
final PeerId peer = new PeerId();
|
||||||
|
if (StringUtils.isBlank(s)) {
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
if (peer.parse(s)) {
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Invalid peer str:" + s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Endpoint instance from a string in the form of "host:port",
|
||||||
|
* returns null when string is blank.
|
||||||
|
*/
|
||||||
|
public static Endpoint getEndPoint(final String s) {
|
||||||
|
if (StringUtils.isBlank(s)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final String[] tmps = StringUtils.split(s, ':');
|
||||||
|
if (tmps.length != 2) {
|
||||||
|
throw new IllegalArgumentException("Invalid endpoint string: " + s);
|
||||||
|
}
|
||||||
|
return new Endpoint(tmps[0], Integer.parseInt(tmps[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private JRaftUtils() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service life cycle mark interface.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-12 3:47:04 PM
|
||||||
|
*/
|
||||||
|
public interface Lifecycle<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the service.
|
||||||
|
*
|
||||||
|
* @return true when successes.
|
||||||
|
*/
|
||||||
|
boolean init(final T opts);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispose the resources for service.
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
}
|
||||||
@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.closure.ReadIndexClosure;
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
import com.alipay.sofa.jraft.core.NodeMetrics;
|
||||||
|
import com.alipay.sofa.jraft.core.Replicator;
|
||||||
|
import com.alipay.sofa.jraft.core.State;
|
||||||
|
import com.alipay.sofa.jraft.entity.NodeId;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.entity.Task;
|
||||||
|
import com.alipay.sofa.jraft.entity.UserLog;
|
||||||
|
import com.alipay.sofa.jraft.error.LogIndexOutOfBoundsException;
|
||||||
|
import com.alipay.sofa.jraft.error.LogNotFoundException;
|
||||||
|
import com.alipay.sofa.jraft.option.NodeOptions;
|
||||||
|
import com.alipay.sofa.jraft.option.RaftOptions;
|
||||||
|
import com.alipay.sofa.jraft.util.Describer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A raft replica node.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-03 4:06:55 PM
|
||||||
|
*/
|
||||||
|
public interface Node extends Lifecycle<NodeOptions>, Describer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the leader peer id for redirect, null if absent.
|
||||||
|
*/
|
||||||
|
PeerId getLeaderId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current node id.
|
||||||
|
*/
|
||||||
|
NodeId getNodeId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the node metrics, only valid when node option {@link NodeOptions#isEnableMetrics()} is true.
|
||||||
|
*/
|
||||||
|
NodeMetrics getNodeMetrics();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the raft group id.
|
||||||
|
*/
|
||||||
|
String getGroupId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the node options.
|
||||||
|
*/
|
||||||
|
NodeOptions getOptions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the raft options
|
||||||
|
*/
|
||||||
|
RaftOptions getRaftOptions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the node is leader.
|
||||||
|
*/
|
||||||
|
boolean isLeader();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the node is leader.
|
||||||
|
* @param blocking if true, will be blocked until the node finish it's state change
|
||||||
|
*/
|
||||||
|
boolean isLeader(final boolean blocking);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shutdown local replica node.
|
||||||
|
*
|
||||||
|
* @param done callback
|
||||||
|
*/
|
||||||
|
void shutdown(final Closure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block the thread until the node is successfully stopped.
|
||||||
|
*
|
||||||
|
* @throws InterruptedException if the current thread is interrupted
|
||||||
|
* while waiting
|
||||||
|
*/
|
||||||
|
void join() throws InterruptedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Thread-safe and wait-free]
|
||||||
|
*
|
||||||
|
* Apply task to the replicated-state-machine
|
||||||
|
*
|
||||||
|
* About the ownership:
|
||||||
|
* |task.data|: for the performance consideration, we will take away the
|
||||||
|
* content. If you want keep the content, copy it before call
|
||||||
|
* this function
|
||||||
|
* |task.done|: If the data is successfully committed to the raft group. We
|
||||||
|
* will pass the ownership to #{@link StateMachine#onApply(Iterator)}.
|
||||||
|
* Otherwise we will specify the error and call it.
|
||||||
|
*
|
||||||
|
* @param task task to apply
|
||||||
|
*/
|
||||||
|
void apply(final Task task);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Thread-safe and wait-free]
|
||||||
|
*
|
||||||
|
* Starts a linearizable read-only query request with request context(optional,
|
||||||
|
* such as request id etc.) and closure. The closure will be called when the
|
||||||
|
* request is completed, and user can read data from state machine if the result
|
||||||
|
* status is OK.
|
||||||
|
*
|
||||||
|
* @param requestContext the context of request
|
||||||
|
* @param done callback
|
||||||
|
*
|
||||||
|
* @since 0.0.3
|
||||||
|
*/
|
||||||
|
void readIndex(final byte[] requestContext, final ReadIndexClosure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List peers of this raft group, only leader returns.
|
||||||
|
*
|
||||||
|
* [NOTE] <strong>when list_peers concurrency with {@link #addPeer(PeerId, Closure)}/{@link #removePeer(PeerId, Closure)},
|
||||||
|
* maybe return peers is staled. Because {@link #addPeer(PeerId, Closure)}/{@link #removePeer(PeerId, Closure)}
|
||||||
|
* immediately modify configuration in memory</strong>
|
||||||
|
*
|
||||||
|
* @return the peer list
|
||||||
|
*/
|
||||||
|
List<PeerId> listPeers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all alive peers of this raft group, only leader returns.</p>
|
||||||
|
*
|
||||||
|
* [NOTE] <strong>list_alive_peers is just a transient data (snapshot)
|
||||||
|
* and a short-term loss of response by the follower will cause it to
|
||||||
|
* temporarily not exist in this list.</strong>
|
||||||
|
*
|
||||||
|
* @return the alive peer list
|
||||||
|
* @since 1.2.6
|
||||||
|
*/
|
||||||
|
List<PeerId> listAlivePeers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all learners of this raft group, only leader returns.</p>
|
||||||
|
*
|
||||||
|
* [NOTE] <strong>when listLearners concurrency with {@link #addLearners(List, Closure)}/{@link #removeLearners(List, Closure)}/{@link #resetLearners(List, Closure)},
|
||||||
|
* maybe return peers is staled. Because {@link #addLearners(List, Closure)}/{@link #removeLearners(List, Closure)}/{@link #resetLearners(List, Closure)}
|
||||||
|
* immediately modify configuration in memory</strong>
|
||||||
|
*
|
||||||
|
* @return the learners set
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
List<PeerId> listLearners();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all alive learners of this raft group, only leader returns.</p>
|
||||||
|
*
|
||||||
|
* [NOTE] <strong>when listAliveLearners concurrency with {@link #addLearners(List, Closure)}/{@link #removeLearners(List, Closure)}/{@link #resetLearners(List, Closure)},
|
||||||
|
* maybe return peers is staled. Because {@link #addLearners(List, Closure)}/{@link #removeLearners(List, Closure)}/{@link #resetLearners(List, Closure)}
|
||||||
|
* immediately modify configuration in memory</strong>
|
||||||
|
*
|
||||||
|
* @return the alive learners set
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
List<PeerId> listAliveLearners();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new peer to the raft group. done.run() would be invoked after this
|
||||||
|
* operation finishes, describing the detailed result.
|
||||||
|
*
|
||||||
|
* @param peer peer to add
|
||||||
|
* @param done callback
|
||||||
|
*/
|
||||||
|
void addPeer(final PeerId peer, final Closure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the peer from the raft group. done.run() would be invoked after
|
||||||
|
* operation finishes, describing the detailed result.
|
||||||
|
*
|
||||||
|
* @param peer peer to remove
|
||||||
|
* @param done callback
|
||||||
|
*/
|
||||||
|
void removePeer(final PeerId peer, final Closure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the configuration of the raft group to |newPeers| , done.run()
|
||||||
|
* would be invoked after this operation finishes, describing the detailed result.
|
||||||
|
*
|
||||||
|
* @param newPeers new peers to change
|
||||||
|
* @param done callback
|
||||||
|
*/
|
||||||
|
void changePeers(final Configuration newPeers, final Closure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the configuration of this node individually, without any replication
|
||||||
|
* to other peers before this node becomes the leader. This function is
|
||||||
|
* supposed to be invoked when the majority of the replication group are
|
||||||
|
* dead and you'd like to revive the service in the consideration of
|
||||||
|
* availability.
|
||||||
|
* Notice that neither consistency nor consensus are guaranteed in this
|
||||||
|
* case, BE CAREFUL when dealing with this method.
|
||||||
|
*
|
||||||
|
* @param newPeers new peers
|
||||||
|
*/
|
||||||
|
Status resetPeers(final Configuration newPeers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add some new learners to the raft group. done.run() will be invoked after this
|
||||||
|
* operation finishes, describing the detailed result.
|
||||||
|
*
|
||||||
|
* @param learners learners to add
|
||||||
|
* @param done callback
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
void addLearners(final List<PeerId> learners, final Closure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove some learners from the raft group. done.run() will be invoked after this
|
||||||
|
* operation finishes, describing the detailed result.
|
||||||
|
*
|
||||||
|
* @param learners learners to remove
|
||||||
|
* @param done callback
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
void removeLearners(final List<PeerId> learners, final Closure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset learners in the raft group. done.run() will be invoked after this
|
||||||
|
* operation finishes, describing the detailed result.
|
||||||
|
*
|
||||||
|
* @param learners learners to set
|
||||||
|
* @param done callback
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
void resetLearners(final List<PeerId> learners, final Closure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a snapshot immediately if possible. done.run() would be invoked when
|
||||||
|
* the snapshot finishes, describing the detailed result.
|
||||||
|
*
|
||||||
|
* @param done callback
|
||||||
|
*/
|
||||||
|
void snapshot(final Closure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the election_timeout for the every node.
|
||||||
|
*
|
||||||
|
* @param electionTimeoutMs the timeout millis of election
|
||||||
|
*/
|
||||||
|
void resetElectionTimeoutMs(final int electionTimeoutMs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try transferring leadership to |peer|. If peer is ANY_PEER, a proper follower
|
||||||
|
* will be chosen as the leader for the next term.
|
||||||
|
* Returns 0 on success, -1 otherwise.
|
||||||
|
*
|
||||||
|
* @param peer the target peer of new leader
|
||||||
|
* @return operation status
|
||||||
|
*/
|
||||||
|
Status transferLeadershipTo(final PeerId peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the first committed user log from the given index.
|
||||||
|
* Return OK on success and user_log is assigned with the very data. Be awared
|
||||||
|
* that the user_log may be not the exact log at the given index, but the
|
||||||
|
* first available user log from the given index to lastCommittedIndex.
|
||||||
|
* Otherwise, appropriate errors are returned:
|
||||||
|
* - return ELOGDELETED when the log has been deleted;
|
||||||
|
* - return ENOMOREUSERLOG when we can't get a user log even reaching lastCommittedIndex.
|
||||||
|
* [NOTE] in consideration of safety, we use lastAppliedIndex instead of lastCommittedIndex
|
||||||
|
* in code implementation.
|
||||||
|
*
|
||||||
|
* @param index log index
|
||||||
|
* @return user log entry
|
||||||
|
* @throws LogNotFoundException the user log is deleted at index.
|
||||||
|
* @throws LogIndexOutOfBoundsException the special index is out of bounds.
|
||||||
|
*/
|
||||||
|
UserLog readCommittedUserLog(final long index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOFAJRaft users can implement the ReplicatorStateListener interface by themselves.
|
||||||
|
* So users can do their own logical operator in this listener when replicator created, destroyed or had some errors.
|
||||||
|
*
|
||||||
|
* @param replicatorStateListener added ReplicatorStateListener which is implemented by users.
|
||||||
|
*/
|
||||||
|
void addReplicatorStateListener(final Replicator.ReplicatorStateListener replicatorStateListener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End User can remove their implement the ReplicatorStateListener interface by themselves.
|
||||||
|
*
|
||||||
|
* @param replicatorStateListener need to remove the ReplicatorStateListener which has been added by users.
|
||||||
|
*/
|
||||||
|
void removeReplicatorStateListener(final Replicator.ReplicatorStateListener replicatorStateListener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all the ReplicatorStateListeners which have been added by users.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void clearReplicatorStateListeners();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ReplicatorStateListeners which have been added by users.
|
||||||
|
*
|
||||||
|
* @return node's replicatorStatueListeners which have been added by users.
|
||||||
|
*/
|
||||||
|
List<Replicator.ReplicatorStateListener> getReplicatorStatueListeners();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the node's target election priority value.
|
||||||
|
*
|
||||||
|
* @return node's target election priority value.
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
int getNodeTargetPriority();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the node's state.
|
||||||
|
*
|
||||||
|
* @return node's state.
|
||||||
|
* @since 1.3.8
|
||||||
|
*/
|
||||||
|
State getNodeState();
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.util.Describer;
|
||||||
|
import com.alipay.sofa.jraft.util.FileOutputSignalHandler;
|
||||||
|
import com.alipay.sofa.jraft.util.SystemPropertyUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jiachun.fjc
|
||||||
|
*/
|
||||||
|
public class NodeDescribeSignalHandler extends FileOutputSignalHandler {
|
||||||
|
|
||||||
|
private static Logger LOG = LoggerFactory.getLogger(NodeDescribeSignalHandler.class);
|
||||||
|
|
||||||
|
private static final String DIR = SystemPropertyUtil.get("jraft.signal.node.describe.dir", "");
|
||||||
|
private static final String BASE_NAME = "node_describe.log";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(final String signalName) {
|
||||||
|
final List<Node> nodes = NodeManager.getInstance().getAllNodes();
|
||||||
|
if (nodes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final File file = getOutputFile(DIR, BASE_NAME);
|
||||||
|
|
||||||
|
LOG.info("Describing raft nodes with signal: {} to file: {}.", signalName, file);
|
||||||
|
|
||||||
|
try (final PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file, true),
|
||||||
|
StandardCharsets.UTF_8))) {
|
||||||
|
final Describer.Printer printer = new Describer.DefaultPrinter(out);
|
||||||
|
for (final Node node : nodes) {
|
||||||
|
node.describe(printer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final IOException e) {
|
||||||
|
LOG.error("Fail to describe nodes: {}.", nodes, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.NodeId;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.util.Endpoint;
|
||||||
|
import com.alipay.sofa.jraft.util.OnlyForTest;
|
||||||
|
import com.alipay.sofa.jraft.util.Utils;
|
||||||
|
import com.alipay.sofa.jraft.util.concurrent.ConcurrentHashSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raft nodes manager.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-22 5:58:23 PM
|
||||||
|
*/
|
||||||
|
public class NodeManager {
|
||||||
|
|
||||||
|
private static final NodeManager INSTANCE = new NodeManager();
|
||||||
|
|
||||||
|
private final ConcurrentMap<NodeId, Node> nodeMap = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentMap<String, List<Node>> groupMap = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentHashSet<Endpoint> addrSet = new ConcurrentHashSet<>();
|
||||||
|
|
||||||
|
public static NodeManager getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true when RPC service is registered.
|
||||||
|
*/
|
||||||
|
public boolean serverExists(final Endpoint addr) {
|
||||||
|
if (addr.getIp().equals(Utils.IP_ANY)) {
|
||||||
|
return this.addrSet.contains(new Endpoint(Utils.IP_ANY, addr.getPort()));
|
||||||
|
}
|
||||||
|
return this.addrSet.contains(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a RPC service address.
|
||||||
|
*/
|
||||||
|
public boolean removeAddress(final Endpoint addr) {
|
||||||
|
return this.addrSet.remove(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a RPC service address.
|
||||||
|
*/
|
||||||
|
public void addAddress(final Endpoint addr) {
|
||||||
|
this.addrSet.add(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a node.
|
||||||
|
*/
|
||||||
|
public boolean add(final Node node) {
|
||||||
|
// check address ok?
|
||||||
|
if (!serverExists(node.getNodeId().getPeerId().getEndpoint())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final NodeId nodeId = node.getNodeId();
|
||||||
|
if (this.nodeMap.putIfAbsent(nodeId, node) == null) {
|
||||||
|
final String groupId = node.getGroupId();
|
||||||
|
List<Node> nodes = this.groupMap.get(groupId);
|
||||||
|
if (nodes == null) {
|
||||||
|
nodes = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
List<Node> existsNode = this.groupMap.putIfAbsent(groupId, nodes);
|
||||||
|
if (existsNode != null) {
|
||||||
|
nodes = existsNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodes.add(node);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the states, for test
|
||||||
|
*/
|
||||||
|
@OnlyForTest
|
||||||
|
public void clear() {
|
||||||
|
this.groupMap.clear();
|
||||||
|
this.nodeMap.clear();
|
||||||
|
this.addrSet.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a node.
|
||||||
|
*/
|
||||||
|
public boolean remove(final Node node) {
|
||||||
|
if (this.nodeMap.remove(node.getNodeId(), node)) {
|
||||||
|
final List<Node> nodes = this.groupMap.get(node.getGroupId());
|
||||||
|
if (nodes != null) {
|
||||||
|
return nodes.remove(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get node by groupId and peer.
|
||||||
|
*/
|
||||||
|
public Node get(final String groupId, final PeerId peerId) {
|
||||||
|
return this.nodeMap.get(new NodeId(groupId, peerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all nodes in a raft group.
|
||||||
|
*/
|
||||||
|
public List<Node> getNodesByGroupId(final String groupId) {
|
||||||
|
return this.groupMap.get(groupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all nodes
|
||||||
|
*/
|
||||||
|
public List<Node> getAllNodes() {
|
||||||
|
return this.groupMap.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private NodeManager() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.core.NodeMetrics;
|
||||||
|
import com.alipay.sofa.jraft.util.FileOutputSignalHandler;
|
||||||
|
import com.alipay.sofa.jraft.util.MetricReporter;
|
||||||
|
import com.alipay.sofa.jraft.util.SystemPropertyUtil;
|
||||||
|
import com.codahale.metrics.MetricRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jiachun.fjc
|
||||||
|
*/
|
||||||
|
public class NodeMetricsSignalHandler extends FileOutputSignalHandler {
|
||||||
|
|
||||||
|
private static Logger LOG = LoggerFactory.getLogger(NodeMetricsSignalHandler.class);
|
||||||
|
|
||||||
|
private static final String DIR = SystemPropertyUtil.get("jraft.signal.node.metrics.dir", "");
|
||||||
|
private static final String BASE_NAME = "node_metrics.log";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(final String signalName) {
|
||||||
|
final List<Node> nodes = NodeManager.getInstance().getAllNodes();
|
||||||
|
if (nodes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final File file = getOutputFile(DIR, BASE_NAME);
|
||||||
|
|
||||||
|
LOG.info("Printing raft nodes metrics with signal: {} to file: {}.", signalName, file);
|
||||||
|
|
||||||
|
try (final PrintStream out = new PrintStream(new FileOutputStream(file, true))) {
|
||||||
|
for (final Node node : nodes) {
|
||||||
|
final NodeMetrics nodeMetrics = node.getNodeMetrics();
|
||||||
|
final MetricRegistry registry = nodeMetrics.getMetricRegistry();
|
||||||
|
if (registry == null) {
|
||||||
|
LOG.warn("Node: {} received a signal to print metric, but it does not have metric enabled.",
|
||||||
|
node);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final MetricReporter reporter = MetricReporter.forRegistry(registry) //
|
||||||
|
.outputTo(out) //
|
||||||
|
.prefixedWith("-- " + node.getNodeId()) //
|
||||||
|
.build();
|
||||||
|
reporter.report();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final IOException e) {
|
||||||
|
LOG.error("Fail to print nodes metrics: {}.", nodes, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,257 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.option.NodeOptions;
|
||||||
|
import com.alipay.sofa.jraft.option.RpcOptions;
|
||||||
|
import com.alipay.sofa.jraft.rpc.ProtobufMsgFactory;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RaftRpcServerFactory;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RpcServer;
|
||||||
|
import com.alipay.sofa.jraft.util.Endpoint;
|
||||||
|
import com.alipay.sofa.jraft.util.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A framework to implement a raft group service.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-08 7:53:03 PM
|
||||||
|
*/
|
||||||
|
public class RaftGroupService {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RaftGroupService.class);
|
||||||
|
|
||||||
|
static {
|
||||||
|
ProtobufMsgFactory.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
private volatile boolean started = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node serverId
|
||||||
|
*/
|
||||||
|
private PeerId serverId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node options
|
||||||
|
*/
|
||||||
|
private NodeOptions nodeOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The raft RPC server
|
||||||
|
*/
|
||||||
|
private RpcServer rpcServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we want to share the rpcServer instance, then we can't stop it when shutdown.
|
||||||
|
*/
|
||||||
|
private final boolean sharedRpcServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The raft group id
|
||||||
|
*/
|
||||||
|
private String groupId;
|
||||||
|
/**
|
||||||
|
* The raft node.
|
||||||
|
*/
|
||||||
|
private Node node;
|
||||||
|
|
||||||
|
public RaftGroupService(final String groupId, final PeerId serverId, final NodeOptions nodeOptions) {
|
||||||
|
this(groupId, serverId, nodeOptions, RaftRpcServerFactory.createRaftRpcServer(serverId.getEndpoint(),
|
||||||
|
JRaftUtils.createExecutor("RAFT-RPC-executor-", nodeOptions.getRaftRpcThreadPoolSize()),
|
||||||
|
JRaftUtils.createExecutor("CLI-RPC-executor-", nodeOptions.getCliRpcThreadPoolSize())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RaftGroupService(final String groupId, final PeerId serverId, final NodeOptions nodeOptions,
|
||||||
|
final RpcServer rpcServer) {
|
||||||
|
this(groupId, serverId, nodeOptions, rpcServer, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RaftGroupService(final String groupId, final PeerId serverId, final NodeOptions nodeOptions,
|
||||||
|
final RpcServer rpcServer, final boolean sharedRpcServer) {
|
||||||
|
super();
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.serverId = serverId;
|
||||||
|
this.nodeOptions = nodeOptions;
|
||||||
|
this.rpcServer = rpcServer;
|
||||||
|
this.sharedRpcServer = sharedRpcServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized Node getRaftNode() {
|
||||||
|
return this.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the raft group service, returns the raft node.
|
||||||
|
*/
|
||||||
|
public synchronized Node start() {
|
||||||
|
return start(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the raft group service, returns the raft node.
|
||||||
|
*
|
||||||
|
* @param startRpcServer whether to start RPC server.
|
||||||
|
*/
|
||||||
|
public synchronized Node start(final boolean startRpcServer) {
|
||||||
|
if (this.started) {
|
||||||
|
return this.node;
|
||||||
|
}
|
||||||
|
if (this.serverId == null || this.serverId.getEndpoint() == null
|
||||||
|
|| this.serverId.getEndpoint().equals(new Endpoint(Utils.IP_ANY, 0))) {
|
||||||
|
throw new IllegalArgumentException("Blank serverId:" + this.serverId);
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(this.groupId)) {
|
||||||
|
throw new IllegalArgumentException("Blank group id:" + this.groupId);
|
||||||
|
}
|
||||||
|
//Adds RPC server to Server.
|
||||||
|
NodeManager.getInstance().addAddress(this.serverId.getEndpoint());
|
||||||
|
|
||||||
|
this.node = RaftServiceFactory.createAndInitRaftNode(this.groupId, this.serverId, this.nodeOptions);
|
||||||
|
if (startRpcServer) {
|
||||||
|
this.rpcServer.init(null);
|
||||||
|
} else {
|
||||||
|
LOG.warn("RPC server is not started in RaftGroupService.");
|
||||||
|
}
|
||||||
|
this.started = true;
|
||||||
|
LOG.info("Start the RaftGroupService successfully.");
|
||||||
|
return this.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block thread to wait the server shutdown.
|
||||||
|
*
|
||||||
|
* @throws InterruptedException if the current thread is interrupted
|
||||||
|
* while waiting
|
||||||
|
*/
|
||||||
|
public synchronized void join() throws InterruptedException {
|
||||||
|
if (this.node != null) {
|
||||||
|
this.node.join();
|
||||||
|
this.node = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void shutdown() {
|
||||||
|
if (!this.started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.rpcServer != null) {
|
||||||
|
try {
|
||||||
|
if (!this.sharedRpcServer) {
|
||||||
|
this.rpcServer.shutdown();
|
||||||
|
}
|
||||||
|
} catch (final Exception ignored) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
this.rpcServer = null;
|
||||||
|
}
|
||||||
|
this.node.shutdown();
|
||||||
|
NodeManager.getInstance().removeAddress(this.serverId.getEndpoint());
|
||||||
|
this.started = false;
|
||||||
|
LOG.info("Stop the RaftGroupService successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when service is started.
|
||||||
|
*/
|
||||||
|
public boolean isStarted() {
|
||||||
|
return this.started;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raft group id.
|
||||||
|
*/
|
||||||
|
public String getGroupId() {
|
||||||
|
return this.groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the raft group id
|
||||||
|
*/
|
||||||
|
public void setGroupId(final String groupId) {
|
||||||
|
if (this.started) {
|
||||||
|
throw new IllegalStateException("Raft group service already started");
|
||||||
|
}
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the node serverId
|
||||||
|
*/
|
||||||
|
public PeerId getServerId() {
|
||||||
|
return this.serverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the node serverId
|
||||||
|
*/
|
||||||
|
public void setServerId(final PeerId serverId) {
|
||||||
|
if (this.started) {
|
||||||
|
throw new IllegalStateException("Raft group service already started");
|
||||||
|
}
|
||||||
|
this.serverId = serverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the node options.
|
||||||
|
*/
|
||||||
|
public RpcOptions getNodeOptions() {
|
||||||
|
return this.nodeOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set node options.
|
||||||
|
*/
|
||||||
|
public void setNodeOptions(final NodeOptions nodeOptions) {
|
||||||
|
if (this.started) {
|
||||||
|
throw new IllegalStateException("Raft group service already started");
|
||||||
|
}
|
||||||
|
if (nodeOptions == null) {
|
||||||
|
throw new IllegalArgumentException("Invalid node options.");
|
||||||
|
}
|
||||||
|
nodeOptions.validate();
|
||||||
|
this.nodeOptions = nodeOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the rpc server instance.
|
||||||
|
*/
|
||||||
|
public RpcServer getRpcServer() {
|
||||||
|
return this.rpcServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set rpc server.
|
||||||
|
*/
|
||||||
|
public void setRpcServer(final RpcServer rpcServer) {
|
||||||
|
if (this.started) {
|
||||||
|
throw new IllegalStateException("Raft group service already started");
|
||||||
|
}
|
||||||
|
if (this.serverId == null) {
|
||||||
|
throw new IllegalStateException("Please set serverId at first");
|
||||||
|
}
|
||||||
|
if (rpcServer.boundPort() != this.serverId.getPort()) {
|
||||||
|
throw new IllegalArgumentException("RPC server port mismatch");
|
||||||
|
}
|
||||||
|
this.rpcServer = rpcServer;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.core.CliServiceImpl;
|
||||||
|
import com.alipay.sofa.jraft.core.NodeImpl;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.option.CliOptions;
|
||||||
|
import com.alipay.sofa.jraft.option.NodeOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service factory to create raft services, such as Node/CliService etc.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-May-03 11:06:02 AM
|
||||||
|
*/
|
||||||
|
public final class RaftServiceFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a raft node with group id and it's serverId.
|
||||||
|
*/
|
||||||
|
public static Node createRaftNode(final String groupId, final PeerId serverId) {
|
||||||
|
return new NodeImpl(groupId, serverId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and initialize a raft node with node options.
|
||||||
|
* Throw {@link IllegalStateException} when fail to initialize.
|
||||||
|
*/
|
||||||
|
public static Node createAndInitRaftNode(final String groupId, final PeerId serverId, final NodeOptions opts) {
|
||||||
|
final Node ret = createRaftNode(groupId, serverId);
|
||||||
|
if (!ret.init(opts)) {
|
||||||
|
throw new IllegalStateException("Fail to init node, please see the logs to find the reason.");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link CliService} instance.
|
||||||
|
*/
|
||||||
|
public static CliService createCliService() {
|
||||||
|
return new CliServiceImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and initialize a CliService instance.
|
||||||
|
*/
|
||||||
|
public static CliService createAndInitCliService(final CliOptions cliOptions) {
|
||||||
|
final CliService ret = createCliService();
|
||||||
|
if (!ret.init(cliOptions)) {
|
||||||
|
throw new IllegalStateException("Fail to init CliService");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.closure.ReadIndexClosure;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftException;
|
||||||
|
import com.alipay.sofa.jraft.option.ReadOnlyServiceOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The read-only query service.
|
||||||
|
*
|
||||||
|
* @author dennis
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ReadOnlyService extends Lifecycle<ReadOnlyServiceOptions> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a ReadIndex request.
|
||||||
|
*
|
||||||
|
* @param reqCtx request context of readIndex
|
||||||
|
* @param closure callback
|
||||||
|
*/
|
||||||
|
void addRequest(final byte[] reqCtx, final ReadIndexClosure closure);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for service shutdown.
|
||||||
|
*
|
||||||
|
* @throws InterruptedException if the current thread is interrupted
|
||||||
|
* while waiting
|
||||||
|
*/
|
||||||
|
void join() throws InterruptedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the node is turned into error state.
|
||||||
|
* @param error error with raft info
|
||||||
|
*/
|
||||||
|
void setError(final RaftException error);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.closure.CatchUpClosure;
|
||||||
|
import com.alipay.sofa.jraft.conf.ConfigurationEntry;
|
||||||
|
import com.alipay.sofa.jraft.core.ReplicatorType;
|
||||||
|
import com.alipay.sofa.jraft.entity.NodeId;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.option.ReplicatorGroupOptions;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RpcRequests.AppendEntriesResponse;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RpcResponseClosure;
|
||||||
|
import com.alipay.sofa.jraft.util.Describer;
|
||||||
|
import com.alipay.sofa.jraft.util.ThreadId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replicators in a raft group.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-08 5:35:26 PM
|
||||||
|
*/
|
||||||
|
public interface ReplicatorGroup extends Describer {
|
||||||
|
/**
|
||||||
|
* Init the replicator group.
|
||||||
|
*
|
||||||
|
* @param nodeId node id
|
||||||
|
* @param opts options of replicator group
|
||||||
|
* @return true if init success
|
||||||
|
*/
|
||||||
|
boolean init(final NodeId nodeId, final ReplicatorGroupOptions opts);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a replicator for follower({@link ReplicatorType#Follower}).
|
||||||
|
* @see #addReplicator(PeerId, ReplicatorType)
|
||||||
|
*
|
||||||
|
* @param peer target peer
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
default boolean addReplicator(final PeerId peer) {
|
||||||
|
return addReplicator(peer, ReplicatorType.Follower);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a replicator attached with |peer|
|
||||||
|
* will be a notification when the replicator catches up according to the
|
||||||
|
* arguments.
|
||||||
|
* NOTE: when calling this function, the replicators starts to work
|
||||||
|
* immediately, and might call Node#stepDown which might have race with
|
||||||
|
* the caller, you should deal with this situation.
|
||||||
|
*
|
||||||
|
* @param peer target peer
|
||||||
|
* @param replicatorType replicator type
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
default boolean addReplicator(final PeerId peer, ReplicatorType replicatorType) {
|
||||||
|
return addReplicator(peer, replicatorType, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to add a replicator attached with |peer|
|
||||||
|
* will be a notification when the replicator catches up according to the
|
||||||
|
* arguments.
|
||||||
|
* NOTE: when calling this function, the replicators starts to work
|
||||||
|
* immediately, and might call Node#stepDown which might have race with
|
||||||
|
* the caller, you should deal with this situation.
|
||||||
|
*
|
||||||
|
* @param peer target peer
|
||||||
|
* @param replicatorType replicator type
|
||||||
|
* @param sync synchronous
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
boolean addReplicator(final PeerId peer, ReplicatorType replicatorType, boolean sync);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send heartbeat to a peer.
|
||||||
|
*
|
||||||
|
* @param peer target peer
|
||||||
|
* @param closure callback
|
||||||
|
*/
|
||||||
|
void sendHeartbeat(final PeerId peer, final RpcResponseClosure<AppendEntriesResponse> closure);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get replicator id by peer, null if not found.
|
||||||
|
*
|
||||||
|
* @param peer peer of replicator
|
||||||
|
* @return the replicator id
|
||||||
|
*/
|
||||||
|
ThreadId getReplicator(final PeerId peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check replicator state, if it's not started, start it;
|
||||||
|
* if it is blocked, unblock it. It should be called by leader.
|
||||||
|
*
|
||||||
|
* @param peer peer of replicator
|
||||||
|
* @param lockNode if lock with node
|
||||||
|
*/
|
||||||
|
void checkReplicator(final PeerId peer, final boolean lockNode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear failure to start replicators
|
||||||
|
*/
|
||||||
|
void clearFailureReplicators();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait the peer catchup.
|
||||||
|
*/
|
||||||
|
boolean waitCaughtUp(final PeerId peer, final long maxMargin, final long dueTime, final CatchUpClosure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get peer's last rpc send timestamp (monotonic time in milliseconds).
|
||||||
|
*
|
||||||
|
* @param peer the peer of replicator
|
||||||
|
*/
|
||||||
|
long getLastRpcSendTimestamp(final PeerId peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop all replicators.
|
||||||
|
*/
|
||||||
|
boolean stopAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop replicator for the peer.
|
||||||
|
*
|
||||||
|
* @param peer the peer of replicator
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
boolean stopReplicator(final PeerId peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the term of all to-add replicators.
|
||||||
|
* This method is supposed to be called when the very candidate becomes the
|
||||||
|
* leader, so we suppose that there are no running replicators.
|
||||||
|
* Return true on success, false otherwise
|
||||||
|
*
|
||||||
|
* @param newTerm new term num
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
boolean resetTerm(final long newTerm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the interval of heartbeat,
|
||||||
|
* This method is supposed to be called when the very candidate becomes the
|
||||||
|
* leader, so we suppose that there are no running replicators.
|
||||||
|
* return true when success, false otherwise.
|
||||||
|
*
|
||||||
|
* @param newIntervalMs new heartbeat interval millis
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
boolean resetHeartbeatInterval(final int newIntervalMs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the interval of electionTimeout for replicator.
|
||||||
|
*
|
||||||
|
* @param newIntervalMs new election timeout millis
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
boolean resetElectionTimeoutInterval(final int newIntervalMs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the there's a replicator attached to the given |peer|
|
||||||
|
*
|
||||||
|
* @param peer target peer
|
||||||
|
* @return true on contains
|
||||||
|
*/
|
||||||
|
boolean contains(final PeerId peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfer leadership to the given |peer|
|
||||||
|
*
|
||||||
|
* @param peer target peer
|
||||||
|
* @param logIndex log index
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
boolean transferLeadershipTo(final PeerId peer, final long logIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop transferring leadership to the given |peer|
|
||||||
|
*
|
||||||
|
* @param peer target peer
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
boolean stopTransferLeadership(final PeerId peer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop all the replicators except for the one that we think can be the
|
||||||
|
* candidate of the next leader, which has the largest `last_log_id' among
|
||||||
|
* peers in |current_conf|.
|
||||||
|
* |candidate| would be returned if we found one and
|
||||||
|
* the caller is responsible for stopping it, or an invalid value if we
|
||||||
|
* found none.
|
||||||
|
* Returns candidate replicator id on success and null otherwise.
|
||||||
|
*
|
||||||
|
* @param conf configuration of all replicators
|
||||||
|
* @return candidate replicator id on success
|
||||||
|
*/
|
||||||
|
ThreadId stopAllAndFindTheNextCandidate(final ConfigurationEntry conf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the follower with the most log entries in this group, which is
|
||||||
|
* likely becomes the leader according to the election algorithm of raft.
|
||||||
|
* Returns the follower peerId on success and null otherwise.
|
||||||
|
*
|
||||||
|
* @param conf configuration of all replicators
|
||||||
|
* @return the follower peerId on success
|
||||||
|
*/
|
||||||
|
PeerId findTheNextCandidate(final ConfigurationEntry conf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all replicators.
|
||||||
|
*/
|
||||||
|
List<ThreadId> listReplicators();
|
||||||
|
}
|
||||||
@ -0,0 +1,384 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.concurrent.locks.StampedLock;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftError;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliClientService;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RpcRequests;
|
||||||
|
import com.alipay.sofa.jraft.util.Describer;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
import com.google.protobuf.Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maintain routes to raft groups.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-09 10:41:21 AM
|
||||||
|
*/
|
||||||
|
public class RouteTable implements Describer {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RouteTable.class);
|
||||||
|
|
||||||
|
private static final RouteTable INSTANCE = new RouteTable();
|
||||||
|
|
||||||
|
// Map<groupId, groupConf>
|
||||||
|
private final ConcurrentMap<String, GroupConf> groupConfTable = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static RouteTable getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update configuration of group in route table.
|
||||||
|
*
|
||||||
|
* @param groupId raft group id
|
||||||
|
* @param conf configuration to update
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
public boolean updateConfiguration(final String groupId, final Configuration conf) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireNonNull(conf, "Null configuration");
|
||||||
|
|
||||||
|
final GroupConf gc = getOrCreateGroupConf(groupId);
|
||||||
|
final StampedLock stampedLock = gc.stampedLock;
|
||||||
|
final long stamp = stampedLock.writeLock();
|
||||||
|
try {
|
||||||
|
gc.conf = conf;
|
||||||
|
if (gc.leader != null && !gc.conf.contains(gc.leader)) {
|
||||||
|
gc.leader = null;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
stampedLock.unlockWrite(stamp);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GroupConf getOrCreateGroupConf(final String groupId) {
|
||||||
|
GroupConf gc = this.groupConfTable.get(groupId);
|
||||||
|
if (gc == null) {
|
||||||
|
gc = new GroupConf();
|
||||||
|
final GroupConf old = this.groupConfTable.putIfAbsent(groupId, gc);
|
||||||
|
if (old != null) {
|
||||||
|
gc = old;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update configuration of group in route table.
|
||||||
|
*
|
||||||
|
* @param groupId raft group id
|
||||||
|
* @param confStr configuration string
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
public boolean updateConfiguration(final String groupId, final String confStr) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(confStr), "Blank configuration");
|
||||||
|
|
||||||
|
final Configuration conf = new Configuration();
|
||||||
|
if (conf.parse(confStr)) {
|
||||||
|
return updateConfiguration(groupId, conf);
|
||||||
|
} else {
|
||||||
|
LOG.error("Fail to parse confStr: {}", confStr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cached leader of the group, return it when found, null otherwise.
|
||||||
|
* Make sure calls {@link #refreshLeader(CliClientService, String, int)} already
|
||||||
|
* before invoke this method.
|
||||||
|
*
|
||||||
|
* @param groupId raft group id
|
||||||
|
* @return peer of leader
|
||||||
|
*/
|
||||||
|
public PeerId selectLeader(final String groupId) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
|
||||||
|
final GroupConf gc = this.groupConfTable.get(groupId);
|
||||||
|
if (gc == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final StampedLock stampedLock = gc.stampedLock;
|
||||||
|
long stamp = stampedLock.tryOptimisticRead();
|
||||||
|
PeerId leader = gc.leader;
|
||||||
|
if (!stampedLock.validate(stamp)) {
|
||||||
|
stamp = stampedLock.readLock();
|
||||||
|
try {
|
||||||
|
leader = gc.leader;
|
||||||
|
} finally {
|
||||||
|
stampedLock.unlockRead(stamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return leader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update leader info.
|
||||||
|
*
|
||||||
|
* @param groupId raft group id
|
||||||
|
* @param leader peer of leader
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
public boolean updateLeader(final String groupId, final PeerId leader) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
|
||||||
|
if (leader != null) {
|
||||||
|
// If leader presents, it should not be empty.
|
||||||
|
Requires.requireTrue(!leader.isEmpty(), "Empty leader");
|
||||||
|
}
|
||||||
|
|
||||||
|
final GroupConf gc = getOrCreateGroupConf(groupId);
|
||||||
|
final StampedLock stampedLock = gc.stampedLock;
|
||||||
|
final long stamp = stampedLock.writeLock();
|
||||||
|
try {
|
||||||
|
gc.leader = leader;
|
||||||
|
} finally {
|
||||||
|
stampedLock.unlockWrite(stamp);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update leader info.
|
||||||
|
*
|
||||||
|
* @param groupId raft group id
|
||||||
|
* @param leaderStr peer string of leader
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
public boolean updateLeader(final String groupId, final String leaderStr) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(leaderStr), "Blank leader");
|
||||||
|
|
||||||
|
final PeerId leader = new PeerId();
|
||||||
|
if (leader.parse(leaderStr)) {
|
||||||
|
return updateLeader(groupId, leader);
|
||||||
|
} else {
|
||||||
|
LOG.error("Fail to parse leaderStr: {}", leaderStr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the configuration by groupId, returns null when not found.
|
||||||
|
*
|
||||||
|
* @param groupId raft group id
|
||||||
|
* @return configuration of the group id
|
||||||
|
*/
|
||||||
|
public Configuration getConfiguration(final String groupId) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
|
||||||
|
final GroupConf gc = this.groupConfTable.get(groupId);
|
||||||
|
if (gc == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final StampedLock stampedLock = gc.stampedLock;
|
||||||
|
long stamp = stampedLock.tryOptimisticRead();
|
||||||
|
Configuration conf = gc.conf;
|
||||||
|
if (!stampedLock.validate(stamp)) {
|
||||||
|
stamp = stampedLock.readLock();
|
||||||
|
try {
|
||||||
|
conf = gc.conf;
|
||||||
|
} finally {
|
||||||
|
stampedLock.unlockRead(stamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocking the thread until query_leader finishes.
|
||||||
|
*
|
||||||
|
* @param groupId raft group id
|
||||||
|
* @param timeoutMs timeout millis
|
||||||
|
* @return operation status
|
||||||
|
*/
|
||||||
|
public Status refreshLeader(final CliClientService cliClientService, final String groupId, final int timeoutMs)
|
||||||
|
throws InterruptedException,
|
||||||
|
TimeoutException {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireTrue(timeoutMs > 0, "Invalid timeout: " + timeoutMs);
|
||||||
|
|
||||||
|
final Configuration conf = getConfiguration(groupId);
|
||||||
|
if (conf == null) {
|
||||||
|
return new Status(RaftError.ENOENT,
|
||||||
|
"Group %s is not registered in RouteTable, forgot to call updateConfiguration?", groupId);
|
||||||
|
}
|
||||||
|
final Status st = Status.OK();
|
||||||
|
final CliRequests.GetLeaderRequest.Builder rb = CliRequests.GetLeaderRequest.newBuilder();
|
||||||
|
rb.setGroupId(groupId);
|
||||||
|
final CliRequests.GetLeaderRequest request = rb.build();
|
||||||
|
TimeoutException timeoutException = null;
|
||||||
|
for (final PeerId peer : conf) {
|
||||||
|
if (!cliClientService.connect(peer.getEndpoint())) {
|
||||||
|
if (st.isOk()) {
|
||||||
|
st.setError(-1, "Fail to init channel to %s", peer);
|
||||||
|
} else {
|
||||||
|
final String savedMsg = st.getErrorMsg();
|
||||||
|
st.setError(-1, "%s, Fail to init channel to %s", savedMsg, peer);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final Future<Message> result = cliClientService.getLeader(peer.getEndpoint(), request, null);
|
||||||
|
try {
|
||||||
|
final Message msg = result.get(timeoutMs, TimeUnit.MILLISECONDS);
|
||||||
|
if (msg instanceof RpcRequests.ErrorResponse) {
|
||||||
|
if (st.isOk()) {
|
||||||
|
st.setError(-1, ((RpcRequests.ErrorResponse) msg).getErrorMsg());
|
||||||
|
} else {
|
||||||
|
final String savedMsg = st.getErrorMsg();
|
||||||
|
st.setError(-1, "%s, %s", savedMsg, ((RpcRequests.ErrorResponse) msg).getErrorMsg());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final CliRequests.GetLeaderResponse response = (CliRequests.GetLeaderResponse) msg;
|
||||||
|
updateLeader(groupId, response.getLeaderId());
|
||||||
|
return Status.OK();
|
||||||
|
}
|
||||||
|
} catch (final TimeoutException e) {
|
||||||
|
timeoutException = e;
|
||||||
|
} catch (final ExecutionException e) {
|
||||||
|
if (st.isOk()) {
|
||||||
|
st.setError(-1, e.getMessage());
|
||||||
|
} else {
|
||||||
|
final String savedMsg = st.getErrorMsg();
|
||||||
|
st.setError(-1, "%s, %s", savedMsg, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (timeoutException != null) {
|
||||||
|
throw timeoutException;
|
||||||
|
}
|
||||||
|
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status refreshConfiguration(final CliClientService cliClientService, final String groupId,
|
||||||
|
final int timeoutMs) throws InterruptedException, TimeoutException {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireTrue(timeoutMs > 0, "Invalid timeout: " + timeoutMs);
|
||||||
|
|
||||||
|
final Configuration conf = getConfiguration(groupId);
|
||||||
|
if (conf == null) {
|
||||||
|
return new Status(RaftError.ENOENT,
|
||||||
|
"Group %s is not registered in RouteTable, forgot to call updateConfiguration?", groupId);
|
||||||
|
}
|
||||||
|
final Status st = Status.OK();
|
||||||
|
PeerId leaderId = selectLeader(groupId);
|
||||||
|
if (leaderId == null) {
|
||||||
|
refreshLeader(cliClientService, groupId, timeoutMs);
|
||||||
|
leaderId = selectLeader(groupId);
|
||||||
|
}
|
||||||
|
if (leaderId == null) {
|
||||||
|
st.setError(-1, "Fail to get leader of group %s", groupId);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
if (!cliClientService.connect(leaderId.getEndpoint())) {
|
||||||
|
st.setError(-1, "Fail to init channel to %s", leaderId);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
final CliRequests.GetPeersRequest.Builder rb = CliRequests.GetPeersRequest.newBuilder();
|
||||||
|
rb.setGroupId(groupId);
|
||||||
|
rb.setLeaderId(leaderId.toString());
|
||||||
|
try {
|
||||||
|
final Message result = cliClientService.getPeers(leaderId.getEndpoint(), rb.build(), null).get(timeoutMs,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
|
if (result instanceof CliRequests.GetPeersResponse) {
|
||||||
|
final CliRequests.GetPeersResponse resp = (CliRequests.GetPeersResponse) result;
|
||||||
|
final Configuration newConf = new Configuration();
|
||||||
|
for (final String peerIdStr : resp.getPeersList()) {
|
||||||
|
final PeerId newPeer = new PeerId();
|
||||||
|
newPeer.parse(peerIdStr);
|
||||||
|
newConf.addPeer(newPeer);
|
||||||
|
}
|
||||||
|
if (!conf.equals(newConf)) {
|
||||||
|
LOG.info("Configuration of replication group {} changed from {} to {}", groupId, conf, newConf);
|
||||||
|
}
|
||||||
|
updateConfiguration(groupId, newConf);
|
||||||
|
} else {
|
||||||
|
final RpcRequests.ErrorResponse resp = (RpcRequests.ErrorResponse) result;
|
||||||
|
st.setError(resp.getErrorCode(), resp.getErrorMsg());
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
st.setError(-1, e.getMessage());
|
||||||
|
}
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the states.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
this.groupConfTable.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the group from route table.
|
||||||
|
*
|
||||||
|
* @param groupId raft group id
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
public boolean removeGroup(final String groupId) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
|
||||||
|
return this.groupConfTable.remove(groupId) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RouteTable{" + "groupConfTable=" + groupConfTable + '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
private RouteTable() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describe(final Printer out) {
|
||||||
|
out.println("RouteTable:") //
|
||||||
|
.print(" ") //
|
||||||
|
.println(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GroupConf {
|
||||||
|
|
||||||
|
private final StampedLock stampedLock = new StampedLock();
|
||||||
|
|
||||||
|
private Configuration conf;
|
||||||
|
private PeerId leader;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "GroupConf{" + "conf=" + conf + ", leader=" + leader + '}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
import com.alipay.sofa.jraft.entity.LeaderChangeContext;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftException;
|
||||||
|
import com.alipay.sofa.jraft.storage.snapshot.SnapshotReader;
|
||||||
|
import com.alipay.sofa.jraft.storage.snapshot.SnapshotWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* |StateMachine| is the sink of all the events of a very raft node.
|
||||||
|
* Implement a specific StateMachine for your own business logic.
|
||||||
|
* NOTE: All the interfaces are not guaranteed to be thread safe and they are
|
||||||
|
* called sequentially, saying that every single operation will block all the
|
||||||
|
* following ones.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-08 5:43:21 PM
|
||||||
|
*/
|
||||||
|
public interface StateMachine {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the StateMachine with a batch a tasks that can be accessed
|
||||||
|
* through |iterator|.
|
||||||
|
*
|
||||||
|
* Invoked when one or more tasks that were passed to Node#apply(Task) have been
|
||||||
|
* committed to the raft group (quorum of the group peers have received
|
||||||
|
* those tasks and stored them on the backing storage).
|
||||||
|
*
|
||||||
|
* Once this function returns to the caller, we will regard all the iterated
|
||||||
|
* tasks through |iter| have been successfully applied. And if you didn't
|
||||||
|
* apply all the the given tasks, we would regard this as a critical error
|
||||||
|
* and report a error whose type is ERROR_TYPE_STATE_MACHINE.
|
||||||
|
*
|
||||||
|
* @param iter iterator of states
|
||||||
|
*/
|
||||||
|
void onApply(final Iterator iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked once when the raft node was shut down.
|
||||||
|
* Default do nothing
|
||||||
|
*/
|
||||||
|
void onShutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User defined snapshot generate function, this method will block StateMachine#onApply(Iterator).
|
||||||
|
* user can make snapshot async when fsm can be cow(copy-on-write).
|
||||||
|
* call done.run(status) when snapshot finished.
|
||||||
|
* Default: Save nothing and returns error.
|
||||||
|
*
|
||||||
|
* @param writer snapshot writer
|
||||||
|
* @param done callback
|
||||||
|
*/
|
||||||
|
void onSnapshotSave(final SnapshotWriter writer, final Closure done);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User defined snapshot load function
|
||||||
|
* get and load snapshot
|
||||||
|
* Default: Load nothing and returns error.
|
||||||
|
*
|
||||||
|
* @param reader snapshot reader
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
boolean onSnapshotLoad(final SnapshotReader reader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when the belonging node becomes the leader of the group at |term|
|
||||||
|
* Default: Do nothing
|
||||||
|
*
|
||||||
|
* @param term new term num
|
||||||
|
*/
|
||||||
|
void onLeaderStart(final long term);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when this node steps down from the leader of the replication
|
||||||
|
* group and |status| describes detailed information
|
||||||
|
*
|
||||||
|
* @param status status info
|
||||||
|
*/
|
||||||
|
void onLeaderStop(final Status status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when a critical error was encountered, after this
|
||||||
|
* point, no any further modification is allowed to applied to this node
|
||||||
|
* until the error is fixed and this node restarts.
|
||||||
|
*
|
||||||
|
* @param e raft error message
|
||||||
|
*/
|
||||||
|
void onError(final RaftException e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a configuration has been committed to the group.
|
||||||
|
*
|
||||||
|
* @param conf committed configuration
|
||||||
|
*/
|
||||||
|
void onConfigurationCommitted(final Configuration conf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when a follower stops following a leader and its leaderId becomes null,
|
||||||
|
* situations including:
|
||||||
|
* 1. handle election timeout and start preVote
|
||||||
|
* 2. receive requests with higher term such as VoteRequest from a candidate
|
||||||
|
* or appendEntries request from a new leader
|
||||||
|
* 3. receive timeoutNow request from current leader and start request vote.
|
||||||
|
*
|
||||||
|
* the parameter ctx gives the information(leaderId, term and status) about the
|
||||||
|
* very leader whom the follower followed before.
|
||||||
|
* User can reset the node's information as it stops following some leader.
|
||||||
|
*
|
||||||
|
* @param ctx context of leader change
|
||||||
|
*/
|
||||||
|
void onStopFollowing(final LeaderChangeContext ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when a follower or candidate starts following a leader and its leaderId
|
||||||
|
* (should be NULL before the method is called) is set to the leader's id,
|
||||||
|
* situations including:
|
||||||
|
* 1. a candidate receives appendEntries request from a leader
|
||||||
|
* 2. a follower(without leader) receives appendEntries from a leader
|
||||||
|
*
|
||||||
|
* the parameter ctx gives the information(leaderId, term and status) about
|
||||||
|
* the very leader whom the follower starts to follow.
|
||||||
|
* User can reset the node's information as it starts to follow some leader.
|
||||||
|
*
|
||||||
|
* @param ctx context of leader change
|
||||||
|
*/
|
||||||
|
void onStartFollowing(final LeaderChangeContext ctx);
|
||||||
|
}
|
||||||
@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.error.RaftError;
|
||||||
|
import com.alipay.sofa.jraft.util.Copiable;
|
||||||
|
|
||||||
|
//A Status encapsulates the result of an operation. It may indicate success,
|
||||||
|
|
||||||
|
//or it may indicate an error with an associated error message. It's suitable
|
||||||
|
//for passing status of functions with richer information than just error_code
|
||||||
|
//in exception-forbidden code. This utility is inspired by leveldb::Status.
|
||||||
|
//
|
||||||
|
//Multiple threads can invoke const methods on a Status without
|
||||||
|
//external synchronization, but if any of the threads may call a
|
||||||
|
//non-const method, all threads accessing the same Status must use
|
||||||
|
//external synchronization.
|
||||||
|
//
|
||||||
|
//Since failed status needs to allocate memory, you should be careful when
|
||||||
|
//failed status is frequent.
|
||||||
|
public class Status implements Copiable<Status> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status internal state.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-03 11:17:51 AM
|
||||||
|
*/
|
||||||
|
private static class State {
|
||||||
|
/** error code */
|
||||||
|
int code;
|
||||||
|
/** error msg*/
|
||||||
|
String msg;
|
||||||
|
|
||||||
|
State(int code, String msg) {
|
||||||
|
super();
|
||||||
|
this.code = code;
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + this.code;
|
||||||
|
result = prime * result + (this.msg == null ? 0 : this.msg.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
State other = (State) obj;
|
||||||
|
if (this.code != other.code) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.msg == null) {
|
||||||
|
return other.msg == null;
|
||||||
|
} else {
|
||||||
|
return this.msg.equals(other.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private State state;
|
||||||
|
|
||||||
|
public Status() {
|
||||||
|
this.state = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a OK status instance.
|
||||||
|
*/
|
||||||
|
public static Status OK() {
|
||||||
|
return new Status();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status(Status s) {
|
||||||
|
if (s.state != null) {
|
||||||
|
this.state = new State(s.state.code, s.state.msg);
|
||||||
|
} else {
|
||||||
|
this.state = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status(RaftError raftError, String fmt, Object... args) {
|
||||||
|
this.state = new State(raftError.getNumber(), String.format(fmt, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status(int code, String fmt, Object... args) {
|
||||||
|
this.state = new State(code, String.format(fmt, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status(int code, String errorMsg) {
|
||||||
|
this.state = new State(code, errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + (this.state == null ? 0 : this.state.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Status other = (Status) obj;
|
||||||
|
if (this.state == null) {
|
||||||
|
return other.state == null;
|
||||||
|
} else {
|
||||||
|
return this.state.equals(other.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset status to be OK state.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
this.state = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when status is in OK state.
|
||||||
|
*/
|
||||||
|
public boolean isOk() {
|
||||||
|
return this.state == null || this.state.code == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set error code.
|
||||||
|
*/
|
||||||
|
public void setCode(int code) {
|
||||||
|
if (this.state == null) {
|
||||||
|
this.state = new State(code, null);
|
||||||
|
} else {
|
||||||
|
this.state.code = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get error code.
|
||||||
|
*/
|
||||||
|
public int getCode() {
|
||||||
|
return this.state == null ? 0 : this.state.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get raft error from error code.
|
||||||
|
*/
|
||||||
|
public RaftError getRaftError() {
|
||||||
|
return this.state == null ? RaftError.SUCCESS : RaftError.forNumber(this.state.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set error msg
|
||||||
|
*/
|
||||||
|
public void setErrorMsg(String errMsg) {
|
||||||
|
if (this.state == null) {
|
||||||
|
this.state = new State(0, errMsg);
|
||||||
|
} else {
|
||||||
|
this.state.msg = errMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set error code and error msg.
|
||||||
|
*/
|
||||||
|
public void setError(int code, String fmt, Object... args) {
|
||||||
|
this.state = new State(code, String.format(String.valueOf(fmt), args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set raft error and error msg.
|
||||||
|
*/
|
||||||
|
public void setError(RaftError error, String fmt, Object... args) {
|
||||||
|
this.state = new State(error.getNumber(), String.format(String.valueOf(fmt), args));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (isOk()) {
|
||||||
|
return "Status[OK]";
|
||||||
|
} else {
|
||||||
|
return "Status[" + RaftError.describeCode(this.state.code) + "<" + this.state.code + ">: " + this.state.msg
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status copy() {
|
||||||
|
return new Status(this.getCode(), this.getErrorMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the error msg.
|
||||||
|
*/
|
||||||
|
public String getErrorMsg() {
|
||||||
|
return this.state == null ? null : this.state.msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.util.FileOutputSignalHandler;
|
||||||
|
import com.alipay.sofa.jraft.util.MetricReporter;
|
||||||
|
import com.alipay.sofa.jraft.util.SystemPropertyUtil;
|
||||||
|
import com.alipay.sofa.jraft.util.ThreadPoolMetricRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jiachun.fjc
|
||||||
|
*/
|
||||||
|
public class ThreadPoolMetricsSignalHandler extends FileOutputSignalHandler {
|
||||||
|
|
||||||
|
private static Logger LOG = LoggerFactory.getLogger(ThreadPoolMetricsSignalHandler.class);
|
||||||
|
|
||||||
|
private static final String DIR = SystemPropertyUtil.get("jraft.signal.thread.pool.metrics.dir", "");
|
||||||
|
private static final String BASE_NAME = "thread_pool_metrics.log";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(final String signalName) {
|
||||||
|
try {
|
||||||
|
final File file = getOutputFile(DIR, BASE_NAME);
|
||||||
|
|
||||||
|
LOG.info("Printing thread pools metrics with signal: {} to file: {}.", signalName, file);
|
||||||
|
|
||||||
|
try (final PrintStream out = new PrintStream(new FileOutputStream(file, true))) {
|
||||||
|
MetricReporter.forRegistry(ThreadPoolMetricRegistry.metricRegistry()) //
|
||||||
|
.outputTo(out) //
|
||||||
|
.build() //
|
||||||
|
.report();
|
||||||
|
}
|
||||||
|
} catch (final IOException e) {
|
||||||
|
LOG.error("Fail to print thread pools metrics.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.closure;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A catchup closure for peer to catch up.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 2:15:05 PM
|
||||||
|
*/
|
||||||
|
public abstract class CatchUpClosure implements Closure {
|
||||||
|
|
||||||
|
private long maxMargin;
|
||||||
|
private ScheduledFuture<?> timer;
|
||||||
|
private boolean hasTimer;
|
||||||
|
private boolean errorWasSet;
|
||||||
|
|
||||||
|
private final Status status = Status.OK();
|
||||||
|
|
||||||
|
public Status getStatus() {
|
||||||
|
return this.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMaxMargin() {
|
||||||
|
return this.maxMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxMargin(long maxMargin) {
|
||||||
|
this.maxMargin = maxMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScheduledFuture<?> getTimer() {
|
||||||
|
return this.timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimer(ScheduledFuture<?> timer) {
|
||||||
|
this.timer = timer;
|
||||||
|
this.hasTimer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasTimer() {
|
||||||
|
return this.hasTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isErrorWasSet() {
|
||||||
|
return this.errorWasSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorWasSet(boolean errorWasSet) {
|
||||||
|
this.errorWasSet = errorWasSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.closure;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A thread-safe closure queue.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-14 10:29:12 AM
|
||||||
|
*/
|
||||||
|
@ThreadSafe
|
||||||
|
public interface ClosureQueue {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all closure in queue.
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the first index in queue.
|
||||||
|
*
|
||||||
|
* @param firstIndex the first index of queue
|
||||||
|
*/
|
||||||
|
void resetFirstIndex(final long firstIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a new closure into queue.
|
||||||
|
*
|
||||||
|
* @param closure the closure to append
|
||||||
|
*/
|
||||||
|
void appendPendingClosure(final Closure closure);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pop closure from queue until index(inclusion), returns the first
|
||||||
|
* popped out index, returns -1 when out of range, returns index+1
|
||||||
|
* when not found.
|
||||||
|
*
|
||||||
|
* @param endIndex the index of queue
|
||||||
|
* @param closures closure list
|
||||||
|
* @return returns the first popped out index, returns -1 when out
|
||||||
|
* of range, returns index+1
|
||||||
|
* when not found.
|
||||||
|
*/
|
||||||
|
long popClosureUntil(final long endIndex, final List<Closure> closures);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pop closure from queue until index(inclusion), returns the first
|
||||||
|
* popped out index, returns -1 when out of range, returns index+1
|
||||||
|
* when not found.
|
||||||
|
*
|
||||||
|
* @param endIndex the index of queue
|
||||||
|
* @param closures closure list
|
||||||
|
* @param taskClosures task closure list
|
||||||
|
* @return returns the first popped out index, returns -1 when out
|
||||||
|
* of range, returns index+1
|
||||||
|
* when not found.
|
||||||
|
*/
|
||||||
|
long popClosureUntil(final long endIndex, final List<Closure> closures, final List<TaskClosure> taskClosures);
|
||||||
|
}
|
||||||
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.closure;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftError;
|
||||||
|
import com.alipay.sofa.jraft.util.OnlyForTest;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
import com.alipay.sofa.jraft.util.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closure queue implementation.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-28 11:44:01 AM
|
||||||
|
*/
|
||||||
|
public class ClosureQueueImpl implements ClosureQueue {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ClosureQueueImpl.class);
|
||||||
|
|
||||||
|
private final Lock lock;
|
||||||
|
private long firstIndex;
|
||||||
|
private LinkedList<Closure> queue;
|
||||||
|
|
||||||
|
@OnlyForTest
|
||||||
|
public long getFirstIndex() {
|
||||||
|
return firstIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyForTest
|
||||||
|
public LinkedList<Closure> getQueue() {
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClosureQueueImpl() {
|
||||||
|
super();
|
||||||
|
this.lock = new ReentrantLock();
|
||||||
|
this.firstIndex = 0;
|
||||||
|
this.queue = new LinkedList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
List<Closure> savedQueue;
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
this.firstIndex = 0;
|
||||||
|
savedQueue = this.queue;
|
||||||
|
this.queue = new LinkedList<>();
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Status status = new Status(RaftError.EPERM, "Leader stepped down");
|
||||||
|
Utils.runInThread(() -> {
|
||||||
|
for (final Closure done : savedQueue) {
|
||||||
|
if (done != null) {
|
||||||
|
done.run(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetFirstIndex(final long firstIndex) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
Requires.requireTrue(this.queue.isEmpty(), "Queue is not empty.");
|
||||||
|
this.firstIndex = firstIndex;
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendPendingClosure(final Closure closure) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
this.queue.add(closure);
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long popClosureUntil(final long endIndex, final List<Closure> closures) {
|
||||||
|
return popClosureUntil(endIndex, closures, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long popClosureUntil(final long endIndex, final List<Closure> closures, final List<TaskClosure> taskClosures) {
|
||||||
|
closures.clear();
|
||||||
|
if (taskClosures != null) {
|
||||||
|
taskClosures.clear();
|
||||||
|
}
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
final int queueSize = this.queue.size();
|
||||||
|
if (queueSize == 0 || endIndex < this.firstIndex) {
|
||||||
|
return endIndex + 1;
|
||||||
|
}
|
||||||
|
if (endIndex > this.firstIndex + queueSize - 1) {
|
||||||
|
LOG.error("Invalid endIndex={}, firstIndex={}, closureQueueSize={}", endIndex, this.firstIndex,
|
||||||
|
queueSize);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
final long outFirstIndex = this.firstIndex;
|
||||||
|
for (long i = outFirstIndex; i <= endIndex; i++) {
|
||||||
|
final Closure closure = this.queue.pollFirst();
|
||||||
|
if (taskClosures != null && closure instanceof TaskClosure) {
|
||||||
|
taskClosures.add((TaskClosure) closure);
|
||||||
|
}
|
||||||
|
closures.add(closure);
|
||||||
|
}
|
||||||
|
this.firstIndex = endIndex + 1;
|
||||||
|
return outFirstIndex;
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.closure;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author jiachun.fjc
|
||||||
|
*/
|
||||||
|
public class JoinableClosure implements Closure {
|
||||||
|
|
||||||
|
private final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
private final Closure closure;
|
||||||
|
|
||||||
|
public JoinableClosure(Closure closure) {
|
||||||
|
this.closure = Requires.requireNonNull(closure, "closure");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(final Status status) {
|
||||||
|
try {
|
||||||
|
this.closure.run(status);
|
||||||
|
} finally {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void join() throws InterruptedException {
|
||||||
|
this.latch.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void join(final long timeoutMillis) throws InterruptedException, TimeoutException {
|
||||||
|
if (!this.latch.await(timeoutMillis, TimeUnit.MILLISECONDS)) {
|
||||||
|
throw new TimeoutException("joined timeout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Closure getClosure() {
|
||||||
|
return closure;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.closure;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.storage.snapshot.SnapshotReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load snapshot closure
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 2:20:09 PM
|
||||||
|
*/
|
||||||
|
public interface LoadSnapshotClosure extends Closure {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start to load snapshot, returns a snapshot reader.
|
||||||
|
*
|
||||||
|
* @return a snapshot reader.
|
||||||
|
*/
|
||||||
|
SnapshotReader start();
|
||||||
|
}
|
||||||
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.closure;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.JRaftUtils;
|
||||||
|
import com.alipay.sofa.jraft.Node;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftError;
|
||||||
|
import com.alipay.sofa.jraft.util.SystemPropertyUtil;
|
||||||
|
import com.alipay.sofa.jraft.util.timer.Timeout;
|
||||||
|
import com.alipay.sofa.jraft.util.timer.Timer;
|
||||||
|
import com.alipay.sofa.jraft.util.timer.TimerTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read index closure
|
||||||
|
*
|
||||||
|
* @author dennis
|
||||||
|
*/
|
||||||
|
public abstract class ReadIndexClosure implements Closure {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory
|
||||||
|
.getLogger(ReadIndexClosure.class);
|
||||||
|
|
||||||
|
private static final AtomicIntegerFieldUpdater<ReadIndexClosure> STATE_UPDATER = AtomicIntegerFieldUpdater
|
||||||
|
.newUpdater(
|
||||||
|
ReadIndexClosure.class,
|
||||||
|
"state");
|
||||||
|
|
||||||
|
private static final long DEFAULT_TIMEOUT = SystemPropertyUtil.getInt(
|
||||||
|
"jraft.read-index.timeout",
|
||||||
|
2 * 1000);
|
||||||
|
|
||||||
|
private static final int PENDING = 0;
|
||||||
|
private static final int COMPLETE = 1;
|
||||||
|
private static final int TIMEOUT = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid log index -1.
|
||||||
|
*/
|
||||||
|
public static final long INVALID_LOG_INDEX = -1;
|
||||||
|
|
||||||
|
private long index = INVALID_LOG_INDEX;
|
||||||
|
private byte[] requestContext;
|
||||||
|
|
||||||
|
private volatile int state = PENDING;
|
||||||
|
|
||||||
|
public ReadIndexClosure() {
|
||||||
|
this(DEFAULT_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a read-index closure with a timeout parameter.
|
||||||
|
*
|
||||||
|
* @param timeoutMs timeout millis
|
||||||
|
*/
|
||||||
|
public ReadIndexClosure(long timeoutMs) {
|
||||||
|
if (timeoutMs >= 0) {
|
||||||
|
// Lazy to init the timer
|
||||||
|
TimeoutScanner.TIMER.newTimeout(new TimeoutTask(this), timeoutMs, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when ReadIndex can be executed.
|
||||||
|
*
|
||||||
|
* @param status the readIndex status.
|
||||||
|
* @param index the committed index when starts readIndex.
|
||||||
|
* @param reqCtx the request context passed by {@link Node#readIndex(byte[], ReadIndexClosure)}.
|
||||||
|
* @see Node#readIndex(byte[], ReadIndexClosure)
|
||||||
|
*/
|
||||||
|
public abstract void run(final Status status, final long index, final byte[] reqCtx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set callback result, called by jraft.
|
||||||
|
*
|
||||||
|
* @param index the committed index.
|
||||||
|
* @param reqCtx the request context passed by {@link Node#readIndex(byte[], ReadIndexClosure)}.
|
||||||
|
*/
|
||||||
|
public void setResult(final long index, final byte[] reqCtx) {
|
||||||
|
this.index = index;
|
||||||
|
this.requestContext = reqCtx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The committed log index when starts readIndex request. return -1 if fails.
|
||||||
|
*
|
||||||
|
* @return returns the committed index. returns -1 if fails.
|
||||||
|
*/
|
||||||
|
public long getIndex() {
|
||||||
|
return this.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request context.
|
||||||
|
*
|
||||||
|
* @return the request context.
|
||||||
|
*/
|
||||||
|
public byte[] getRequestContext() {
|
||||||
|
return this.requestContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(final Status status) {
|
||||||
|
if (!STATE_UPDATER.compareAndSet(this, PENDING, COMPLETE)) {
|
||||||
|
LOG.warn("A timeout read-index response finally returned: {}.", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
run(status, this.index, this.requestContext);
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
LOG.error("Fail to run ReadIndexClosure with status: {}.", status, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TimeoutTask implements TimerTask {
|
||||||
|
|
||||||
|
private final ReadIndexClosure closure;
|
||||||
|
|
||||||
|
TimeoutTask(ReadIndexClosure closure) {
|
||||||
|
this.closure = closure;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(final Timeout timeout) throws Exception {
|
||||||
|
if (!STATE_UPDATER.compareAndSet(this.closure, PENDING, TIMEOUT)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Status status = new Status(RaftError.ETIMEDOUT, "read-index request timeout");
|
||||||
|
try {
|
||||||
|
this.closure.run(status, INVALID_LOG_INDEX, null);
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
LOG.error("[Timeout] fail to run ReadIndexClosure with status: {}.", status, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazy to create a timer
|
||||||
|
*/
|
||||||
|
static class TimeoutScanner {
|
||||||
|
private static final Timer TIMER = JRaftUtils.raftTimerFactory().createTimer("read-index.timeout.scanner");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.closure;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.entity.RaftOutter.SnapshotMeta;
|
||||||
|
import com.alipay.sofa.jraft.storage.snapshot.SnapshotWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save snapshot closure
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 2:21:30 PM
|
||||||
|
*/
|
||||||
|
public interface SaveSnapshotClosure extends Closure {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts to save snapshot, returns the writer.
|
||||||
|
*
|
||||||
|
* @param meta metadata of snapshot.
|
||||||
|
* @return returns snapshot writer.
|
||||||
|
*/
|
||||||
|
SnapshotWriter start(final SnapshotMeta meta);
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.closure;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special Closure which provides synchronization primitives.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-16 2:45:34 PM
|
||||||
|
*/
|
||||||
|
public class SynchronizedClosure implements Closure {
|
||||||
|
|
||||||
|
private CountDownLatch latch;
|
||||||
|
private volatile Status status;
|
||||||
|
/**
|
||||||
|
* Latch count to reset
|
||||||
|
*/
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
public SynchronizedClosure() {
|
||||||
|
this(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SynchronizedClosure(final int n) {
|
||||||
|
this.count = n;
|
||||||
|
this.latch = new CountDownLatch(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get last ran status
|
||||||
|
*
|
||||||
|
* @return returns the last ran status
|
||||||
|
*/
|
||||||
|
public Status getStatus() {
|
||||||
|
return this.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(final Status status) {
|
||||||
|
this.status = status;
|
||||||
|
this.latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for closure run
|
||||||
|
*
|
||||||
|
* @return status
|
||||||
|
* @throws InterruptedException if the current thread is interrupted
|
||||||
|
* while waiting
|
||||||
|
*/
|
||||||
|
public Status await() throws InterruptedException {
|
||||||
|
this.latch.await();
|
||||||
|
return this.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the closure
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
this.status = null;
|
||||||
|
this.latch = new CountDownLatch(this.count);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.closure;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closure for task applying.
|
||||||
|
* @author dennis
|
||||||
|
*/
|
||||||
|
public interface TaskClosure extends Closure {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when task is committed to majority peers of the
|
||||||
|
* RAFT group but before it is applied to state machine.
|
||||||
|
*
|
||||||
|
* <strong>Note: user implementation should not block
|
||||||
|
* this method and throw any exceptions.</strong>
|
||||||
|
*/
|
||||||
|
void onCommitted();
|
||||||
|
}
|
||||||
@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.conf;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.util.Copiable;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A configuration with a set of peers.
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-15 11:00:26 AM
|
||||||
|
*/
|
||||||
|
public class Configuration implements Iterable<PeerId>, Copiable<Configuration> {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(Configuration.class);
|
||||||
|
|
||||||
|
private static final String LEARNER_POSTFIX = "/learner";
|
||||||
|
|
||||||
|
private List<PeerId> peers = new ArrayList<>();
|
||||||
|
|
||||||
|
// use LinkedHashSet to keep insertion order.
|
||||||
|
private LinkedHashSet<PeerId> learners = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
public Configuration() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a configuration instance with peers.
|
||||||
|
*
|
||||||
|
* @param conf configuration
|
||||||
|
*/
|
||||||
|
public Configuration(final Iterable<PeerId> conf) {
|
||||||
|
this(conf, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a configuration from another conf.
|
||||||
|
*
|
||||||
|
* @param conf configuration
|
||||||
|
*/
|
||||||
|
public Configuration(final Configuration conf) {
|
||||||
|
this(conf.getPeers(), conf.getLearners());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a Configuration instance with peers and learners.
|
||||||
|
*
|
||||||
|
* @param conf peers configuration
|
||||||
|
* @param learners learners
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
public Configuration(final Iterable<PeerId> conf, final Iterable<PeerId> learners) {
|
||||||
|
Requires.requireNonNull(conf, "conf");
|
||||||
|
for (final PeerId peer : conf) {
|
||||||
|
this.peers.add(peer.copy());
|
||||||
|
}
|
||||||
|
addLearners(learners);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLearners(final LinkedHashSet<PeerId> learners) {
|
||||||
|
this.learners = learners;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a learner peer.
|
||||||
|
*
|
||||||
|
* @param learner learner to add
|
||||||
|
* @return true when add successfully.
|
||||||
|
*/
|
||||||
|
public boolean addLearner(final PeerId learner) {
|
||||||
|
return this.learners.add(learner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add learners in batch, returns the added count.
|
||||||
|
*
|
||||||
|
* @param learners learners to add
|
||||||
|
* @return the total added count
|
||||||
|
*/
|
||||||
|
public int addLearners(final Iterable<PeerId> learners) {
|
||||||
|
int ret = 0;
|
||||||
|
if (learners != null) {
|
||||||
|
for (final PeerId peer : learners) {
|
||||||
|
if (this.learners.add(peer.copy())) {
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a learner peer.
|
||||||
|
*
|
||||||
|
* @param learner learner to remove
|
||||||
|
* @return true when remove successfully.
|
||||||
|
*/
|
||||||
|
public boolean removeLearner(final PeerId learner) {
|
||||||
|
return this.learners.remove(learner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the learners set.
|
||||||
|
*
|
||||||
|
* @return learners
|
||||||
|
*/
|
||||||
|
public LinkedHashSet<PeerId> getLearners() {
|
||||||
|
return this.learners;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the learners set copy.
|
||||||
|
*
|
||||||
|
* @return learners
|
||||||
|
*/
|
||||||
|
public List<PeerId> listLearners() {
|
||||||
|
return new ArrayList<>(this.learners);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Configuration copy() {
|
||||||
|
return new Configuration(this.peers, this.learners);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the configuration is valid.
|
||||||
|
*
|
||||||
|
* @return true if the configuration is valid.
|
||||||
|
*/
|
||||||
|
public boolean isValid() {
|
||||||
|
final Set<PeerId> intersection = new HashSet<>(this.peers);
|
||||||
|
intersection.retainAll(this.learners);
|
||||||
|
return !this.peers.isEmpty() && intersection.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.peers.clear();
|
||||||
|
this.learners.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return this.peers.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the peers total number.
|
||||||
|
*
|
||||||
|
* @return total num of peers
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return this.peers.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<PeerId> iterator() {
|
||||||
|
return this.peers.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<PeerId> getPeerSet() {
|
||||||
|
return new HashSet<>(this.peers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PeerId> listPeers() {
|
||||||
|
return new ArrayList<>(this.peers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PeerId> getPeers() {
|
||||||
|
return this.peers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeers(final List<PeerId> peers) {
|
||||||
|
this.peers.clear();
|
||||||
|
for (final PeerId peer : peers) {
|
||||||
|
this.peers.add(peer.copy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendPeers(final Collection<PeerId> set) {
|
||||||
|
this.peers.addAll(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addPeer(final PeerId peer) {
|
||||||
|
return this.peers.add(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removePeer(final PeerId peer) {
|
||||||
|
return this.peers.remove(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(final PeerId peer) {
|
||||||
|
return this.peers.contains(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((this.learners == null) ? 0 : this.learners.hashCode());
|
||||||
|
result = prime * result + ((this.peers == null) ? 0 : this.peers.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Configuration other = (Configuration) obj;
|
||||||
|
if (this.learners == null) {
|
||||||
|
if (other.learners != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!this.learners.equals(other.learners)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.peers == null) {
|
||||||
|
return other.peers == null;
|
||||||
|
} else {
|
||||||
|
return this.peers.equals(other.peers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
final List<PeerId> peers = listPeers();
|
||||||
|
int i = 0;
|
||||||
|
int size = peers.size();
|
||||||
|
for (final PeerId peer : peers) {
|
||||||
|
sb.append(peer);
|
||||||
|
if (i < size - 1 || !this.learners.isEmpty()) {
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = this.learners.size();
|
||||||
|
i = 0;
|
||||||
|
for (final PeerId peer : this.learners) {
|
||||||
|
sb.append(peer).append(LEARNER_POSTFIX);
|
||||||
|
if (i < size - 1) {
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean parse(final String conf) {
|
||||||
|
if (StringUtils.isBlank(conf)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
reset();
|
||||||
|
final String[] peerStrs = StringUtils.split(conf, ',');
|
||||||
|
for (String peerStr : peerStrs) {
|
||||||
|
final PeerId peer = new PeerId();
|
||||||
|
int index;
|
||||||
|
boolean isLearner = false;
|
||||||
|
if ((index = peerStr.indexOf(LEARNER_POSTFIX)) > 0) {
|
||||||
|
// It's a learner
|
||||||
|
peerStr = peerStr.substring(0, index);
|
||||||
|
isLearner = true;
|
||||||
|
}
|
||||||
|
if (peer.parse(peerStr)) {
|
||||||
|
if (isLearner) {
|
||||||
|
addLearner(peer);
|
||||||
|
} else {
|
||||||
|
addPeer(peer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.error("Fail to parse peer {} in {}, ignore it.", peerStr, conf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the difference between |*this| and |rhs|
|
||||||
|
* |included| would be assigned to |*this| - |rhs|
|
||||||
|
* |excluded| would be assigned to |rhs| - |*this|
|
||||||
|
*/
|
||||||
|
public void diff(final Configuration rhs, final Configuration included, final Configuration excluded) {
|
||||||
|
included.peers = new ArrayList<>(this.peers);
|
||||||
|
included.peers.removeAll(rhs.peers);
|
||||||
|
excluded.peers = new ArrayList<>(rhs.peers);
|
||||||
|
excluded.peers.removeAll(this.peers);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.conf;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.LogId;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A configuration entry with current peers and old peers.
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 2:25:06 PM
|
||||||
|
*/
|
||||||
|
public class ConfigurationEntry {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationEntry.class);
|
||||||
|
|
||||||
|
private LogId id = new LogId(0, 0);
|
||||||
|
private Configuration conf = new Configuration();
|
||||||
|
private Configuration oldConf = new Configuration();
|
||||||
|
|
||||||
|
public LogId getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(final LogId id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Configuration getConf() {
|
||||||
|
return this.conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConf(final Configuration conf) {
|
||||||
|
this.conf = conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Configuration getOldConf() {
|
||||||
|
return this.oldConf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOldConf(final Configuration oldConf) {
|
||||||
|
this.oldConf = oldConf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationEntry() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationEntry(final LogId id, final Configuration conf, final Configuration oldConf) {
|
||||||
|
super();
|
||||||
|
this.id = id;
|
||||||
|
this.conf = conf;
|
||||||
|
this.oldConf = oldConf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStable() {
|
||||||
|
return this.oldConf.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return this.conf.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<PeerId> listPeers() {
|
||||||
|
final Set<PeerId> ret = new HashSet<>(this.conf.listPeers());
|
||||||
|
ret.addAll(this.oldConf.listPeers());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the conf entry is valid.
|
||||||
|
*
|
||||||
|
* @return if the the entry is valid
|
||||||
|
*/
|
||||||
|
public boolean isValid() {
|
||||||
|
if (!this.conf.isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The peer set and learner set should not have intersection set.
|
||||||
|
final Set<PeerId> intersection = listPeers();
|
||||||
|
intersection.retainAll(listLearners());
|
||||||
|
if (intersection.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOG.error("Invalid conf entry {}, peers and learners have intersection: {}.", this, intersection);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<PeerId> listLearners() {
|
||||||
|
final Set<PeerId> ret = new HashSet<>(this.conf.getLearners());
|
||||||
|
ret.addAll(this.oldConf.getLearners());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsLearner(final PeerId learner) {
|
||||||
|
return this.conf.getLearners().contains(learner) || this.oldConf.getLearners().contains(learner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(final PeerId peer) {
|
||||||
|
return this.conf.contains(peer) || this.oldConf.contains(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ConfigurationEntry [id=" + this.id + ", conf=" + this.conf + ", oldConf=" + this.oldConf + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.conf;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration manager
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
* <p>
|
||||||
|
* 2018-Apr-04 2:24:54 PM
|
||||||
|
*/
|
||||||
|
public class ConfigurationManager {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationManager.class);
|
||||||
|
|
||||||
|
private final LinkedList<ConfigurationEntry> configurations = new LinkedList<>();
|
||||||
|
private ConfigurationEntry snapshot = new ConfigurationEntry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new conf entry.
|
||||||
|
*/
|
||||||
|
public boolean add(final ConfigurationEntry entry) {
|
||||||
|
if (!this.configurations.isEmpty()) {
|
||||||
|
if (this.configurations.peekLast().getId().getIndex() >= entry.getId().getIndex()) {
|
||||||
|
LOG.error("Did you forget to call truncateSuffix before the last log index goes back.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.configurations.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [1, first_index_kept) are being discarded
|
||||||
|
*/
|
||||||
|
public void truncatePrefix(final long firstIndexKept) {
|
||||||
|
while (!this.configurations.isEmpty() && this.configurations.peekFirst().getId().getIndex() < firstIndexKept) {
|
||||||
|
this.configurations.pollFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (last_index_kept, infinity) are being discarded
|
||||||
|
*/
|
||||||
|
public void truncateSuffix(final long lastIndexKept) {
|
||||||
|
while (!this.configurations.isEmpty() && this.configurations.peekLast().getId().getIndex() > lastIndexKept) {
|
||||||
|
this.configurations.pollLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationEntry getSnapshot() {
|
||||||
|
return this.snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnapshot(final ConfigurationEntry snapshot) {
|
||||||
|
this.snapshot = snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationEntry getLastConfiguration() {
|
||||||
|
if (this.configurations.isEmpty()) {
|
||||||
|
return snapshot;
|
||||||
|
} else {
|
||||||
|
return this.configurations.peekLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationEntry get(final long lastIncludedIndex) {
|
||||||
|
if (this.configurations.isEmpty()) {
|
||||||
|
Requires.requireTrue(lastIncludedIndex >= this.snapshot.getId().getIndex(),
|
||||||
|
"lastIncludedIndex %d is less than snapshot index %d", lastIncludedIndex, this.snapshot.getId()
|
||||||
|
.getIndex());
|
||||||
|
return this.snapshot;
|
||||||
|
}
|
||||||
|
ListIterator<ConfigurationEntry> it = this.configurations.listIterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
if (it.next().getId().getIndex() > lastIncludedIndex) {
|
||||||
|
it.previous();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (it.hasPrevious()) {
|
||||||
|
// find the first position that is less than or equal to lastIncludedIndex.
|
||||||
|
return it.previous();
|
||||||
|
} else {
|
||||||
|
// position not found position, return snapshot.
|
||||||
|
return this.snapshot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,283 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.StampedLock;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.FSMCaller;
|
||||||
|
import com.alipay.sofa.jraft.Lifecycle;
|
||||||
|
import com.alipay.sofa.jraft.closure.ClosureQueue;
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
import com.alipay.sofa.jraft.entity.Ballot;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.option.BallotBoxOptions;
|
||||||
|
import com.alipay.sofa.jraft.util.Describer;
|
||||||
|
import com.alipay.sofa.jraft.util.OnlyForTest;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
import com.alipay.sofa.jraft.util.SegmentList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ballot box for voting.
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 2:32:10 PM
|
||||||
|
*/
|
||||||
|
@ThreadSafe
|
||||||
|
public class BallotBox implements Lifecycle<BallotBoxOptions>, Describer {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(BallotBox.class);
|
||||||
|
|
||||||
|
private FSMCaller waiter;
|
||||||
|
private ClosureQueue closureQueue;
|
||||||
|
private final StampedLock stampedLock = new StampedLock();
|
||||||
|
private long lastCommittedIndex = 0;
|
||||||
|
private long pendingIndex;
|
||||||
|
private final SegmentList<Ballot> pendingMetaQueue = new SegmentList<>(false);
|
||||||
|
|
||||||
|
@OnlyForTest
|
||||||
|
long getPendingIndex() {
|
||||||
|
return this.pendingIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyForTest
|
||||||
|
SegmentList<Ballot> getPendingMetaQueue() {
|
||||||
|
return this.pendingMetaQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastCommittedIndex() {
|
||||||
|
long stamp = this.stampedLock.tryOptimisticRead();
|
||||||
|
final long optimisticVal = this.lastCommittedIndex;
|
||||||
|
if (this.stampedLock.validate(stamp)) {
|
||||||
|
return optimisticVal;
|
||||||
|
}
|
||||||
|
stamp = this.stampedLock.readLock();
|
||||||
|
try {
|
||||||
|
return this.lastCommittedIndex;
|
||||||
|
} finally {
|
||||||
|
this.stampedLock.unlockRead(stamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean init(final BallotBoxOptions opts) {
|
||||||
|
if (opts.getWaiter() == null || opts.getClosureQueue() == null) {
|
||||||
|
LOG.error("waiter or closure queue is null.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.waiter = opts.getWaiter();
|
||||||
|
this.closureQueue = opts.getClosureQueue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by leader, otherwise the behavior is undefined
|
||||||
|
* Set logs in [first_log_index, last_log_index] are stable at |peer|.
|
||||||
|
*/
|
||||||
|
public boolean commitAt(final long firstLogIndex, final long lastLogIndex, final PeerId peer) {
|
||||||
|
// TODO use lock-free algorithm here?
|
||||||
|
final long stamp = this.stampedLock.writeLock();
|
||||||
|
long lastCommittedIndex = 0;
|
||||||
|
try {
|
||||||
|
if (this.pendingIndex == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (lastLogIndex < this.pendingIndex) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastLogIndex >= this.pendingIndex + this.pendingMetaQueue.size()) {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final long startAt = Math.max(this.pendingIndex, firstLogIndex);
|
||||||
|
Ballot.PosHint hint = new Ballot.PosHint();
|
||||||
|
for (long logIndex = startAt; logIndex <= lastLogIndex; logIndex++) {
|
||||||
|
final Ballot bl = this.pendingMetaQueue.get((int) (logIndex - this.pendingIndex));
|
||||||
|
hint = bl.grant(peer, hint);
|
||||||
|
if (bl.isGranted()) {
|
||||||
|
lastCommittedIndex = logIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastCommittedIndex == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// When removing a peer off the raft group which contains even number of
|
||||||
|
// peers, the quorum would decrease by 1, e.g. 3 of 4 changes to 2 of 3. In
|
||||||
|
// this case, the log after removal may be committed before some previous
|
||||||
|
// logs, since we use the new configuration to deal the quorum of the
|
||||||
|
// removal request, we think it's safe to commit all the uncommitted
|
||||||
|
// previous logs, which is not well proved right now
|
||||||
|
this.pendingMetaQueue.removeFromFirst((int) (lastCommittedIndex - this.pendingIndex) + 1);
|
||||||
|
LOG.debug("Committed log fromIndex={}, toIndex={}.", this.pendingIndex, lastCommittedIndex);
|
||||||
|
this.pendingIndex = lastCommittedIndex + 1;
|
||||||
|
this.lastCommittedIndex = lastCommittedIndex;
|
||||||
|
} finally {
|
||||||
|
this.stampedLock.unlockWrite(stamp);
|
||||||
|
}
|
||||||
|
this.waiter.onCommitted(lastCommittedIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the leader steps down, otherwise the behavior is undefined
|
||||||
|
* When a leader steps down, the uncommitted user applications should
|
||||||
|
* fail immediately, which the new leader will deal whether to commit or
|
||||||
|
* truncate.
|
||||||
|
*/
|
||||||
|
public void clearPendingTasks() {
|
||||||
|
final long stamp = this.stampedLock.writeLock();
|
||||||
|
try {
|
||||||
|
this.pendingMetaQueue.clear();
|
||||||
|
this.pendingIndex = 0;
|
||||||
|
this.closureQueue.clear();
|
||||||
|
} finally {
|
||||||
|
this.stampedLock.unlockWrite(stamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a candidate becomes the new leader, otherwise the behavior is
|
||||||
|
* undefined.
|
||||||
|
* According the the raft algorithm, the logs from previous terms can't be
|
||||||
|
* committed until a log at the new term becomes committed, so
|
||||||
|
* |newPendingIndex| should be |last_log_index| + 1.
|
||||||
|
* @param newPendingIndex pending index of new leader
|
||||||
|
* @return returns true if reset success
|
||||||
|
*/
|
||||||
|
public boolean resetPendingIndex(final long newPendingIndex) {
|
||||||
|
final long stamp = this.stampedLock.writeLock();
|
||||||
|
try {
|
||||||
|
if (!(this.pendingIndex == 0 && this.pendingMetaQueue.isEmpty())) {
|
||||||
|
LOG.error("resetPendingIndex fail, pendingIndex={}, pendingMetaQueueSize={}.", this.pendingIndex,
|
||||||
|
this.pendingMetaQueue.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (newPendingIndex <= this.lastCommittedIndex) {
|
||||||
|
LOG.error("resetPendingIndex fail, newPendingIndex={}, lastCommittedIndex={}.", newPendingIndex,
|
||||||
|
this.lastCommittedIndex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.pendingIndex = newPendingIndex;
|
||||||
|
this.closureQueue.resetFirstIndex(newPendingIndex);
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
this.stampedLock.unlockWrite(stamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by leader, otherwise the behavior is undefined
|
||||||
|
* Store application context before replication.
|
||||||
|
*
|
||||||
|
* @param conf current configuration
|
||||||
|
* @param oldConf old configuration
|
||||||
|
* @param done callback
|
||||||
|
* @return returns true on success
|
||||||
|
*/
|
||||||
|
public boolean appendPendingTask(final Configuration conf, final Configuration oldConf, final Closure done) {
|
||||||
|
final Ballot bl = new Ballot();
|
||||||
|
if (!bl.init(conf, oldConf)) {
|
||||||
|
LOG.error("Fail to init ballot.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final long stamp = this.stampedLock.writeLock();
|
||||||
|
try {
|
||||||
|
if (this.pendingIndex <= 0) {
|
||||||
|
LOG.error("Fail to appendingTask, pendingIndex={}.", this.pendingIndex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.pendingMetaQueue.add(bl);
|
||||||
|
this.closureQueue.appendPendingClosure(done);
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
this.stampedLock.unlockWrite(stamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by follower, otherwise the behavior is undefined.
|
||||||
|
* Set committed index received from leader
|
||||||
|
*
|
||||||
|
* @param lastCommittedIndex last committed index
|
||||||
|
* @return returns true if set success
|
||||||
|
*/
|
||||||
|
public boolean setLastCommittedIndex(final long lastCommittedIndex) {
|
||||||
|
boolean doUnlock = true;
|
||||||
|
final long stamp = this.stampedLock.writeLock();
|
||||||
|
try {
|
||||||
|
if (this.pendingIndex != 0 || !this.pendingMetaQueue.isEmpty()) {
|
||||||
|
Requires.requireTrue(lastCommittedIndex < this.pendingIndex,
|
||||||
|
"Node changes to leader, pendingIndex=%d, param lastCommittedIndex=%d", this.pendingIndex,
|
||||||
|
lastCommittedIndex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (lastCommittedIndex < this.lastCommittedIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (lastCommittedIndex > this.lastCommittedIndex) {
|
||||||
|
this.lastCommittedIndex = lastCommittedIndex;
|
||||||
|
this.stampedLock.unlockWrite(stamp);
|
||||||
|
doUnlock = false;
|
||||||
|
this.waiter.onCommitted(lastCommittedIndex);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (doUnlock) {
|
||||||
|
this.stampedLock.unlockWrite(stamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
clearPendingTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describe(final Printer out) {
|
||||||
|
long _lastCommittedIndex;
|
||||||
|
long _pendingIndex;
|
||||||
|
long _pendingMetaQueueSize;
|
||||||
|
long stamp = this.stampedLock.tryOptimisticRead();
|
||||||
|
if (this.stampedLock.validate(stamp)) {
|
||||||
|
_lastCommittedIndex = this.lastCommittedIndex;
|
||||||
|
_pendingIndex = this.pendingIndex;
|
||||||
|
_pendingMetaQueueSize = this.pendingMetaQueue.size();
|
||||||
|
} else {
|
||||||
|
stamp = this.stampedLock.readLock();
|
||||||
|
try {
|
||||||
|
_lastCommittedIndex = this.lastCommittedIndex;
|
||||||
|
_pendingIndex = this.pendingIndex;
|
||||||
|
_pendingMetaQueueSize = this.pendingMetaQueue.size();
|
||||||
|
} finally {
|
||||||
|
this.stampedLock.unlockRead(stamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.print(" lastCommittedIndex: ") //
|
||||||
|
.println(_lastCommittedIndex);
|
||||||
|
out.print(" pendingIndex: ") //
|
||||||
|
.println(_pendingIndex);
|
||||||
|
out.print(" pendingMetaQueueSize: ") //
|
||||||
|
.println(_pendingMetaQueueSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,672 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.CliService;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.error.JRaftException;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftError;
|
||||||
|
import com.alipay.sofa.jraft.option.CliOptions;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliClientService;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.AddLearnersRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.AddPeerRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.AddPeerResponse;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.ChangePeersRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.ChangePeersResponse;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.GetLeaderRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.GetLeaderResponse;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.GetPeersRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.GetPeersResponse;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.LearnersOpResponse;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.RemoveLearnersRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.RemovePeerRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.RemovePeerResponse;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.ResetLearnersRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.ResetPeerRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.SnapshotRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.CliRequests.TransferLeaderRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RpcRequests.ErrorResponse;
|
||||||
|
import com.alipay.sofa.jraft.rpc.impl.cli.CliClientServiceImpl;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
import com.alipay.sofa.jraft.util.Utils;
|
||||||
|
import com.google.protobuf.Message;
|
||||||
|
import com.google.protobuf.ProtocolStringList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cli service implementation.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
* @author jiachun.fjc
|
||||||
|
*/
|
||||||
|
public class CliServiceImpl implements CliService {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(CliServiceImpl.class);
|
||||||
|
|
||||||
|
private CliOptions cliOptions;
|
||||||
|
private CliClientService cliClientService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean init(final CliOptions opts) {
|
||||||
|
Requires.requireNonNull(opts, "Null cli options");
|
||||||
|
|
||||||
|
if (this.cliClientService != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.cliOptions = opts;
|
||||||
|
this.cliClientService = new CliClientServiceImpl();
|
||||||
|
return this.cliClientService.init(this.cliOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void shutdown() {
|
||||||
|
if (this.cliClientService == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.cliClientService.shutdown();
|
||||||
|
this.cliClientService = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recordConfigurationChange(final String groupId, final List<String> oldPeersList,
|
||||||
|
final List<String> newPeersList) {
|
||||||
|
final Configuration oldConf = new Configuration();
|
||||||
|
for (final String peerIdStr : oldPeersList) {
|
||||||
|
final PeerId oldPeer = new PeerId();
|
||||||
|
oldPeer.parse(peerIdStr);
|
||||||
|
oldConf.addPeer(oldPeer);
|
||||||
|
}
|
||||||
|
final Configuration newConf = new Configuration();
|
||||||
|
for (final String peerIdStr : newPeersList) {
|
||||||
|
final PeerId newPeer = new PeerId();
|
||||||
|
newPeer.parse(peerIdStr);
|
||||||
|
newConf.addPeer(newPeer);
|
||||||
|
}
|
||||||
|
LOG.info("Configuration of replication group {} changed from {} to {}.", groupId, oldConf, newConf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Status checkLeaderAndConnect(final String groupId, final Configuration conf, final PeerId leaderId) {
|
||||||
|
final Status st = getLeader(groupId, conf, leaderId);
|
||||||
|
if (!st.isOk()) {
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.cliClientService.connect(leaderId.getEndpoint())) {
|
||||||
|
return new Status(-1, "Fail to init channel to leader %s", leaderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Status.OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status addPeer(final String groupId, final Configuration conf, final PeerId peer) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireNonNull(conf, "Null configuration");
|
||||||
|
Requires.requireNonNull(peer, "Null peer");
|
||||||
|
|
||||||
|
final PeerId leaderId = new PeerId();
|
||||||
|
final Status st = checkLeaderAndConnect(groupId, conf, leaderId);
|
||||||
|
if (!st.isOk()) {
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
final AddPeerRequest.Builder rb = AddPeerRequest.newBuilder() //
|
||||||
|
.setGroupId(groupId) //
|
||||||
|
.setLeaderId(leaderId.toString()) //
|
||||||
|
.setPeerId(peer.toString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Message result = this.cliClientService.addPeer(leaderId.getEndpoint(), rb.build(), null).get();
|
||||||
|
if (result instanceof AddPeerResponse) {
|
||||||
|
final AddPeerResponse resp = (AddPeerResponse) result;
|
||||||
|
recordConfigurationChange(groupId, resp.getOldPeersList(), resp.getNewPeersList());
|
||||||
|
return Status.OK();
|
||||||
|
} else {
|
||||||
|
return statusFromResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return new Status(-1, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Status statusFromResponse(final Message result) {
|
||||||
|
final ErrorResponse resp = (ErrorResponse) result;
|
||||||
|
return new Status(resp.getErrorCode(), resp.getErrorMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status removePeer(final String groupId, final Configuration conf, final PeerId peer) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireNonNull(conf, "Null configuration");
|
||||||
|
Requires.requireNonNull(peer, "Null peer");
|
||||||
|
Requires.requireTrue(!peer.isEmpty(), "Removing peer is blank");
|
||||||
|
|
||||||
|
final PeerId leaderId = new PeerId();
|
||||||
|
final Status st = checkLeaderAndConnect(groupId, conf, leaderId);
|
||||||
|
if (!st.isOk()) {
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
final RemovePeerRequest.Builder rb = RemovePeerRequest.newBuilder() //
|
||||||
|
.setGroupId(groupId) //
|
||||||
|
.setLeaderId(leaderId.toString()) //
|
||||||
|
.setPeerId(peer.toString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Message result = this.cliClientService.removePeer(leaderId.getEndpoint(), rb.build(), null).get();
|
||||||
|
if (result instanceof RemovePeerResponse) {
|
||||||
|
final RemovePeerResponse resp = (RemovePeerResponse) result;
|
||||||
|
recordConfigurationChange(groupId, resp.getOldPeersList(), resp.getNewPeersList());
|
||||||
|
return Status.OK();
|
||||||
|
} else {
|
||||||
|
return statusFromResponse(result);
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return new Status(-1, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status changePeers(final String groupId, final Configuration conf, final Configuration newPeers) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireNonNull(conf, "Null configuration");
|
||||||
|
Requires.requireNonNull(newPeers, "Null new peers");
|
||||||
|
|
||||||
|
final PeerId leaderId = new PeerId();
|
||||||
|
final Status st = checkLeaderAndConnect(groupId, conf, leaderId);
|
||||||
|
if (!st.isOk()) {
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ChangePeersRequest.Builder rb = ChangePeersRequest.newBuilder() //
|
||||||
|
.setGroupId(groupId) //
|
||||||
|
.setLeaderId(leaderId.toString());
|
||||||
|
for (final PeerId peer : newPeers) {
|
||||||
|
rb.addNewPeers(peer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Message result = this.cliClientService.changePeers(leaderId.getEndpoint(), rb.build(), null).get();
|
||||||
|
if (result instanceof ChangePeersResponse) {
|
||||||
|
final ChangePeersResponse resp = (ChangePeersResponse) result;
|
||||||
|
recordConfigurationChange(groupId, resp.getOldPeersList(), resp.getNewPeersList());
|
||||||
|
return Status.OK();
|
||||||
|
} else {
|
||||||
|
return statusFromResponse(result);
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return new Status(-1, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status resetPeer(final String groupId, final PeerId peerId, final Configuration newPeers) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireNonNull(peerId, "Null peerId");
|
||||||
|
Requires.requireNonNull(newPeers, "Null new peers");
|
||||||
|
|
||||||
|
if (!this.cliClientService.connect(peerId.getEndpoint())) {
|
||||||
|
return new Status(-1, "Fail to init channel to %s", peerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ResetPeerRequest.Builder rb = ResetPeerRequest.newBuilder() //
|
||||||
|
.setGroupId(groupId) //
|
||||||
|
.setPeerId(peerId.toString());
|
||||||
|
for (final PeerId peer : newPeers) {
|
||||||
|
rb.addNewPeers(peer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Message result = this.cliClientService.resetPeer(peerId.getEndpoint(), rb.build(), null).get();
|
||||||
|
return statusFromResponse(result);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return new Status(-1, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPeers(final Collection<PeerId> peers) {
|
||||||
|
for (final PeerId peer : peers) {
|
||||||
|
Requires.requireNonNull(peer, "Null peer in collection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status addLearners(final String groupId, final Configuration conf, final List<PeerId> learners) {
|
||||||
|
checkLearnersOpParams(groupId, conf, learners);
|
||||||
|
|
||||||
|
final PeerId leaderId = new PeerId();
|
||||||
|
final Status st = getLeader(groupId, conf, leaderId);
|
||||||
|
if (!st.isOk()) {
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.cliClientService.connect(leaderId.getEndpoint())) {
|
||||||
|
return new Status(-1, "Fail to init channel to leader %s", leaderId);
|
||||||
|
}
|
||||||
|
final AddLearnersRequest.Builder rb = AddLearnersRequest.newBuilder() //
|
||||||
|
.setGroupId(groupId) //
|
||||||
|
.setLeaderId(leaderId.toString());
|
||||||
|
for (final PeerId peer : learners) {
|
||||||
|
rb.addLearners(peer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Message result = this.cliClientService.addLearners(leaderId.getEndpoint(), rb.build(), null).get();
|
||||||
|
return processLearnersOpResponse(groupId, result, "adding learners: %s", learners);
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return new Status(-1, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkLearnersOpParams(final String groupId, final Configuration conf, final List<PeerId> learners) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireNonNull(conf, "Null configuration");
|
||||||
|
Requires.requireTrue(learners != null && !learners.isEmpty(), "Empty peers");
|
||||||
|
checkPeers(learners);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Status processLearnersOpResponse(final String groupId, final Message result, final String fmt,
|
||||||
|
final Object... formatArgs) {
|
||||||
|
if (result instanceof LearnersOpResponse) {
|
||||||
|
final LearnersOpResponse resp = (LearnersOpResponse) result;
|
||||||
|
final Configuration oldConf = new Configuration();
|
||||||
|
for (final String peerIdStr : resp.getOldLearnersList()) {
|
||||||
|
final PeerId oldPeer = new PeerId();
|
||||||
|
oldPeer.parse(peerIdStr);
|
||||||
|
oldConf.addLearner(oldPeer);
|
||||||
|
}
|
||||||
|
final Configuration newConf = new Configuration();
|
||||||
|
for (final String peerIdStr : resp.getNewLearnersList()) {
|
||||||
|
final PeerId newPeer = new PeerId();
|
||||||
|
newPeer.parse(peerIdStr);
|
||||||
|
newConf.addLearner(newPeer);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info("Learners of replication group {} changed from {} to {} after {}.", groupId, oldConf, newConf,
|
||||||
|
String.format(fmt, formatArgs));
|
||||||
|
return Status.OK();
|
||||||
|
} else {
|
||||||
|
return statusFromResponse(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status removeLearners(final String groupId, final Configuration conf, final List<PeerId> learners) {
|
||||||
|
checkLearnersOpParams(groupId, conf, learners);
|
||||||
|
|
||||||
|
final PeerId leaderId = new PeerId();
|
||||||
|
final Status st = getLeader(groupId, conf, leaderId);
|
||||||
|
if (!st.isOk()) {
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.cliClientService.connect(leaderId.getEndpoint())) {
|
||||||
|
return new Status(-1, "Fail to init channel to leader %s", leaderId);
|
||||||
|
}
|
||||||
|
final RemoveLearnersRequest.Builder rb = RemoveLearnersRequest.newBuilder() //
|
||||||
|
.setGroupId(groupId) //
|
||||||
|
.setLeaderId(leaderId.toString());
|
||||||
|
for (final PeerId peer : learners) {
|
||||||
|
rb.addLearners(peer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Message result = this.cliClientService.removeLearners(leaderId.getEndpoint(), rb.build(), null).get();
|
||||||
|
return processLearnersOpResponse(groupId, result, "removing learners: %s", learners);
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return new Status(-1, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status learner2Follower(final String groupId, final Configuration conf, final PeerId learner) {
|
||||||
|
Status status = removeLearners(groupId, conf, Arrays.asList(learner));
|
||||||
|
if (status.isOk()) {
|
||||||
|
status = addPeer(groupId, conf, new PeerId(learner.getIp(), learner.getPort()));
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status resetLearners(final String groupId, final Configuration conf, final List<PeerId> learners) {
|
||||||
|
checkLearnersOpParams(groupId, conf, learners);
|
||||||
|
|
||||||
|
final PeerId leaderId = new PeerId();
|
||||||
|
final Status st = getLeader(groupId, conf, leaderId);
|
||||||
|
if (!st.isOk()) {
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.cliClientService.connect(leaderId.getEndpoint())) {
|
||||||
|
return new Status(-1, "Fail to init channel to leader %s", leaderId);
|
||||||
|
}
|
||||||
|
final ResetLearnersRequest.Builder rb = ResetLearnersRequest.newBuilder() //
|
||||||
|
.setGroupId(groupId) //
|
||||||
|
.setLeaderId(leaderId.toString());
|
||||||
|
for (final PeerId peer : learners) {
|
||||||
|
rb.addLearners(peer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Message result = this.cliClientService.resetLearners(leaderId.getEndpoint(), rb.build(), null).get();
|
||||||
|
return processLearnersOpResponse(groupId, result, "resetting learners: %s", learners);
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return new Status(-1, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status transferLeader(final String groupId, final Configuration conf, final PeerId peer) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireNonNull(conf, "Null configuration");
|
||||||
|
Requires.requireNonNull(peer, "Null peer");
|
||||||
|
|
||||||
|
final PeerId leaderId = new PeerId();
|
||||||
|
final Status st = checkLeaderAndConnect(groupId, conf, leaderId);
|
||||||
|
if (!st.isOk()) {
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
final TransferLeaderRequest.Builder rb = TransferLeaderRequest.newBuilder() //
|
||||||
|
.setGroupId(groupId) //
|
||||||
|
.setLeaderId(leaderId.toString());
|
||||||
|
if (!peer.isEmpty()) {
|
||||||
|
rb.setPeerId(peer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Message result = this.cliClientService.transferLeader(leaderId.getEndpoint(), rb.build(), null).get();
|
||||||
|
return statusFromResponse(result);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return new Status(-1, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status snapshot(final String groupId, final PeerId peer) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireNonNull(peer, "Null peer");
|
||||||
|
|
||||||
|
if (!this.cliClientService.connect(peer.getEndpoint())) {
|
||||||
|
return new Status(-1, "Fail to init channel to %s", peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
final SnapshotRequest.Builder rb = SnapshotRequest.newBuilder() //
|
||||||
|
.setGroupId(groupId) //
|
||||||
|
.setPeerId(peer.toString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Message result = this.cliClientService.snapshot(peer.getEndpoint(), rb.build(), null).get();
|
||||||
|
return statusFromResponse(result);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return new Status(-1, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status getLeader(final String groupId, final Configuration conf, final PeerId leaderId) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireNonNull(leaderId, "Null leader id");
|
||||||
|
|
||||||
|
if (conf == null || conf.isEmpty()) {
|
||||||
|
return new Status(RaftError.EINVAL, "Empty group configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Status st = new Status(-1, "Fail to get leader of group %s", groupId);
|
||||||
|
for (final PeerId peer : conf) {
|
||||||
|
if (!this.cliClientService.connect(peer.getEndpoint())) {
|
||||||
|
LOG.error("Fail to connect peer {} to get leader for group {}.", peer, groupId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final GetLeaderRequest.Builder rb = GetLeaderRequest.newBuilder() //
|
||||||
|
.setGroupId(groupId) //
|
||||||
|
.setPeerId(peer.toString());
|
||||||
|
|
||||||
|
final Future<Message> result = this.cliClientService.getLeader(peer.getEndpoint(), rb.build(), null);
|
||||||
|
try {
|
||||||
|
|
||||||
|
final Message msg = result.get(
|
||||||
|
this.cliOptions.getTimeoutMs() <= 0 ? this.cliOptions.getRpcDefaultTimeout() : this.cliOptions
|
||||||
|
.getTimeoutMs(), TimeUnit.MILLISECONDS);
|
||||||
|
if (msg instanceof ErrorResponse) {
|
||||||
|
if (st.isOk()) {
|
||||||
|
st.setError(-1, ((ErrorResponse) msg).getErrorMsg());
|
||||||
|
} else {
|
||||||
|
final String savedMsg = st.getErrorMsg();
|
||||||
|
st.setError(-1, "%s, %s", savedMsg, ((ErrorResponse) msg).getErrorMsg());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final GetLeaderResponse response = (GetLeaderResponse) msg;
|
||||||
|
if (leaderId.parse(response.getLeaderId())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
if (st.isOk()) {
|
||||||
|
st.setError(-1, e.getMessage());
|
||||||
|
} else {
|
||||||
|
final String savedMsg = st.getErrorMsg();
|
||||||
|
st.setError(-1, "%s, %s", savedMsg, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leaderId.isEmpty()) {
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
return Status.OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PeerId> getPeers(final String groupId, final Configuration conf) {
|
||||||
|
return getPeers(groupId, conf, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PeerId> getAlivePeers(final String groupId, final Configuration conf) {
|
||||||
|
return getPeers(groupId, conf, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PeerId> getLearners(final String groupId, final Configuration conf) {
|
||||||
|
return getPeers(groupId, conf, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PeerId> getAliveLearners(final String groupId, final Configuration conf) {
|
||||||
|
return getPeers(groupId, conf, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status rebalance(final Set<String> balanceGroupIds, final Configuration conf,
|
||||||
|
final Map<String, PeerId> rebalancedLeaderIds) {
|
||||||
|
Requires.requireNonNull(balanceGroupIds, "Null balance group ids");
|
||||||
|
Requires.requireTrue(!balanceGroupIds.isEmpty(), "Empty balance group ids");
|
||||||
|
Requires.requireNonNull(conf, "Null configuration");
|
||||||
|
Requires.requireTrue(!conf.isEmpty(), "No peers of configuration");
|
||||||
|
|
||||||
|
LOG.info("Rebalance start with raft groups={}.", balanceGroupIds);
|
||||||
|
|
||||||
|
final long start = Utils.monotonicMs();
|
||||||
|
int transfers = 0;
|
||||||
|
Status failedStatus = null;
|
||||||
|
final Queue<String> groupDeque = new ArrayDeque<>(balanceGroupIds);
|
||||||
|
final LeaderCounter leaderCounter = new LeaderCounter(balanceGroupIds.size(), conf.size());
|
||||||
|
for (;;) {
|
||||||
|
final String groupId = groupDeque.poll();
|
||||||
|
if (groupId == null) { // well done
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PeerId leaderId = new PeerId();
|
||||||
|
final Status leaderStatus = getLeader(groupId, conf, leaderId);
|
||||||
|
if (!leaderStatus.isOk()) {
|
||||||
|
failedStatus = leaderStatus;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rebalancedLeaderIds != null) {
|
||||||
|
rebalancedLeaderIds.put(groupId, leaderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leaderCounter.incrementAndGet(leaderId) <= leaderCounter.getExpectedAverage()) {
|
||||||
|
// The num of leaders is less than the expected average, we are going to deal with others
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the target peer and try to transfer the leader to this peer
|
||||||
|
final PeerId targetPeer = findTargetPeer(leaderId, groupId, conf, leaderCounter);
|
||||||
|
if (!targetPeer.isEmpty()) {
|
||||||
|
final Status transferStatus = transferLeader(groupId, conf, targetPeer);
|
||||||
|
transfers++;
|
||||||
|
if (!transferStatus.isOk()) {
|
||||||
|
// The failure of `transfer leader` usually means the node is busy,
|
||||||
|
// so we return failure status and should try `rebalance` again later.
|
||||||
|
failedStatus = transferStatus;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info("Group {} transfer leader to {}.", groupId, targetPeer);
|
||||||
|
leaderCounter.decrementAndGet(leaderId);
|
||||||
|
groupDeque.add(groupId);
|
||||||
|
if (rebalancedLeaderIds != null) {
|
||||||
|
rebalancedLeaderIds.put(groupId, targetPeer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Status status = failedStatus != null ? failedStatus : Status.OK();
|
||||||
|
if (LOG.isInfoEnabled()) {
|
||||||
|
LOG.info(
|
||||||
|
"Rebalanced raft groups={}, status={}, number of transfers={}, elapsed time={} ms, rebalanced result={}.",
|
||||||
|
balanceGroupIds, status, transfers, Utils.monotonicMs() - start, rebalancedLeaderIds);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PeerId findTargetPeer(final PeerId self, final String groupId, final Configuration conf,
|
||||||
|
final LeaderCounter leaderCounter) {
|
||||||
|
for (final PeerId peerId : getAlivePeers(groupId, conf)) {
|
||||||
|
if (peerId.equals(self)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (leaderCounter.get(peerId) >= leaderCounter.getExpectedAverage()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return peerId;
|
||||||
|
}
|
||||||
|
return PeerId.emptyPeer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PeerId> getPeers(final String groupId, final Configuration conf, final boolean returnLearners,
|
||||||
|
final boolean onlyGetAlive) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(groupId), "Blank group id");
|
||||||
|
Requires.requireNonNull(conf, "Null conf");
|
||||||
|
|
||||||
|
final PeerId leaderId = new PeerId();
|
||||||
|
final Status st = getLeader(groupId, conf, leaderId);
|
||||||
|
if (!st.isOk()) {
|
||||||
|
throw new IllegalStateException(st.getErrorMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.cliClientService.connect(leaderId.getEndpoint())) {
|
||||||
|
throw new IllegalStateException("Fail to init channel to leader " + leaderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final GetPeersRequest.Builder rb = GetPeersRequest.newBuilder() //
|
||||||
|
.setGroupId(groupId) //
|
||||||
|
.setLeaderId(leaderId.toString()) //
|
||||||
|
.setOnlyAlive(onlyGetAlive);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Message result = this.cliClientService.getPeers(leaderId.getEndpoint(), rb.build(), null).get(
|
||||||
|
this.cliOptions.getTimeoutMs() <= 0 ? this.cliOptions.getRpcDefaultTimeout()
|
||||||
|
: this.cliOptions.getTimeoutMs(), TimeUnit.MILLISECONDS);
|
||||||
|
if (result instanceof GetPeersResponse) {
|
||||||
|
final GetPeersResponse resp = (GetPeersResponse) result;
|
||||||
|
final List<PeerId> peerIdList = new ArrayList<>();
|
||||||
|
final ProtocolStringList responsePeers = returnLearners ? resp.getLearnersList() : resp.getPeersList();
|
||||||
|
for (final String peerIdStr : responsePeers) {
|
||||||
|
final PeerId newPeer = new PeerId();
|
||||||
|
newPeer.parse(peerIdStr);
|
||||||
|
peerIdList.add(newPeer);
|
||||||
|
}
|
||||||
|
return peerIdList;
|
||||||
|
} else {
|
||||||
|
final ErrorResponse resp = (ErrorResponse) result;
|
||||||
|
throw new JRaftException(resp.getErrorMsg());
|
||||||
|
}
|
||||||
|
} catch (final JRaftException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new JRaftException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliClientService getCliClientService() {
|
||||||
|
return this.cliClientService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LeaderCounter {
|
||||||
|
|
||||||
|
private final Map<PeerId, Integer> counter = new HashMap<>();
|
||||||
|
// The expected average leader number on every peerId
|
||||||
|
private final int expectedAverage;
|
||||||
|
|
||||||
|
public LeaderCounter(final int groupCount, final int peerCount) {
|
||||||
|
this.expectedAverage = (int) Math.ceil((double) groupCount / peerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getExpectedAverage() {
|
||||||
|
return this.expectedAverage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int incrementAndGet(final PeerId peerId) {
|
||||||
|
return this.counter.compute(peerId, (ignored, num) -> num == null ? 1 : num + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int decrementAndGet(final PeerId peerId) {
|
||||||
|
return this.counter.compute(peerId, (ignored, num) -> num == null ? 0 : num - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int get(final PeerId peerId) {
|
||||||
|
return this.counter.getOrDefault(peerId, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.JRaftServiceFactory;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryCodecFactory;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.v2.LogEntryV2CodecFactory;
|
||||||
|
import com.alipay.sofa.jraft.option.RaftOptions;
|
||||||
|
import com.alipay.sofa.jraft.storage.LogStorage;
|
||||||
|
import com.alipay.sofa.jraft.storage.RaftMetaStorage;
|
||||||
|
import com.alipay.sofa.jraft.storage.SnapshotStorage;
|
||||||
|
import com.alipay.sofa.jraft.storage.impl.LocalRaftMetaStorage;
|
||||||
|
import com.alipay.sofa.jraft.storage.impl.RocksDBLogStorage;
|
||||||
|
import com.alipay.sofa.jraft.storage.snapshot.local.LocalSnapshotStorage;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
import com.alipay.sofa.jraft.util.SPI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default factory for JRaft services.
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
* @since 1.2.6
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SPI
|
||||||
|
public class DefaultJRaftServiceFactory implements JRaftServiceFactory {
|
||||||
|
|
||||||
|
public static DefaultJRaftServiceFactory newInstance() {
|
||||||
|
return new DefaultJRaftServiceFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogStorage createLogStorage(final String uri, final RaftOptions raftOptions) {
|
||||||
|
Requires.requireTrue(StringUtils.isNotBlank(uri), "Blank log storage uri.");
|
||||||
|
return new RocksDBLogStorage(uri, raftOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SnapshotStorage createSnapshotStorage(final String uri, final RaftOptions raftOptions) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(uri), "Blank snapshot storage uri.");
|
||||||
|
return new LocalSnapshotStorage(uri, raftOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RaftMetaStorage createRaftMetaStorage(final String uri, final RaftOptions raftOptions) {
|
||||||
|
Requires.requireTrue(!StringUtils.isBlank(uri), "Blank raft meta storage uri.");
|
||||||
|
return new LocalRaftMetaStorage(uri, raftOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogEntryCodecFactory createLogEntryCodecFactory() {
|
||||||
|
return LogEntryV2CodecFactory.getInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ElectionPriority Type
|
||||||
|
*
|
||||||
|
* @author zongtanghu
|
||||||
|
*/
|
||||||
|
public class ElectionPriority {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Priority -1 represents this node disabled the priority election function.
|
||||||
|
*/
|
||||||
|
public static final int Disabled = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Priority 0 is a special value so that a node will never participate in election.
|
||||||
|
*/
|
||||||
|
public static final int NotElected = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Priority 1 is a minimum value for priority election.
|
||||||
|
*/
|
||||||
|
public static final int MinValue = 1;
|
||||||
|
}
|
||||||
@ -0,0 +1,726 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.FSMCaller;
|
||||||
|
import com.alipay.sofa.jraft.StateMachine;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
import com.alipay.sofa.jraft.closure.ClosureQueue;
|
||||||
|
import com.alipay.sofa.jraft.closure.LoadSnapshotClosure;
|
||||||
|
import com.alipay.sofa.jraft.closure.SaveSnapshotClosure;
|
||||||
|
import com.alipay.sofa.jraft.closure.TaskClosure;
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
import com.alipay.sofa.jraft.conf.ConfigurationEntry;
|
||||||
|
import com.alipay.sofa.jraft.entity.EnumOutter;
|
||||||
|
import com.alipay.sofa.jraft.entity.LeaderChangeContext;
|
||||||
|
import com.alipay.sofa.jraft.entity.LogEntry;
|
||||||
|
import com.alipay.sofa.jraft.entity.LogId;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.entity.RaftOutter;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftError;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftException;
|
||||||
|
import com.alipay.sofa.jraft.option.FSMCallerOptions;
|
||||||
|
import com.alipay.sofa.jraft.storage.LogManager;
|
||||||
|
import com.alipay.sofa.jraft.storage.snapshot.SnapshotReader;
|
||||||
|
import com.alipay.sofa.jraft.storage.snapshot.SnapshotWriter;
|
||||||
|
import com.alipay.sofa.jraft.util.DisruptorBuilder;
|
||||||
|
import com.alipay.sofa.jraft.util.DisruptorMetricSet;
|
||||||
|
import com.alipay.sofa.jraft.util.LogExceptionHandler;
|
||||||
|
import com.alipay.sofa.jraft.util.NamedThreadFactory;
|
||||||
|
import com.alipay.sofa.jraft.util.OnlyForTest;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
import com.alipay.sofa.jraft.util.Utils;
|
||||||
|
import com.lmax.disruptor.BlockingWaitStrategy;
|
||||||
|
import com.lmax.disruptor.EventFactory;
|
||||||
|
import com.lmax.disruptor.EventHandler;
|
||||||
|
import com.lmax.disruptor.EventTranslator;
|
||||||
|
import com.lmax.disruptor.RingBuffer;
|
||||||
|
import com.lmax.disruptor.dsl.Disruptor;
|
||||||
|
import com.lmax.disruptor.dsl.ProducerType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The finite state machine caller implementation.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-03 11:12:14 AM
|
||||||
|
*/
|
||||||
|
public class FSMCallerImpl implements FSMCaller {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(FSMCallerImpl.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task type
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-03 11:12:25 AM
|
||||||
|
*/
|
||||||
|
private enum TaskType {
|
||||||
|
IDLE, //
|
||||||
|
COMMITTED, //
|
||||||
|
SNAPSHOT_SAVE, //
|
||||||
|
SNAPSHOT_LOAD, //
|
||||||
|
LEADER_STOP, //
|
||||||
|
LEADER_START, //
|
||||||
|
START_FOLLOWING, //
|
||||||
|
STOP_FOLLOWING, //
|
||||||
|
SHUTDOWN, //
|
||||||
|
FLUSH, //
|
||||||
|
ERROR;
|
||||||
|
|
||||||
|
private String metricName;
|
||||||
|
|
||||||
|
public String metricName() {
|
||||||
|
if (this.metricName == null) {
|
||||||
|
this.metricName = "fsm-" + name().toLowerCase().replaceAll("_", "-");
|
||||||
|
}
|
||||||
|
return this.metricName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply task for disruptor.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-03 11:12:35 AM
|
||||||
|
*/
|
||||||
|
private static class ApplyTask {
|
||||||
|
TaskType type;
|
||||||
|
// union fields
|
||||||
|
long committedIndex;
|
||||||
|
long term;
|
||||||
|
Status status;
|
||||||
|
LeaderChangeContext leaderChangeCtx;
|
||||||
|
Closure done;
|
||||||
|
CountDownLatch shutdownLatch;
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.type = null;
|
||||||
|
this.committedIndex = 0;
|
||||||
|
this.term = 0;
|
||||||
|
this.status = null;
|
||||||
|
this.leaderChangeCtx = null;
|
||||||
|
this.done = null;
|
||||||
|
this.shutdownLatch = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ApplyTaskFactory implements EventFactory<ApplyTask> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApplyTask newInstance() {
|
||||||
|
return new ApplyTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ApplyTaskHandler implements EventHandler<ApplyTask> {
|
||||||
|
// max committed index in current batch, reset to -1 every batch
|
||||||
|
private long maxCommittedIndex = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(final ApplyTask event, final long sequence, final boolean endOfBatch) throws Exception {
|
||||||
|
this.maxCommittedIndex = runApplyTask(event, this.maxCommittedIndex, endOfBatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LogManager logManager;
|
||||||
|
private StateMachine fsm;
|
||||||
|
private ClosureQueue closureQueue;
|
||||||
|
private final AtomicLong lastAppliedIndex;
|
||||||
|
private long lastAppliedTerm;
|
||||||
|
private Closure afterShutdown;
|
||||||
|
private NodeImpl node;
|
||||||
|
private volatile TaskType currTask;
|
||||||
|
private final AtomicLong applyingIndex;
|
||||||
|
private volatile RaftException error;
|
||||||
|
private Disruptor<ApplyTask> disruptor;
|
||||||
|
private RingBuffer<ApplyTask> taskQueue;
|
||||||
|
private volatile CountDownLatch shutdownLatch;
|
||||||
|
private NodeMetrics nodeMetrics;
|
||||||
|
private final CopyOnWriteArrayList<LastAppliedLogIndexListener> lastAppliedLogIndexListeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
public FSMCallerImpl() {
|
||||||
|
super();
|
||||||
|
this.currTask = TaskType.IDLE;
|
||||||
|
this.lastAppliedIndex = new AtomicLong(0);
|
||||||
|
this.applyingIndex = new AtomicLong(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean init(final FSMCallerOptions opts) {
|
||||||
|
this.logManager = opts.getLogManager();
|
||||||
|
this.fsm = opts.getFsm();
|
||||||
|
this.closureQueue = opts.getClosureQueue();
|
||||||
|
this.afterShutdown = opts.getAfterShutdown();
|
||||||
|
this.node = opts.getNode();
|
||||||
|
this.nodeMetrics = this.node.getNodeMetrics();
|
||||||
|
this.lastAppliedIndex.set(opts.getBootstrapId().getIndex());
|
||||||
|
notifyLastAppliedIndexUpdated(this.lastAppliedIndex.get());
|
||||||
|
this.lastAppliedTerm = opts.getBootstrapId().getTerm();
|
||||||
|
this.disruptor = DisruptorBuilder.<ApplyTask> newInstance() //
|
||||||
|
.setEventFactory(new ApplyTaskFactory()) //
|
||||||
|
.setRingBufferSize(opts.getDisruptorBufferSize()) //
|
||||||
|
.setThreadFactory(new NamedThreadFactory("JRaft-FSMCaller-Disruptor-", true)) //
|
||||||
|
.setProducerType(ProducerType.MULTI) //
|
||||||
|
.setWaitStrategy(new BlockingWaitStrategy()) //
|
||||||
|
.build();
|
||||||
|
this.disruptor.handleEventsWith(new ApplyTaskHandler());
|
||||||
|
this.disruptor.setDefaultExceptionHandler(new LogExceptionHandler<Object>(getClass().getSimpleName()));
|
||||||
|
this.taskQueue = this.disruptor.start();
|
||||||
|
if (this.nodeMetrics.getMetricRegistry() != null) {
|
||||||
|
this.nodeMetrics.getMetricRegistry().register("jraft-fsm-caller-disruptor",
|
||||||
|
new DisruptorMetricSet(this.taskQueue));
|
||||||
|
}
|
||||||
|
this.error = new RaftException(EnumOutter.ErrorType.ERROR_TYPE_NONE);
|
||||||
|
LOG.info("Starts FSMCaller successfully.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void shutdown() {
|
||||||
|
if (this.shutdownLatch != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG.info("Shutting down FSMCaller...");
|
||||||
|
|
||||||
|
if (this.taskQueue != null) {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
this.shutdownLatch = latch;
|
||||||
|
Utils.runInThread(() -> this.taskQueue.publishEvent((task, sequence) -> {
|
||||||
|
task.reset();
|
||||||
|
task.type = TaskType.SHUTDOWN;
|
||||||
|
task.shutdownLatch = latch;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
doShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLastAppliedLogIndexListener(final LastAppliedLogIndexListener listener) {
|
||||||
|
this.lastAppliedLogIndexListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean enqueueTask(final EventTranslator<ApplyTask> tpl) {
|
||||||
|
if (this.shutdownLatch != null) {
|
||||||
|
// Shutting down
|
||||||
|
LOG.warn("FSMCaller is stopped, can not apply new task.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.taskQueue.publishEvent(tpl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommitted(final long committedIndex) {
|
||||||
|
return enqueueTask((task, sequence) -> {
|
||||||
|
task.type = TaskType.COMMITTED;
|
||||||
|
task.committedIndex = committedIndex;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush all events in disruptor.
|
||||||
|
*/
|
||||||
|
@OnlyForTest
|
||||||
|
void flush() throws InterruptedException {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
enqueueTask((task, sequence) -> {
|
||||||
|
task.type = TaskType.FLUSH;
|
||||||
|
task.shutdownLatch = latch;
|
||||||
|
});
|
||||||
|
latch.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSnapshotLoad(final LoadSnapshotClosure done) {
|
||||||
|
return enqueueTask((task, sequence) -> {
|
||||||
|
task.type = TaskType.SNAPSHOT_LOAD;
|
||||||
|
task.done = done;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSnapshotSave(final SaveSnapshotClosure done) {
|
||||||
|
return enqueueTask((task, sequence) -> {
|
||||||
|
task.type = TaskType.SNAPSHOT_SAVE;
|
||||||
|
task.done = done;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onLeaderStop(final Status status) {
|
||||||
|
return enqueueTask((task, sequence) -> {
|
||||||
|
task.type = TaskType.LEADER_STOP;
|
||||||
|
task.status = new Status(status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onLeaderStart(final long term) {
|
||||||
|
return enqueueTask((task, sequence) -> {
|
||||||
|
task.type = TaskType.LEADER_START;
|
||||||
|
task.term = term;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onStartFollowing(final LeaderChangeContext ctx) {
|
||||||
|
return enqueueTask((task, sequence) -> {
|
||||||
|
task.type = TaskType.START_FOLLOWING;
|
||||||
|
task.leaderChangeCtx = new LeaderChangeContext(ctx.getLeaderId(), ctx.getTerm(), ctx.getStatus());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onStopFollowing(final LeaderChangeContext ctx) {
|
||||||
|
return enqueueTask((task, sequence) -> {
|
||||||
|
task.type = TaskType.STOP_FOLLOWING;
|
||||||
|
task.leaderChangeCtx = new LeaderChangeContext(ctx.getLeaderId(), ctx.getTerm(), ctx.getStatus());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closure runs with an error.
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 2:20:31 PM
|
||||||
|
*/
|
||||||
|
public class OnErrorClosure implements Closure {
|
||||||
|
private RaftException error;
|
||||||
|
|
||||||
|
public OnErrorClosure(final RaftException error) {
|
||||||
|
super();
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RaftException getError() {
|
||||||
|
return this.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError(final RaftException error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(final Status st) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onError(final RaftException error) {
|
||||||
|
if (!this.error.getStatus().isOk()) {
|
||||||
|
LOG.warn("FSMCaller already in error status, ignore new error.", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final OnErrorClosure c = new OnErrorClosure(error);
|
||||||
|
return enqueueTask((task, sequence) -> {
|
||||||
|
task.type = TaskType.ERROR;
|
||||||
|
task.done = c;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLastAppliedIndex() {
|
||||||
|
return this.lastAppliedIndex.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void join() throws InterruptedException {
|
||||||
|
if (this.shutdownLatch != null) {
|
||||||
|
this.shutdownLatch.await();
|
||||||
|
this.disruptor.shutdown();
|
||||||
|
if (this.afterShutdown != null) {
|
||||||
|
this.afterShutdown.run(Status.OK());
|
||||||
|
this.afterShutdown = null;
|
||||||
|
}
|
||||||
|
this.shutdownLatch = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
private long runApplyTask(final ApplyTask task, long maxCommittedIndex, final boolean endOfBatch) {
|
||||||
|
CountDownLatch shutdown = null;
|
||||||
|
if (task.type == TaskType.COMMITTED) {
|
||||||
|
if (task.committedIndex > maxCommittedIndex) {
|
||||||
|
maxCommittedIndex = task.committedIndex;
|
||||||
|
}
|
||||||
|
task.reset();
|
||||||
|
} else {
|
||||||
|
if (maxCommittedIndex >= 0) {
|
||||||
|
this.currTask = TaskType.COMMITTED;
|
||||||
|
doCommitted(maxCommittedIndex);
|
||||||
|
maxCommittedIndex = -1L; // reset maxCommittedIndex
|
||||||
|
}
|
||||||
|
final long startMs = Utils.monotonicMs();
|
||||||
|
try {
|
||||||
|
switch (task.type) {
|
||||||
|
case COMMITTED:
|
||||||
|
Requires.requireTrue(false, "Impossible");
|
||||||
|
break;
|
||||||
|
case SNAPSHOT_SAVE:
|
||||||
|
this.currTask = TaskType.SNAPSHOT_SAVE;
|
||||||
|
if (passByStatus(task.done)) {
|
||||||
|
doSnapshotSave((SaveSnapshotClosure) task.done);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SNAPSHOT_LOAD:
|
||||||
|
this.currTask = TaskType.SNAPSHOT_LOAD;
|
||||||
|
if (passByStatus(task.done)) {
|
||||||
|
doSnapshotLoad((LoadSnapshotClosure) task.done);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LEADER_STOP:
|
||||||
|
this.currTask = TaskType.LEADER_STOP;
|
||||||
|
doLeaderStop(task.status);
|
||||||
|
break;
|
||||||
|
case LEADER_START:
|
||||||
|
this.currTask = TaskType.LEADER_START;
|
||||||
|
doLeaderStart(task.term);
|
||||||
|
break;
|
||||||
|
case START_FOLLOWING:
|
||||||
|
this.currTask = TaskType.START_FOLLOWING;
|
||||||
|
doStartFollowing(task.leaderChangeCtx);
|
||||||
|
break;
|
||||||
|
case STOP_FOLLOWING:
|
||||||
|
this.currTask = TaskType.STOP_FOLLOWING;
|
||||||
|
doStopFollowing(task.leaderChangeCtx);
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
this.currTask = TaskType.ERROR;
|
||||||
|
doOnError((OnErrorClosure) task.done);
|
||||||
|
break;
|
||||||
|
case IDLE:
|
||||||
|
Requires.requireTrue(false, "Can't reach here");
|
||||||
|
break;
|
||||||
|
case SHUTDOWN:
|
||||||
|
this.currTask = TaskType.SHUTDOWN;
|
||||||
|
shutdown = task.shutdownLatch;
|
||||||
|
break;
|
||||||
|
case FLUSH:
|
||||||
|
this.currTask = TaskType.FLUSH;
|
||||||
|
shutdown = task.shutdownLatch;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.nodeMetrics.recordLatency(task.type.metricName(), Utils.monotonicMs() - startMs);
|
||||||
|
task.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (endOfBatch && maxCommittedIndex >= 0) {
|
||||||
|
this.currTask = TaskType.COMMITTED;
|
||||||
|
doCommitted(maxCommittedIndex);
|
||||||
|
maxCommittedIndex = -1L; // reset maxCommittedIndex
|
||||||
|
}
|
||||||
|
this.currTask = TaskType.IDLE;
|
||||||
|
return maxCommittedIndex;
|
||||||
|
} finally {
|
||||||
|
if (shutdown != null) {
|
||||||
|
shutdown.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doShutdown() {
|
||||||
|
if (this.node != null) {
|
||||||
|
this.node = null;
|
||||||
|
}
|
||||||
|
if (this.fsm != null) {
|
||||||
|
this.fsm.onShutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyLastAppliedIndexUpdated(final long lastAppliedIndex) {
|
||||||
|
for (final LastAppliedLogIndexListener listener : this.lastAppliedLogIndexListeners) {
|
||||||
|
listener.onApplied(lastAppliedIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doCommitted(final long committedIndex) {
|
||||||
|
if (!this.error.getStatus().isOk()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final long lastAppliedIndex = this.lastAppliedIndex.get();
|
||||||
|
// We can tolerate the disorder of committed_index
|
||||||
|
if (lastAppliedIndex >= committedIndex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final long startMs = Utils.monotonicMs();
|
||||||
|
try {
|
||||||
|
final List<Closure> closures = new ArrayList<>();
|
||||||
|
final List<TaskClosure> taskClosures = new ArrayList<>();
|
||||||
|
final long firstClosureIndex = this.closureQueue.popClosureUntil(committedIndex, closures, taskClosures);
|
||||||
|
|
||||||
|
// Calls TaskClosure#onCommitted if necessary
|
||||||
|
onTaskCommitted(taskClosures);
|
||||||
|
|
||||||
|
Requires.requireTrue(firstClosureIndex >= 0, "Invalid firstClosureIndex");
|
||||||
|
final IteratorImpl iterImpl = new IteratorImpl(this.fsm, this.logManager, closures, firstClosureIndex,
|
||||||
|
lastAppliedIndex, committedIndex, this.applyingIndex);
|
||||||
|
while (iterImpl.isGood()) {
|
||||||
|
final LogEntry logEntry = iterImpl.entry();
|
||||||
|
if (logEntry.getType() != EnumOutter.EntryType.ENTRY_TYPE_DATA) {
|
||||||
|
if (logEntry.getType() == EnumOutter.EntryType.ENTRY_TYPE_CONFIGURATION) {
|
||||||
|
if (logEntry.getOldPeers() != null && !logEntry.getOldPeers().isEmpty()) {
|
||||||
|
// Joint stage is not supposed to be noticeable by end users.
|
||||||
|
this.fsm.onConfigurationCommitted(new Configuration(iterImpl.entry().getPeers()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (iterImpl.done() != null) {
|
||||||
|
// For other entries, we have nothing to do besides flush the
|
||||||
|
// pending tasks and run this closure to notify the caller that the
|
||||||
|
// entries before this one were successfully committed and applied.
|
||||||
|
iterImpl.done().run(Status.OK());
|
||||||
|
}
|
||||||
|
iterImpl.next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply data task to user state machine
|
||||||
|
doApplyTasks(iterImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iterImpl.hasError()) {
|
||||||
|
setError(iterImpl.getError());
|
||||||
|
iterImpl.runTheRestClosureWithError();
|
||||||
|
}
|
||||||
|
final long lastIndex = iterImpl.getIndex() - 1;
|
||||||
|
final long lastTerm = this.logManager.getTerm(lastIndex);
|
||||||
|
final LogId lastAppliedId = new LogId(lastIndex, lastTerm);
|
||||||
|
this.lastAppliedIndex.set(lastIndex);
|
||||||
|
this.lastAppliedTerm = lastTerm;
|
||||||
|
this.logManager.setAppliedId(lastAppliedId);
|
||||||
|
notifyLastAppliedIndexUpdated(lastIndex);
|
||||||
|
} finally {
|
||||||
|
this.nodeMetrics.recordLatency("fsm-commit", Utils.monotonicMs() - startMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onTaskCommitted(final List<TaskClosure> closures) {
|
||||||
|
for (int i = 0, size = closures.size(); i < size; i++) {
|
||||||
|
final TaskClosure done = closures.get(i);
|
||||||
|
done.onCommitted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doApplyTasks(final IteratorImpl iterImpl) {
|
||||||
|
final IteratorWrapper iter = new IteratorWrapper(iterImpl);
|
||||||
|
final long startApplyMs = Utils.monotonicMs();
|
||||||
|
final long startIndex = iter.getIndex();
|
||||||
|
try {
|
||||||
|
this.fsm.onApply(iter);
|
||||||
|
} finally {
|
||||||
|
this.nodeMetrics.recordLatency("fsm-apply-tasks", Utils.monotonicMs() - startApplyMs);
|
||||||
|
this.nodeMetrics.recordSize("fsm-apply-tasks-count", iter.getIndex() - startIndex);
|
||||||
|
}
|
||||||
|
if (iter.hasNext()) {
|
||||||
|
LOG.error("Iterator is still valid, did you return before iterator reached the end?");
|
||||||
|
}
|
||||||
|
// Try move to next in case that we pass the same log twice.
|
||||||
|
iter.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doSnapshotSave(final SaveSnapshotClosure done) {
|
||||||
|
Requires.requireNonNull(done, "SaveSnapshotClosure is null");
|
||||||
|
final long lastAppliedIndex = this.lastAppliedIndex.get();
|
||||||
|
final RaftOutter.SnapshotMeta.Builder metaBuilder = RaftOutter.SnapshotMeta.newBuilder() //
|
||||||
|
.setLastIncludedIndex(lastAppliedIndex) //
|
||||||
|
.setLastIncludedTerm(this.lastAppliedTerm);
|
||||||
|
final ConfigurationEntry confEntry = this.logManager.getConfiguration(lastAppliedIndex);
|
||||||
|
if (confEntry == null || confEntry.isEmpty()) {
|
||||||
|
LOG.error("Empty conf entry for lastAppliedIndex={}", lastAppliedIndex);
|
||||||
|
Utils.runClosureInThread(done, new Status(RaftError.EINVAL, "Empty conf entry for lastAppliedIndex=%s",
|
||||||
|
lastAppliedIndex));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (final PeerId peer : confEntry.getConf()) {
|
||||||
|
metaBuilder.addPeers(peer.toString());
|
||||||
|
}
|
||||||
|
for (final PeerId peer : confEntry.getConf().getLearners()) {
|
||||||
|
metaBuilder.addLearners(peer.toString());
|
||||||
|
}
|
||||||
|
if (confEntry.getOldConf() != null) {
|
||||||
|
for (final PeerId peer : confEntry.getOldConf()) {
|
||||||
|
metaBuilder.addOldPeers(peer.toString());
|
||||||
|
}
|
||||||
|
for (final PeerId peer : confEntry.getOldConf().getLearners()) {
|
||||||
|
metaBuilder.addOldLearners(peer.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final SnapshotWriter writer = done.start(metaBuilder.build());
|
||||||
|
if (writer == null) {
|
||||||
|
done.run(new Status(RaftError.EINVAL, "snapshot_storage create SnapshotWriter failed"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.fsm.onSnapshotSave(writer, done);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder sb = new StringBuilder("StateMachine [");
|
||||||
|
switch (this.currTask) {
|
||||||
|
case IDLE:
|
||||||
|
sb.append("Idle");
|
||||||
|
break;
|
||||||
|
case COMMITTED:
|
||||||
|
sb.append("Applying logIndex=").append(this.applyingIndex);
|
||||||
|
break;
|
||||||
|
case SNAPSHOT_SAVE:
|
||||||
|
sb.append("Saving snapshot");
|
||||||
|
break;
|
||||||
|
case SNAPSHOT_LOAD:
|
||||||
|
sb.append("Loading snapshot");
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
sb.append("Notifying error");
|
||||||
|
break;
|
||||||
|
case LEADER_STOP:
|
||||||
|
sb.append("Notifying leader stop");
|
||||||
|
break;
|
||||||
|
case LEADER_START:
|
||||||
|
sb.append("Notifying leader start");
|
||||||
|
break;
|
||||||
|
case START_FOLLOWING:
|
||||||
|
sb.append("Notifying start following");
|
||||||
|
break;
|
||||||
|
case STOP_FOLLOWING:
|
||||||
|
sb.append("Notifying stop following");
|
||||||
|
break;
|
||||||
|
case SHUTDOWN:
|
||||||
|
sb.append("Shutting down");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return sb.append(']').toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doSnapshotLoad(final LoadSnapshotClosure done) {
|
||||||
|
Requires.requireNonNull(done, "LoadSnapshotClosure is null");
|
||||||
|
final SnapshotReader reader = done.start();
|
||||||
|
if (reader == null) {
|
||||||
|
done.run(new Status(RaftError.EINVAL, "open SnapshotReader failed"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final RaftOutter.SnapshotMeta meta = reader.load();
|
||||||
|
if (meta == null) {
|
||||||
|
done.run(new Status(RaftError.EINVAL, "SnapshotReader load meta failed"));
|
||||||
|
if (reader.getRaftError() == RaftError.EIO) {
|
||||||
|
final RaftException err = new RaftException(EnumOutter.ErrorType.ERROR_TYPE_SNAPSHOT, RaftError.EIO,
|
||||||
|
"Fail to load snapshot meta");
|
||||||
|
setError(err);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final LogId lastAppliedId = new LogId(this.lastAppliedIndex.get(), this.lastAppliedTerm);
|
||||||
|
final LogId snapshotId = new LogId(meta.getLastIncludedIndex(), meta.getLastIncludedTerm());
|
||||||
|
if (lastAppliedId.compareTo(snapshotId) > 0) {
|
||||||
|
done.run(new Status(
|
||||||
|
RaftError.ESTALE,
|
||||||
|
"Loading a stale snapshot last_applied_index=%d last_applied_term=%d snapshot_index=%d snapshot_term=%d",
|
||||||
|
lastAppliedId.getIndex(), lastAppliedId.getTerm(), snapshotId.getIndex(), snapshotId.getTerm()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.fsm.onSnapshotLoad(reader)) {
|
||||||
|
done.run(new Status(-1, "StateMachine onSnapshotLoad failed"));
|
||||||
|
final RaftException e = new RaftException(EnumOutter.ErrorType.ERROR_TYPE_STATE_MACHINE,
|
||||||
|
RaftError.ESTATEMACHINE, "StateMachine onSnapshotLoad failed");
|
||||||
|
setError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (meta.getOldPeersCount() == 0) {
|
||||||
|
// Joint stage is not supposed to be noticeable by end users.
|
||||||
|
final Configuration conf = new Configuration();
|
||||||
|
for (int i = 0, size = meta.getPeersCount(); i < size; i++) {
|
||||||
|
final PeerId peer = new PeerId();
|
||||||
|
Requires.requireTrue(peer.parse(meta.getPeers(i)), "Parse peer failed");
|
||||||
|
conf.addPeer(peer);
|
||||||
|
}
|
||||||
|
this.fsm.onConfigurationCommitted(conf);
|
||||||
|
}
|
||||||
|
this.lastAppliedIndex.set(meta.getLastIncludedIndex());
|
||||||
|
this.lastAppliedTerm = meta.getLastIncludedTerm();
|
||||||
|
done.run(Status.OK());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doOnError(final OnErrorClosure done) {
|
||||||
|
setError(done.getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doLeaderStop(final Status status) {
|
||||||
|
this.fsm.onLeaderStop(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doLeaderStart(final long term) {
|
||||||
|
this.fsm.onLeaderStart(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doStartFollowing(final LeaderChangeContext ctx) {
|
||||||
|
this.fsm.onStartFollowing(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doStopFollowing(final LeaderChangeContext ctx) {
|
||||||
|
this.fsm.onStopFollowing(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setError(final RaftException e) {
|
||||||
|
if (this.error.getType() != EnumOutter.ErrorType.ERROR_TYPE_NONE) {
|
||||||
|
// already report
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.error = e;
|
||||||
|
if (this.fsm != null) {
|
||||||
|
this.fsm.onError(e);
|
||||||
|
}
|
||||||
|
if (this.node != null) {
|
||||||
|
this.node.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyForTest
|
||||||
|
RaftException getError() {
|
||||||
|
return this.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean passByStatus(final Closure done) {
|
||||||
|
final Status status = this.error.getStatus();
|
||||||
|
if (!status.isOk()) {
|
||||||
|
if (done != null) {
|
||||||
|
done.run(new Status(RaftError.EINVAL, "FSMCaller is in bad status=`%s`", status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describe(final Printer out) {
|
||||||
|
out.print(" ") //
|
||||||
|
.println(toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.StateMachine;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
import com.alipay.sofa.jraft.entity.EnumOutter;
|
||||||
|
import com.alipay.sofa.jraft.entity.LogEntry;
|
||||||
|
import com.alipay.sofa.jraft.error.LogEntryCorruptedException;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftError;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftException;
|
||||||
|
import com.alipay.sofa.jraft.storage.LogManager;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
import com.alipay.sofa.jraft.util.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The iterator implementation.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-03 3:28:37 PM
|
||||||
|
*/
|
||||||
|
public class IteratorImpl {
|
||||||
|
|
||||||
|
private final StateMachine fsm;
|
||||||
|
private final LogManager logManager;
|
||||||
|
private final List<Closure> closures;
|
||||||
|
private final long firstClosureIndex;
|
||||||
|
private long currentIndex;
|
||||||
|
private final long committedIndex;
|
||||||
|
private LogEntry currEntry = new LogEntry(); // blank entry
|
||||||
|
private final AtomicLong applyingIndex;
|
||||||
|
private RaftException error;
|
||||||
|
|
||||||
|
public IteratorImpl(final StateMachine fsm, final LogManager logManager, final List<Closure> closures,
|
||||||
|
final long firstClosureIndex, final long lastAppliedIndex, final long committedIndex,
|
||||||
|
final AtomicLong applyingIndex) {
|
||||||
|
super();
|
||||||
|
this.fsm = fsm;
|
||||||
|
this.logManager = logManager;
|
||||||
|
this.closures = closures;
|
||||||
|
this.firstClosureIndex = firstClosureIndex;
|
||||||
|
this.currentIndex = lastAppliedIndex;
|
||||||
|
this.committedIndex = committedIndex;
|
||||||
|
this.applyingIndex = applyingIndex;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "IteratorImpl [fsm=" + this.fsm + ", logManager=" + this.logManager + ", closures=" + this.closures
|
||||||
|
+ ", firstClosureIndex=" + this.firstClosureIndex + ", currentIndex=" + this.currentIndex
|
||||||
|
+ ", committedIndex=" + this.committedIndex + ", currEntry=" + this.currEntry + ", applyingIndex="
|
||||||
|
+ this.applyingIndex + ", error=" + this.error + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogEntry entry() {
|
||||||
|
return this.currEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RaftException getError() {
|
||||||
|
return this.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isGood() {
|
||||||
|
return this.currentIndex <= this.committedIndex && !hasError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasError() {
|
||||||
|
return this.error != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move to next
|
||||||
|
*/
|
||||||
|
public void next() {
|
||||||
|
this.currEntry = null; //release current entry
|
||||||
|
//get next entry
|
||||||
|
if (this.currentIndex <= this.committedIndex) {
|
||||||
|
++this.currentIndex;
|
||||||
|
if (this.currentIndex <= this.committedIndex) {
|
||||||
|
try {
|
||||||
|
this.currEntry = this.logManager.getEntry(this.currentIndex);
|
||||||
|
if (this.currEntry == null) {
|
||||||
|
getOrCreateError().setType(EnumOutter.ErrorType.ERROR_TYPE_LOG);
|
||||||
|
getOrCreateError().getStatus().setError(-1,
|
||||||
|
"Fail to get entry at index=%d while committed_index=%d", this.currentIndex,
|
||||||
|
this.committedIndex);
|
||||||
|
}
|
||||||
|
} catch (final LogEntryCorruptedException e) {
|
||||||
|
getOrCreateError().setType(EnumOutter.ErrorType.ERROR_TYPE_LOG);
|
||||||
|
getOrCreateError().getStatus().setError(RaftError.EINVAL, e.getMessage());
|
||||||
|
}
|
||||||
|
this.applyingIndex.set(this.currentIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getIndex() {
|
||||||
|
return this.currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Closure done() {
|
||||||
|
if (this.currentIndex < this.firstClosureIndex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.closures.get((int) (this.currentIndex - this.firstClosureIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void runTheRestClosureWithError() {
|
||||||
|
for (long i = Math.max(this.currentIndex, this.firstClosureIndex); i <= this.committedIndex; i++) {
|
||||||
|
final Closure done = this.closures.get((int) (i - this.firstClosureIndex));
|
||||||
|
if (done != null) {
|
||||||
|
Requires.requireNonNull(this.error, "error");
|
||||||
|
Requires.requireNonNull(this.error.getStatus(), "error.status");
|
||||||
|
final Status status = this.error.getStatus();
|
||||||
|
Utils.runClosureInThread(done, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorAndRollback(final long ntail, final Status st) {
|
||||||
|
Requires.requireTrue(ntail > 0, "Invalid ntail=" + ntail);
|
||||||
|
if (this.currEntry == null || this.currEntry.getType() != EnumOutter.EntryType.ENTRY_TYPE_DATA) {
|
||||||
|
this.currentIndex -= ntail;
|
||||||
|
} else {
|
||||||
|
this.currentIndex -= ntail - 1;
|
||||||
|
}
|
||||||
|
this.currEntry = null;
|
||||||
|
getOrCreateError().setType(EnumOutter.ErrorType.ERROR_TYPE_STATE_MACHINE);
|
||||||
|
getOrCreateError().getStatus().setError(RaftError.ESTATEMACHINE,
|
||||||
|
"StateMachine meet critical error when applying one or more tasks since index=%d, %s", this.currentIndex,
|
||||||
|
st != null ? st.toString() : "none");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private RaftException getOrCreateError() {
|
||||||
|
if (this.error == null) {
|
||||||
|
this.error = new RaftException();
|
||||||
|
}
|
||||||
|
return this.error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.Iterator;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
import com.alipay.sofa.jraft.entity.EnumOutter;
|
||||||
|
import com.alipay.sofa.jraft.entity.LogEntry;
|
||||||
|
|
||||||
|
public class IteratorWrapper implements Iterator {
|
||||||
|
|
||||||
|
private final IteratorImpl impl;
|
||||||
|
|
||||||
|
public IteratorWrapper(IteratorImpl iterImpl) {
|
||||||
|
super();
|
||||||
|
this.impl = iterImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return this.impl.isGood() && this.impl.entry().getType() == EnumOutter.EntryType.ENTRY_TYPE_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer next() {
|
||||||
|
final ByteBuffer data = getData();
|
||||||
|
if (hasNext()) {
|
||||||
|
this.impl.next();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer getData() {
|
||||||
|
final LogEntry entry = this.impl.entry();
|
||||||
|
return entry != null ? entry.getData() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getIndex() {
|
||||||
|
return this.impl.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTerm() {
|
||||||
|
return this.impl.entry().getId().getTerm();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Closure done() {
|
||||||
|
return this.impl.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setErrorAndRollback(final long ntail, final Status st) {
|
||||||
|
this.impl.setErrorAndRollback(ntail, st);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.codahale.metrics.Metric;
|
||||||
|
import com.codahale.metrics.MetricRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node metrics
|
||||||
|
*
|
||||||
|
* @author dennis
|
||||||
|
*/
|
||||||
|
public class NodeMetrics {
|
||||||
|
|
||||||
|
private final MetricRegistry metrics;
|
||||||
|
|
||||||
|
public NodeMetrics(final boolean enableMetrics) {
|
||||||
|
if (enableMetrics) {
|
||||||
|
this.metrics = new MetricRegistry();
|
||||||
|
} else {
|
||||||
|
this.metrics = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the metrics map, returns empty map if it is disabled.
|
||||||
|
*
|
||||||
|
* @return metrics map
|
||||||
|
*/
|
||||||
|
public Map<String, Metric> getMetrics() {
|
||||||
|
if (this.metrics != null) {
|
||||||
|
return this.metrics.getMetrics();
|
||||||
|
}
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the metrics registry, return null if is is disabled.
|
||||||
|
*
|
||||||
|
* @return metrics registry
|
||||||
|
*/
|
||||||
|
public MetricRegistry getMetricRegistry() {
|
||||||
|
return this.metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether metric is enabled.
|
||||||
|
*
|
||||||
|
* @return true if metric is enabled
|
||||||
|
*/
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return this.metrics != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records operation times.
|
||||||
|
* @param key key of operation
|
||||||
|
* @param times times of operation
|
||||||
|
*/
|
||||||
|
public void recordTimes(final String key, final long times) {
|
||||||
|
if (this.metrics != null) {
|
||||||
|
this.metrics.counter(key).inc(times);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records operation batch size.
|
||||||
|
*
|
||||||
|
* @param key key of operation
|
||||||
|
* @param size size of operation
|
||||||
|
*/
|
||||||
|
public void recordSize(final String key, final long size) {
|
||||||
|
if (this.metrics != null) {
|
||||||
|
this.metrics.histogram(key).update(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records operation latency.
|
||||||
|
*
|
||||||
|
* @param key key of operation
|
||||||
|
* @param duration duration of operation
|
||||||
|
*/
|
||||||
|
public void recordLatency(final String key, final long duration) {
|
||||||
|
if (this.metrics != null) {
|
||||||
|
this.metrics.timer(key).update(duration, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,451 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.FSMCaller;
|
||||||
|
import com.alipay.sofa.jraft.FSMCaller.LastAppliedLogIndexListener;
|
||||||
|
import com.alipay.sofa.jraft.ReadOnlyService;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
import com.alipay.sofa.jraft.closure.ReadIndexClosure;
|
||||||
|
import com.alipay.sofa.jraft.entity.ReadIndexState;
|
||||||
|
import com.alipay.sofa.jraft.entity.ReadIndexStatus;
|
||||||
|
import com.alipay.sofa.jraft.error.OverloadException;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftError;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftException;
|
||||||
|
import com.alipay.sofa.jraft.option.RaftOptions;
|
||||||
|
import com.alipay.sofa.jraft.option.ReadOnlyServiceOptions;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RpcRequests.ReadIndexRequest;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RpcRequests.ReadIndexResponse;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RpcResponseClosureAdapter;
|
||||||
|
import com.alipay.sofa.jraft.util.Bytes;
|
||||||
|
import com.alipay.sofa.jraft.util.DisruptorBuilder;
|
||||||
|
import com.alipay.sofa.jraft.util.DisruptorMetricSet;
|
||||||
|
import com.alipay.sofa.jraft.util.LogExceptionHandler;
|
||||||
|
import com.alipay.sofa.jraft.util.NamedThreadFactory;
|
||||||
|
import com.alipay.sofa.jraft.util.OnlyForTest;
|
||||||
|
import com.alipay.sofa.jraft.util.Utils;
|
||||||
|
import com.google.protobuf.ZeroByteStringHelper;
|
||||||
|
import com.lmax.disruptor.BlockingWaitStrategy;
|
||||||
|
import com.lmax.disruptor.EventFactory;
|
||||||
|
import com.lmax.disruptor.EventHandler;
|
||||||
|
import com.lmax.disruptor.EventTranslator;
|
||||||
|
import com.lmax.disruptor.RingBuffer;
|
||||||
|
import com.lmax.disruptor.dsl.Disruptor;
|
||||||
|
import com.lmax.disruptor.dsl.ProducerType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read-only service implementation.
|
||||||
|
*
|
||||||
|
* @author dennis
|
||||||
|
*/
|
||||||
|
public class ReadOnlyServiceImpl implements ReadOnlyService, LastAppliedLogIndexListener {
|
||||||
|
|
||||||
|
/** Disruptor to run readonly service. */
|
||||||
|
private Disruptor<ReadIndexEvent> readIndexDisruptor;
|
||||||
|
private RingBuffer<ReadIndexEvent> readIndexQueue;
|
||||||
|
private RaftOptions raftOptions;
|
||||||
|
private NodeImpl node;
|
||||||
|
private final Lock lock = new ReentrantLock();
|
||||||
|
private FSMCaller fsmCaller;
|
||||||
|
private volatile CountDownLatch shutdownLatch;
|
||||||
|
|
||||||
|
private ScheduledExecutorService scheduledExecutorService;
|
||||||
|
|
||||||
|
private NodeMetrics nodeMetrics;
|
||||||
|
|
||||||
|
private volatile RaftException error;
|
||||||
|
|
||||||
|
// <logIndex, statusList>
|
||||||
|
private final TreeMap<Long, List<ReadIndexStatus>> pendingNotifyStatus = new TreeMap<>();
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory
|
||||||
|
.getLogger(ReadOnlyServiceImpl.class);
|
||||||
|
|
||||||
|
private static class ReadIndexEvent {
|
||||||
|
Bytes requestContext;
|
||||||
|
ReadIndexClosure done;
|
||||||
|
CountDownLatch shutdownLatch;
|
||||||
|
long startTime;
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
this.requestContext = null;
|
||||||
|
this.done = null;
|
||||||
|
this.shutdownLatch = null;
|
||||||
|
this.startTime = 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ReadIndexEventFactory implements EventFactory<ReadIndexEvent> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadIndexEvent newInstance() {
|
||||||
|
return new ReadIndexEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ReadIndexEventHandler implements EventHandler<ReadIndexEvent> {
|
||||||
|
// task list for batch
|
||||||
|
private final List<ReadIndexEvent> events = new ArrayList<>(
|
||||||
|
ReadOnlyServiceImpl.this.raftOptions.getApplyBatch());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(final ReadIndexEvent newEvent, final long sequence, final boolean endOfBatch)
|
||||||
|
throws Exception {
|
||||||
|
if (newEvent.shutdownLatch != null) {
|
||||||
|
executeReadIndexEvents(this.events);
|
||||||
|
reset();
|
||||||
|
newEvent.shutdownLatch.countDown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.events.add(newEvent);
|
||||||
|
if (this.events.size() >= ReadOnlyServiceImpl.this.raftOptions.getApplyBatch() || endOfBatch) {
|
||||||
|
executeReadIndexEvents(this.events);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
for (final ReadIndexEvent event : this.events) {
|
||||||
|
event.reset();
|
||||||
|
}
|
||||||
|
this.events.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReadIndexResponse process closure
|
||||||
|
*
|
||||||
|
* @author dennis
|
||||||
|
*/
|
||||||
|
class ReadIndexResponseClosure extends RpcResponseClosureAdapter<ReadIndexResponse> {
|
||||||
|
|
||||||
|
final List<ReadIndexState> states;
|
||||||
|
final ReadIndexRequest request;
|
||||||
|
|
||||||
|
public ReadIndexResponseClosure(final List<ReadIndexState> states, final ReadIndexRequest request) {
|
||||||
|
super();
|
||||||
|
this.states = states;
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when ReadIndex response returns.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run(final Status status) {
|
||||||
|
if (!status.isOk()) {
|
||||||
|
notifyFail(status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final ReadIndexResponse readIndexResponse = getResponse();
|
||||||
|
if (!readIndexResponse.getSuccess()) {
|
||||||
|
notifyFail(new Status(-1, "Fail to run ReadIndex task, maybe the leader stepped down."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Success
|
||||||
|
final ReadIndexStatus readIndexStatus = new ReadIndexStatus(this.states, this.request,
|
||||||
|
readIndexResponse.getIndex());
|
||||||
|
for (final ReadIndexState state : this.states) {
|
||||||
|
// Records current commit log index.
|
||||||
|
state.setIndex(readIndexResponse.getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean doUnlock = true;
|
||||||
|
ReadOnlyServiceImpl.this.lock.lock();
|
||||||
|
try {
|
||||||
|
if (readIndexStatus.isApplied(ReadOnlyServiceImpl.this.fsmCaller.getLastAppliedIndex())) {
|
||||||
|
// Already applied, notify readIndex request.
|
||||||
|
ReadOnlyServiceImpl.this.lock.unlock();
|
||||||
|
doUnlock = false;
|
||||||
|
notifySuccess(readIndexStatus);
|
||||||
|
} else {
|
||||||
|
if (readIndexStatus.isOverMaxReadIndexLag(ReadOnlyServiceImpl.this.fsmCaller.getLastAppliedIndex(), ReadOnlyServiceImpl.this.raftOptions.getMaxReadIndexLag())) {
|
||||||
|
ReadOnlyServiceImpl.this.lock.unlock();
|
||||||
|
doUnlock = false;
|
||||||
|
notifyFail(new Status(-1, "Fail to run ReadIndex task, the gap of current node's apply index between leader's commit index over maxReadIndexLag"));
|
||||||
|
} else {
|
||||||
|
// Not applied, add it to pending-notify cache.
|
||||||
|
ReadOnlyServiceImpl.this.pendingNotifyStatus
|
||||||
|
.computeIfAbsent(readIndexStatus.getIndex(), k -> new ArrayList<>(10)) //
|
||||||
|
.add(readIndexStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (doUnlock) {
|
||||||
|
ReadOnlyServiceImpl.this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyFail(final Status status) {
|
||||||
|
final long nowMs = Utils.monotonicMs();
|
||||||
|
for (final ReadIndexState state : this.states) {
|
||||||
|
ReadOnlyServiceImpl.this.nodeMetrics.recordLatency("read-index", nowMs - state.getStartTimeMs());
|
||||||
|
final ReadIndexClosure done = state.getDone();
|
||||||
|
if (done != null) {
|
||||||
|
final Bytes reqCtx = state.getRequestContext();
|
||||||
|
done.run(status, ReadIndexClosure.INVALID_LOG_INDEX, reqCtx != null ? reqCtx.get() : null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeReadIndexEvents(final List<ReadIndexEvent> events) {
|
||||||
|
if (events.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final ReadIndexRequest.Builder rb = ReadIndexRequest.newBuilder() //
|
||||||
|
.setGroupId(this.node.getGroupId()) //
|
||||||
|
.setServerId(this.node.getServerId().toString());
|
||||||
|
|
||||||
|
final List<ReadIndexState> states = new ArrayList<>(events.size());
|
||||||
|
|
||||||
|
for (final ReadIndexEvent event : events) {
|
||||||
|
rb.addEntries(ZeroByteStringHelper.wrap(event.requestContext.get()));
|
||||||
|
states.add(new ReadIndexState(event.requestContext, event.done, event.startTime));
|
||||||
|
}
|
||||||
|
final ReadIndexRequest request = rb.build();
|
||||||
|
|
||||||
|
this.node.handleReadIndexRequest(request, new ReadIndexResponseClosure(states, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetPendingStatusError(final Status st) {
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
final Iterator<List<ReadIndexStatus>> it = this.pendingNotifyStatus.values().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
final List<ReadIndexStatus> statuses = it.next();
|
||||||
|
for (final ReadIndexStatus status : statuses) {
|
||||||
|
reportError(status, st);
|
||||||
|
}
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean init(final ReadOnlyServiceOptions opts) {
|
||||||
|
this.node = opts.getNode();
|
||||||
|
this.nodeMetrics = this.node.getNodeMetrics();
|
||||||
|
this.fsmCaller = opts.getFsmCaller();
|
||||||
|
this.raftOptions = opts.getRaftOptions();
|
||||||
|
|
||||||
|
this.scheduledExecutorService = Executors
|
||||||
|
.newSingleThreadScheduledExecutor(new NamedThreadFactory("ReadOnlyService-PendingNotify-Scanner", true));
|
||||||
|
this.readIndexDisruptor = DisruptorBuilder.<ReadIndexEvent> newInstance() //
|
||||||
|
.setEventFactory(new ReadIndexEventFactory()) //
|
||||||
|
.setRingBufferSize(this.raftOptions.getDisruptorBufferSize()) //
|
||||||
|
.setThreadFactory(new NamedThreadFactory("JRaft-ReadOnlyService-Disruptor-", true)) //
|
||||||
|
.setWaitStrategy(new BlockingWaitStrategy()) //
|
||||||
|
.setProducerType(ProducerType.MULTI) //
|
||||||
|
.build();
|
||||||
|
this.readIndexDisruptor.handleEventsWith(new ReadIndexEventHandler());
|
||||||
|
this.readIndexDisruptor
|
||||||
|
.setDefaultExceptionHandler(new LogExceptionHandler<Object>(getClass().getSimpleName()));
|
||||||
|
this.readIndexQueue = this.readIndexDisruptor.start();
|
||||||
|
if (this.nodeMetrics.getMetricRegistry() != null) {
|
||||||
|
this.nodeMetrics.getMetricRegistry() //
|
||||||
|
.register("jraft-read-only-service-disruptor", new DisruptorMetricSet(this.readIndexQueue));
|
||||||
|
}
|
||||||
|
// listen on lastAppliedLogIndex change events.
|
||||||
|
this.fsmCaller.addLastAppliedLogIndexListener(this);
|
||||||
|
|
||||||
|
// start scanner
|
||||||
|
this.scheduledExecutorService.scheduleAtFixedRate(() -> onApplied(this.fsmCaller.getLastAppliedIndex()),
|
||||||
|
this.raftOptions.getMaxElectionDelayMs(), this.raftOptions.getMaxElectionDelayMs(), TimeUnit.MILLISECONDS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void setError(final RaftException error) {
|
||||||
|
if (this.error == null) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void shutdown() {
|
||||||
|
if (this.shutdownLatch != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.shutdownLatch = new CountDownLatch(1);
|
||||||
|
Utils.runInThread( //
|
||||||
|
() -> this.readIndexQueue.publishEvent((event, sequence) -> event.shutdownLatch = this.shutdownLatch));
|
||||||
|
this.scheduledExecutorService.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void join() throws InterruptedException {
|
||||||
|
if (this.shutdownLatch != null) {
|
||||||
|
this.shutdownLatch.await();
|
||||||
|
}
|
||||||
|
this.readIndexDisruptor.shutdown();
|
||||||
|
resetPendingStatusError(new Status(RaftError.ESTOP, "Node is quit."));
|
||||||
|
this.scheduledExecutorService.awaitTermination(5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addRequest(final byte[] reqCtx, final ReadIndexClosure closure) {
|
||||||
|
if (this.shutdownLatch != null) {
|
||||||
|
Utils.runClosureInThread(closure, new Status(RaftError.EHOSTDOWN, "Was stopped"));
|
||||||
|
throw new IllegalStateException("Service already shutdown.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
EventTranslator<ReadIndexEvent> translator = (event, sequence) -> {
|
||||||
|
event.done = closure;
|
||||||
|
event.requestContext = new Bytes(reqCtx);
|
||||||
|
event.startTime = Utils.monotonicMs();
|
||||||
|
};
|
||||||
|
|
||||||
|
switch(this.node.getOptions().getApplyTaskMode()) {
|
||||||
|
case Blocking:
|
||||||
|
this.readIndexQueue.publishEvent(translator);
|
||||||
|
break;
|
||||||
|
case NonBlocking:
|
||||||
|
default:
|
||||||
|
if (!this.readIndexQueue.tryPublishEvent(translator)) {
|
||||||
|
final String errorMsg = "Node is busy, has too many read-index requests, queue is full and bufferSize="+ this.readIndexQueue.getBufferSize();
|
||||||
|
Utils.runClosureInThread(closure,
|
||||||
|
new Status(RaftError.EBUSY, errorMsg));
|
||||||
|
this.nodeMetrics.recordTimes("read-index-overload-times", 1);
|
||||||
|
LOG.warn("Node {} ReadOnlyServiceImpl readIndexQueue is overload.", this.node.getNodeId());
|
||||||
|
if(closure == null) {
|
||||||
|
throw new OverloadException(errorMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
Utils.runClosureInThread(closure, new Status(RaftError.EPERM, "Node is down."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when lastAppliedIndex updates.
|
||||||
|
*
|
||||||
|
* @param appliedIndex applied index
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onApplied(final long appliedIndex) {
|
||||||
|
// TODO reuse pendingStatuses list?
|
||||||
|
List<ReadIndexStatus> pendingStatuses = null;
|
||||||
|
this.lock.lock();
|
||||||
|
try {
|
||||||
|
if (this.pendingNotifyStatus.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Find all statuses that log index less than or equal to appliedIndex.
|
||||||
|
final Map<Long, List<ReadIndexStatus>> statuses = this.pendingNotifyStatus.headMap(appliedIndex, true);
|
||||||
|
if (statuses != null) {
|
||||||
|
pendingStatuses = new ArrayList<>(statuses.size() << 1);
|
||||||
|
|
||||||
|
final Iterator<Map.Entry<Long, List<ReadIndexStatus>>> it = statuses.entrySet().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
final Map.Entry<Long, List<ReadIndexStatus>> entry = it.next();
|
||||||
|
pendingStatuses.addAll(entry.getValue());
|
||||||
|
// Remove the entry from statuses, it will also be removed in pendingNotifyStatus.
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remaining pending statuses are notified by error if it is presented.
|
||||||
|
* When the node is in error state, consider following situations:
|
||||||
|
* 1. If commitIndex > appliedIndex, then all pending statuses should be notified by error status.
|
||||||
|
* 2. When commitIndex == appliedIndex, there will be no more pending statuses.
|
||||||
|
*/
|
||||||
|
if (this.error != null) {
|
||||||
|
resetPendingStatusError(this.error.getStatus());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.lock.unlock();
|
||||||
|
if (pendingStatuses != null && !pendingStatuses.isEmpty()) {
|
||||||
|
for (final ReadIndexStatus status : pendingStatuses) {
|
||||||
|
notifySuccess(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush all events in disruptor.
|
||||||
|
*/
|
||||||
|
@OnlyForTest
|
||||||
|
void flush() throws InterruptedException {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
this.readIndexQueue.publishEvent((task, sequence) -> task.shutdownLatch = latch);
|
||||||
|
latch.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyForTest
|
||||||
|
TreeMap<Long, List<ReadIndexStatus>> getPendingNotifyStatus() {
|
||||||
|
return this.pendingNotifyStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyForTest
|
||||||
|
RaftOptions getRaftOptions() {
|
||||||
|
return this.raftOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reportError(final ReadIndexStatus status, final Status st) {
|
||||||
|
final long nowMs = Utils.monotonicMs();
|
||||||
|
final List<ReadIndexState> states = status.getStates();
|
||||||
|
final int taskCount = states.size();
|
||||||
|
for (int i = 0; i < taskCount; i++) {
|
||||||
|
final ReadIndexState task = states.get(i);
|
||||||
|
final ReadIndexClosure done = task.getDone();
|
||||||
|
if (done != null) {
|
||||||
|
this.nodeMetrics.recordLatency("read-index", nowMs - task.getStartTimeMs());
|
||||||
|
done.run(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifySuccess(final ReadIndexStatus status) {
|
||||||
|
final long nowMs = Utils.monotonicMs();
|
||||||
|
final List<ReadIndexState> states = status.getStates();
|
||||||
|
final int taskCount = states.size();
|
||||||
|
for (int i = 0; i < taskCount; i++) {
|
||||||
|
final ReadIndexState task = states.get(i);
|
||||||
|
final ReadIndexClosure done = task.getDone(); // stack copy
|
||||||
|
if (done != null) {
|
||||||
|
this.nodeMetrics.recordLatency("read-index", nowMs - task.getStartTimeMs());
|
||||||
|
done.setResult(task.getIndex(), task.getRequestContext().get());
|
||||||
|
done.run(Status.OK());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,312 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.ReplicatorGroup;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
import com.alipay.sofa.jraft.closure.CatchUpClosure;
|
||||||
|
import com.alipay.sofa.jraft.conf.ConfigurationEntry;
|
||||||
|
import com.alipay.sofa.jraft.entity.NodeId;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftError;
|
||||||
|
import com.alipay.sofa.jraft.option.RaftOptions;
|
||||||
|
import com.alipay.sofa.jraft.option.ReplicatorGroupOptions;
|
||||||
|
import com.alipay.sofa.jraft.option.ReplicatorOptions;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RaftClientService;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RpcRequests.AppendEntriesResponse;
|
||||||
|
import com.alipay.sofa.jraft.rpc.RpcResponseClosure;
|
||||||
|
import com.alipay.sofa.jraft.util.OnlyForTest;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
import com.alipay.sofa.jraft.util.ThreadId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replicator group for a raft group.
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 1:54:51 PM
|
||||||
|
*/
|
||||||
|
public class ReplicatorGroupImpl implements ReplicatorGroup {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory
|
||||||
|
.getLogger(ReplicatorGroupImpl.class);
|
||||||
|
|
||||||
|
// <peerId, replicatorId>
|
||||||
|
private final ConcurrentMap<PeerId, ThreadId> replicatorMap = new ConcurrentHashMap<>();
|
||||||
|
/** common replicator options */
|
||||||
|
private ReplicatorOptions commonOptions;
|
||||||
|
private int dynamicTimeoutMs = -1;
|
||||||
|
private int electionTimeoutMs = -1;
|
||||||
|
private RaftOptions raftOptions;
|
||||||
|
private final Map<PeerId, ReplicatorType> failureReplicators = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean init(final NodeId nodeId, final ReplicatorGroupOptions opts) {
|
||||||
|
this.dynamicTimeoutMs = opts.getHeartbeatTimeoutMs();
|
||||||
|
this.electionTimeoutMs = opts.getElectionTimeoutMs();
|
||||||
|
this.raftOptions = opts.getRaftOptions();
|
||||||
|
this.commonOptions = new ReplicatorOptions();
|
||||||
|
this.commonOptions.setDynamicHeartBeatTimeoutMs(this.dynamicTimeoutMs);
|
||||||
|
this.commonOptions.setElectionTimeoutMs(this.electionTimeoutMs);
|
||||||
|
this.commonOptions.setRaftRpcService(opts.getRaftRpcClientService());
|
||||||
|
this.commonOptions.setLogManager(opts.getLogManager());
|
||||||
|
this.commonOptions.setBallotBox(opts.getBallotBox());
|
||||||
|
this.commonOptions.setNode(opts.getNode());
|
||||||
|
this.commonOptions.setTerm(0);
|
||||||
|
this.commonOptions.setGroupId(nodeId.getGroupId());
|
||||||
|
this.commonOptions.setServerId(nodeId.getPeerId());
|
||||||
|
this.commonOptions.setSnapshotStorage(opts.getSnapshotStorage());
|
||||||
|
this.commonOptions.setTimerManager(opts.getTimerManager());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyForTest
|
||||||
|
ConcurrentMap<PeerId, ThreadId> getReplicatorMap() {
|
||||||
|
return this.replicatorMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyForTest
|
||||||
|
Map<PeerId, ReplicatorType> getFailureReplicators() {
|
||||||
|
return this.failureReplicators;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendHeartbeat(final PeerId peer, final RpcResponseClosure<AppendEntriesResponse> closure) {
|
||||||
|
final ThreadId rid = this.replicatorMap.get(peer);
|
||||||
|
if (rid == null) {
|
||||||
|
if (closure != null) {
|
||||||
|
closure.run(new Status(RaftError.EHOSTDOWN, "Peer %s is not connected", peer));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Replicator.sendHeartbeat(rid, closure);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ThreadId getReplicator(final PeerId peer) {
|
||||||
|
return this.replicatorMap.get(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addReplicator(final PeerId peer, final ReplicatorType replicatorType, final boolean sync) {
|
||||||
|
Requires.requireTrue(this.commonOptions.getTerm() != 0);
|
||||||
|
this.failureReplicators.remove(peer);
|
||||||
|
if (this.replicatorMap.containsKey(peer)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
final ReplicatorOptions opts = this.commonOptions == null ? new ReplicatorOptions() : this.commonOptions.copy();
|
||||||
|
opts.setReplicatorType(replicatorType);
|
||||||
|
opts.setPeerId(peer);
|
||||||
|
if (!sync) {
|
||||||
|
final RaftClientService client = opts.getRaftRpcService();
|
||||||
|
if (client != null && !client.checkConnection(peer.getEndpoint(), true)) {
|
||||||
|
LOG.error("Fail to check replicator connection to peer={}, replicatorType={}.", peer, replicatorType);
|
||||||
|
this.failureReplicators.put(peer, replicatorType);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final ThreadId rid = Replicator.start(opts, this.raftOptions);
|
||||||
|
if (rid == null) {
|
||||||
|
LOG.error("Fail to start replicator to peer={}, replicatorType={}.", peer, replicatorType);
|
||||||
|
this.failureReplicators.put(peer, replicatorType);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.replicatorMap.put(peer, rid) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearFailureReplicators() {
|
||||||
|
this.failureReplicators.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean waitCaughtUp(final PeerId peer, final long maxMargin, final long dueTime, final CatchUpClosure done) {
|
||||||
|
final ThreadId rid = this.replicatorMap.get(peer);
|
||||||
|
if (rid == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Replicator.waitForCaughtUp(rid, maxMargin, dueTime, done);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLastRpcSendTimestamp(final PeerId peer) {
|
||||||
|
final ThreadId rid = this.replicatorMap.get(peer);
|
||||||
|
if (rid == null) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
return Replicator.getLastRpcSendTimestamp(rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopAll() {
|
||||||
|
final List<ThreadId> rids = new ArrayList<>(this.replicatorMap.values());
|
||||||
|
this.replicatorMap.clear();
|
||||||
|
this.failureReplicators.clear();
|
||||||
|
for (final ThreadId rid : rids) {
|
||||||
|
Replicator.stop(rid);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkReplicator(final PeerId peer, final boolean lockNode) {
|
||||||
|
final ThreadId rid = this.replicatorMap.get(peer);
|
||||||
|
if (rid == null) {
|
||||||
|
// Create replicator if it's not found for leader.
|
||||||
|
final NodeImpl node = this.commonOptions.getNode();
|
||||||
|
if (lockNode) {
|
||||||
|
node.writeLock.lock();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (node.isLeader()) {
|
||||||
|
final ReplicatorType rType = this.failureReplicators.get(peer);
|
||||||
|
if (rType != null && addReplicator(peer, rType, false)) {
|
||||||
|
this.failureReplicators.remove(peer, rType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (lockNode) {
|
||||||
|
node.writeLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopReplicator(final PeerId peer) {
|
||||||
|
LOG.info("Stop replicator to {}.", peer);
|
||||||
|
this.failureReplicators.remove(peer);
|
||||||
|
final ThreadId rid = this.replicatorMap.remove(peer);
|
||||||
|
if (rid == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Calling ReplicatorId.stop might lead to calling stopReplicator again,
|
||||||
|
// erase entry first to avoid race condition
|
||||||
|
return Replicator.stop(rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean resetTerm(final long newTerm) {
|
||||||
|
if (newTerm <= this.commonOptions.getTerm()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.commonOptions.setTerm(newTerm);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean resetHeartbeatInterval(final int newIntervalMs) {
|
||||||
|
this.dynamicTimeoutMs = newIntervalMs;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean resetElectionTimeoutInterval(final int newIntervalMs) {
|
||||||
|
this.electionTimeoutMs = newIntervalMs;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(final PeerId peer) {
|
||||||
|
return this.replicatorMap.containsKey(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean transferLeadershipTo(final PeerId peer, final long logIndex) {
|
||||||
|
final ThreadId rid = this.replicatorMap.get(peer);
|
||||||
|
return rid != null && Replicator.transferLeadership(rid, logIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopTransferLeadership(final PeerId peer) {
|
||||||
|
final ThreadId rid = this.replicatorMap.get(peer);
|
||||||
|
return rid != null && Replicator.stopTransferLeadership(rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ThreadId stopAllAndFindTheNextCandidate(final ConfigurationEntry conf) {
|
||||||
|
ThreadId candidate = null;
|
||||||
|
final PeerId candidateId = findTheNextCandidate(conf);
|
||||||
|
if (candidateId != null) {
|
||||||
|
candidate = this.replicatorMap.get(candidateId);
|
||||||
|
} else {
|
||||||
|
LOG.info("Fail to find the next candidate.");
|
||||||
|
}
|
||||||
|
for (final ThreadId r : this.replicatorMap.values()) {
|
||||||
|
if (r != candidate) {
|
||||||
|
Replicator.stop(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.replicatorMap.clear();
|
||||||
|
this.failureReplicators.clear();
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PeerId findTheNextCandidate(final ConfigurationEntry conf) {
|
||||||
|
PeerId peerId = null;
|
||||||
|
int priority = Integer.MIN_VALUE;
|
||||||
|
long maxIndex = -1L;
|
||||||
|
for (final Map.Entry<PeerId, ThreadId> entry : this.replicatorMap.entrySet()) {
|
||||||
|
if (!conf.contains(entry.getKey())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final int nextPriority = entry.getKey().getPriority();
|
||||||
|
if (nextPriority == ElectionPriority.NotElected) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final long nextIndex = Replicator.getNextIndex(entry.getValue());
|
||||||
|
if (nextIndex > maxIndex) {
|
||||||
|
maxIndex = nextIndex;
|
||||||
|
peerId = entry.getKey();
|
||||||
|
priority = peerId.getPriority();
|
||||||
|
} else if (nextIndex == maxIndex && nextPriority > priority) {
|
||||||
|
peerId = entry.getKey();
|
||||||
|
priority = peerId.getPriority();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxIndex == -1L) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return peerId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ThreadId> listReplicators() {
|
||||||
|
return new ArrayList<>(this.replicatorMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describe(final Printer out) {
|
||||||
|
out.print(" replicators: ") //
|
||||||
|
.println(this.replicatorMap.values());
|
||||||
|
out.print(" failureReplicators: ") //
|
||||||
|
.println(this.failureReplicators);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replicator role
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum ReplicatorType {
|
||||||
|
Follower, Learner;
|
||||||
|
|
||||||
|
public final boolean isFollower() {
|
||||||
|
return this == Follower;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isLearner() {
|
||||||
|
return this == Learner;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jiachun.fjc
|
||||||
|
*/
|
||||||
|
public interface Scheduler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and executes a one-shot action that becomes enabled
|
||||||
|
* after the given delay.
|
||||||
|
*
|
||||||
|
* @param command the task to execute
|
||||||
|
* @param delay the time from now to delay execution
|
||||||
|
* @param unit the time unit of the delay parameter
|
||||||
|
* @return a ScheduledFuture representing pending completion of
|
||||||
|
* the task and whose {@code get()} method will return
|
||||||
|
* {@code null} upon completion
|
||||||
|
* scheduled for execution
|
||||||
|
*/
|
||||||
|
ScheduledFuture<?> schedule(final Runnable command, final long delay, final TimeUnit unit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and executes a periodic action that becomes enabled first
|
||||||
|
* after the given initial delay, and subsequently with the given
|
||||||
|
* period; that is executions will commence after
|
||||||
|
* {@code initialDelay} then {@code initialDelay+period}, then
|
||||||
|
* {@code initialDelay + 2 * period}, and so on.
|
||||||
|
* If any execution of the task
|
||||||
|
* encounters an exception, subsequent executions are suppressed.
|
||||||
|
* Otherwise, the task will only terminate via cancellation or
|
||||||
|
* termination of the executor. If any execution of this task
|
||||||
|
* takes longer than its period, then subsequent executions
|
||||||
|
* may start late, but will not concurrently execute.
|
||||||
|
*
|
||||||
|
* @param command the task to execute
|
||||||
|
* @param initialDelay the time to delay first execution
|
||||||
|
* @param period the period between successive executions
|
||||||
|
* @param unit the time unit of the initialDelay and period parameters
|
||||||
|
* @return a ScheduledFuture representing pending completion of
|
||||||
|
* the task, and whose {@code get()} method will throw an
|
||||||
|
* exception upon cancellation
|
||||||
|
*/
|
||||||
|
ScheduledFuture<?> scheduleAtFixedRate(final Runnable command, final long initialDelay, final long period,
|
||||||
|
final TimeUnit unit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and executes a periodic action that becomes enabled first
|
||||||
|
* after the given initial delay, and subsequently with the
|
||||||
|
* given delay between the termination of one execution and the
|
||||||
|
* commencement of the next. If any execution of the task
|
||||||
|
* encounters an exception, subsequent executions are suppressed.
|
||||||
|
* Otherwise, the task will only terminate via cancellation or
|
||||||
|
* termination of the executor.
|
||||||
|
*
|
||||||
|
* @param command the task to execute
|
||||||
|
* @param initialDelay the time to delay first execution
|
||||||
|
* @param delay the delay between the termination of one
|
||||||
|
* execution and the commencement of the next
|
||||||
|
* @param unit the time unit of the initialDelay and delay parameters
|
||||||
|
* @return a ScheduledFuture representing pending completion of
|
||||||
|
* the task, and whose {@code get()} method will throw an
|
||||||
|
* exception upon cancellation
|
||||||
|
*/
|
||||||
|
ScheduledFuture<?> scheduleWithFixedDelay(final Runnable command, final long initialDelay, final long delay,
|
||||||
|
final TimeUnit unit);
|
||||||
|
|
||||||
|
void shutdown();
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node state
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-08 5:41:54 PM
|
||||||
|
*/
|
||||||
|
public enum State {
|
||||||
|
STATE_LEADER, // It's a leader
|
||||||
|
STATE_TRANSFERRING, // It's transferring leadership
|
||||||
|
STATE_CANDIDATE, // It's a candidate
|
||||||
|
STATE_FOLLOWER, // It's a follower
|
||||||
|
STATE_ERROR, // It's in error
|
||||||
|
STATE_UNINITIALIZED, // It's uninitialized
|
||||||
|
STATE_SHUTTING, // It's shutting down
|
||||||
|
STATE_SHUTDOWN, // It's shutdown already
|
||||||
|
STATE_END; // State end
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return this.ordinal() < STATE_ERROR.ordinal();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.StateMachine;
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
import com.alipay.sofa.jraft.entity.LeaderChangeContext;
|
||||||
|
import com.alipay.sofa.jraft.error.RaftException;
|
||||||
|
import com.alipay.sofa.jraft.storage.snapshot.SnapshotReader;
|
||||||
|
import com.alipay.sofa.jraft.storage.snapshot.SnapshotWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State machine adapter that implements all methods with default behavior
|
||||||
|
* except {@link #onApply(com.alipay.sofa.jraft.Iterator)}.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-30 10:32:27 AM
|
||||||
|
*/
|
||||||
|
public abstract class StateMachineAdapter implements StateMachine {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(StateMachineAdapter.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onShutdown() {
|
||||||
|
LOG.info("onShutdown.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSnapshotSave(final SnapshotWriter writer, final Closure done) {
|
||||||
|
error("onSnapshotSave");
|
||||||
|
runClosure(done, "onSnapshotSave");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSnapshotLoad(final SnapshotReader reader) {
|
||||||
|
error("onSnapshotLoad", "while a snapshot is saved in " + reader.getPath());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLeaderStart(final long term) {
|
||||||
|
LOG.info("onLeaderStart: term={}.", term);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLeaderStop(final Status status) {
|
||||||
|
LOG.info("onLeaderStop: status={}.", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(final RaftException e) {
|
||||||
|
LOG.error(
|
||||||
|
"Encountered an error={} on StateMachine {}, it's highly recommended to implement this method as raft stops working since some error occurs, you should figure out the cause and repair or remove this node.",
|
||||||
|
e.getStatus(), getClassName(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationCommitted(final Configuration conf) {
|
||||||
|
LOG.info("onConfigurationCommitted: {}.", conf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopFollowing(final LeaderChangeContext ctx) {
|
||||||
|
LOG.info("onStopFollowing: {}.", ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartFollowing(final LeaderChangeContext ctx) {
|
||||||
|
LOG.info("onStartFollowing: {}.", ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("SameParameterValue")
|
||||||
|
private void runClosure(final Closure done, final String methodName) {
|
||||||
|
done.run(new Status(-1, "%s doesn't implement %s", getClassName(), methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getClassName() {
|
||||||
|
return getClass().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("SameParameterValue")
|
||||||
|
private void error(final String methodName) {
|
||||||
|
error(methodName, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void error(final String methodName, final String msg) {
|
||||||
|
LOG.error("{} doesn't implement {} {}.", getClassName(), methodName, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.core;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.util.NamedThreadFactory;
|
||||||
|
import com.alipay.sofa.jraft.util.ThreadPoolUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The global timer manager.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-30 3:24:34 PM
|
||||||
|
*/
|
||||||
|
public class TimerManager implements Scheduler {
|
||||||
|
|
||||||
|
private final ScheduledExecutorService executor;
|
||||||
|
|
||||||
|
public TimerManager(int workerNum) {
|
||||||
|
this(workerNum, "JRaft-Node-ScheduleThreadPool");
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimerManager(int workerNum, String name) {
|
||||||
|
this.executor = ThreadPoolUtil.newScheduledBuilder() //
|
||||||
|
.poolName(name) //
|
||||||
|
.coreThreads(workerNum) //
|
||||||
|
.enableMetric(true) //
|
||||||
|
.threadFactory(new NamedThreadFactory(name, true)) //
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledFuture<?> schedule(final Runnable command, final long delay, final TimeUnit unit) {
|
||||||
|
return this.executor.schedule(command, delay, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledFuture<?> scheduleAtFixedRate(final Runnable command, final long initialDelay, final long period,
|
||||||
|
final TimeUnit unit) {
|
||||||
|
return this.executor.scheduleAtFixedRate(command, initialDelay, period, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScheduledFuture<?> scheduleWithFixedDelay(final Runnable command, final long initialDelay, final long delay,
|
||||||
|
final TimeUnit unit) {
|
||||||
|
return this.executor.scheduleWithFixedDelay(command, initialDelay, delay, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
this.executor.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ballot to vote.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-15 2:29:11 PM
|
||||||
|
*/
|
||||||
|
public class Ballot {
|
||||||
|
|
||||||
|
public static final class PosHint {
|
||||||
|
int pos0 = -1; // position in current peers
|
||||||
|
int pos1 = -1; // position in old peers
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UnfoundPeerId {
|
||||||
|
PeerId peerId;
|
||||||
|
boolean found;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
public UnfoundPeerId(PeerId peerId, int index, boolean found) {
|
||||||
|
super();
|
||||||
|
this.peerId = peerId;
|
||||||
|
this.index = index;
|
||||||
|
this.found = found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<UnfoundPeerId> peers = new ArrayList<>();
|
||||||
|
private int quorum;
|
||||||
|
private final List<UnfoundPeerId> oldPeers = new ArrayList<>();
|
||||||
|
private int oldQuorum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init the ballot with current conf and old conf.
|
||||||
|
*
|
||||||
|
* @param conf current configuration
|
||||||
|
* @param oldConf old configuration
|
||||||
|
* @return true if init success
|
||||||
|
*/
|
||||||
|
public boolean init(final Configuration conf, final Configuration oldConf) {
|
||||||
|
this.peers.clear();
|
||||||
|
this.oldPeers.clear();
|
||||||
|
this.quorum = this.oldQuorum = 0;
|
||||||
|
int index = 0;
|
||||||
|
if (conf != null) {
|
||||||
|
for (final PeerId peer : conf) {
|
||||||
|
this.peers.add(new UnfoundPeerId(peer, index++, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.quorum = this.peers.size() / 2 + 1;
|
||||||
|
if (oldConf == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
index = 0;
|
||||||
|
for (final PeerId peer : oldConf) {
|
||||||
|
this.oldPeers.add(new UnfoundPeerId(peer, index++, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.oldQuorum = this.oldPeers.size() / 2 + 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UnfoundPeerId findPeer(final PeerId peerId, final List<UnfoundPeerId> peers, final int posHint) {
|
||||||
|
if (posHint < 0 || posHint >= peers.size() || !peers.get(posHint).peerId.equals(peerId)) {
|
||||||
|
for (final UnfoundPeerId ufp : peers) {
|
||||||
|
if (ufp.peerId.equals(peerId)) {
|
||||||
|
return ufp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return peers.get(posHint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PosHint grant(final PeerId peerId, final PosHint hint) {
|
||||||
|
UnfoundPeerId peer = findPeer(peerId, this.peers, hint.pos0);
|
||||||
|
if (peer != null) {
|
||||||
|
if (!peer.found) {
|
||||||
|
peer.found = true;
|
||||||
|
this.quorum--;
|
||||||
|
}
|
||||||
|
hint.pos0 = peer.index;
|
||||||
|
} else {
|
||||||
|
hint.pos0 = -1;
|
||||||
|
}
|
||||||
|
if (this.oldPeers.isEmpty()) {
|
||||||
|
hint.pos1 = -1;
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
peer = findPeer(peerId, this.oldPeers, hint.pos1);
|
||||||
|
if (peer != null) {
|
||||||
|
if (!peer.found) {
|
||||||
|
peer.found = true;
|
||||||
|
this.oldQuorum--;
|
||||||
|
}
|
||||||
|
hint.pos1 = peer.index;
|
||||||
|
} else {
|
||||||
|
hint.pos1 = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void grant(final PeerId peerId) {
|
||||||
|
grant(peerId, new PosHint());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the ballot is granted.
|
||||||
|
*
|
||||||
|
* @return true if the ballot is granted
|
||||||
|
*/
|
||||||
|
public boolean isGranted() {
|
||||||
|
return this.quorum <= 0 && this.oldQuorum <= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checksum for entity.
|
||||||
|
*
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
* @since 1.2.6
|
||||||
|
*/
|
||||||
|
public interface Checksum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate a checksum value for this entity.
|
||||||
|
* @return checksum value
|
||||||
|
*/
|
||||||
|
long checksum();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the checksum value of two long values.
|
||||||
|
*
|
||||||
|
* @param v1 first long value
|
||||||
|
* @param v2 second long value
|
||||||
|
* @return checksum value
|
||||||
|
*/
|
||||||
|
default long checksum(final long v1, final long v2) {
|
||||||
|
return v1 ^ v2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the checksum value of act on factors.
|
||||||
|
*
|
||||||
|
* @param factors checksum collection
|
||||||
|
* @param v origin checksum
|
||||||
|
* @return checksum value
|
||||||
|
*/
|
||||||
|
default long checksum(final Collection<? extends Checksum> factors, long v) {
|
||||||
|
if (factors != null && !factors.isEmpty()) {
|
||||||
|
for (final Checksum factor : factors) {
|
||||||
|
v = checksum(v, factor.checksum());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,290 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
// source: enum.proto
|
||||||
|
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
public final class EnumOutter {
|
||||||
|
private EnumOutter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) {
|
||||||
|
registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protobuf enum {@code jraft.EntryType}
|
||||||
|
*/
|
||||||
|
public enum EntryType implements com.google.protobuf.ProtocolMessageEnum {
|
||||||
|
/**
|
||||||
|
* <code>ENTRY_TYPE_UNKNOWN = 0;</code>
|
||||||
|
*/
|
||||||
|
ENTRY_TYPE_UNKNOWN(0),
|
||||||
|
/**
|
||||||
|
* <code>ENTRY_TYPE_NO_OP = 1;</code>
|
||||||
|
*/
|
||||||
|
ENTRY_TYPE_NO_OP(1),
|
||||||
|
/**
|
||||||
|
* <code>ENTRY_TYPE_DATA = 2;</code>
|
||||||
|
*/
|
||||||
|
ENTRY_TYPE_DATA(2),
|
||||||
|
/**
|
||||||
|
* <code>ENTRY_TYPE_CONFIGURATION = 3;</code>
|
||||||
|
*/
|
||||||
|
ENTRY_TYPE_CONFIGURATION(3), ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>ENTRY_TYPE_UNKNOWN = 0;</code>
|
||||||
|
*/
|
||||||
|
public static final int ENTRY_TYPE_UNKNOWN_VALUE = 0;
|
||||||
|
/**
|
||||||
|
* <code>ENTRY_TYPE_NO_OP = 1;</code>
|
||||||
|
*/
|
||||||
|
public static final int ENTRY_TYPE_NO_OP_VALUE = 1;
|
||||||
|
/**
|
||||||
|
* <code>ENTRY_TYPE_DATA = 2;</code>
|
||||||
|
*/
|
||||||
|
public static final int ENTRY_TYPE_DATA_VALUE = 2;
|
||||||
|
/**
|
||||||
|
* <code>ENTRY_TYPE_CONFIGURATION = 3;</code>
|
||||||
|
*/
|
||||||
|
public static final int ENTRY_TYPE_CONFIGURATION_VALUE = 3;
|
||||||
|
|
||||||
|
public final int getNumber() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #forNumber(int)} instead.
|
||||||
|
*/
|
||||||
|
@java.lang.Deprecated
|
||||||
|
public static EntryType valueOf(int value) {
|
||||||
|
return forNumber(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntryType forNumber(int value) {
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
return ENTRY_TYPE_UNKNOWN;
|
||||||
|
case 1:
|
||||||
|
return ENTRY_TYPE_NO_OP;
|
||||||
|
case 2:
|
||||||
|
return ENTRY_TYPE_DATA;
|
||||||
|
case 3:
|
||||||
|
return ENTRY_TYPE_CONFIGURATION;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.google.protobuf.Internal.EnumLiteMap<EntryType> internalGetValueMap() {
|
||||||
|
return internalValueMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final com.google.protobuf.Internal.EnumLiteMap<EntryType> internalValueMap = new com.google.protobuf.Internal.EnumLiteMap<EntryType>() {
|
||||||
|
public EntryType findValueByNumber(int number) {
|
||||||
|
return EntryType
|
||||||
|
.forNumber(number);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public final com.google.protobuf.Descriptors.EnumValueDescriptor getValueDescriptor() {
|
||||||
|
return getDescriptor().getValues().get(ordinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public final com.google.protobuf.Descriptors.EnumDescriptor getDescriptorForType() {
|
||||||
|
return getDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() {
|
||||||
|
return com.alipay.sofa.jraft.entity.EnumOutter.getDescriptor().getEnumTypes().get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final EntryType[] VALUES = values();
|
||||||
|
|
||||||
|
public static EntryType valueOf(com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
|
||||||
|
if (desc.getType() != getDescriptor()) {
|
||||||
|
throw new java.lang.IllegalArgumentException("EnumValueDescriptor is not for this type.");
|
||||||
|
}
|
||||||
|
return VALUES[desc.getIndex()];
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
private EntryType(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @@protoc_insertion_point(enum_scope:jraft.EntryType)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protobuf enum {@code jraft.ErrorType}
|
||||||
|
*/
|
||||||
|
public enum ErrorType implements com.google.protobuf.ProtocolMessageEnum {
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_NONE = 0;</code>
|
||||||
|
*/
|
||||||
|
ERROR_TYPE_NONE(0),
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_LOG = 1;</code>
|
||||||
|
*/
|
||||||
|
ERROR_TYPE_LOG(1),
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_STABLE = 2;</code>
|
||||||
|
*/
|
||||||
|
ERROR_TYPE_STABLE(2),
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_SNAPSHOT = 3;</code>
|
||||||
|
*/
|
||||||
|
ERROR_TYPE_SNAPSHOT(3),
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_STATE_MACHINE = 4;</code>
|
||||||
|
*/
|
||||||
|
ERROR_TYPE_STATE_MACHINE(4),
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_META = 5;</code>
|
||||||
|
*/
|
||||||
|
ERROR_TYPE_META(5), ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_NONE = 0;</code>
|
||||||
|
*/
|
||||||
|
public static final int ERROR_TYPE_NONE_VALUE = 0;
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_LOG = 1;</code>
|
||||||
|
*/
|
||||||
|
public static final int ERROR_TYPE_LOG_VALUE = 1;
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_STABLE = 2;</code>
|
||||||
|
*/
|
||||||
|
public static final int ERROR_TYPE_STABLE_VALUE = 2;
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_SNAPSHOT = 3;</code>
|
||||||
|
*/
|
||||||
|
public static final int ERROR_TYPE_SNAPSHOT_VALUE = 3;
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_STATE_MACHINE = 4;</code>
|
||||||
|
*/
|
||||||
|
public static final int ERROR_TYPE_STATE_MACHINE_VALUE = 4;
|
||||||
|
/**
|
||||||
|
* <code>ERROR_TYPE_META = 5;</code>
|
||||||
|
*/
|
||||||
|
public static final int ERROR_TYPE_META_VALUE = 5;
|
||||||
|
|
||||||
|
public final int getNumber() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #forNumber(int)} instead.
|
||||||
|
*/
|
||||||
|
@java.lang.Deprecated
|
||||||
|
public static ErrorType valueOf(int value) {
|
||||||
|
return forNumber(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ErrorType forNumber(int value) {
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
return ERROR_TYPE_NONE;
|
||||||
|
case 1:
|
||||||
|
return ERROR_TYPE_LOG;
|
||||||
|
case 2:
|
||||||
|
return ERROR_TYPE_STABLE;
|
||||||
|
case 3:
|
||||||
|
return ERROR_TYPE_SNAPSHOT;
|
||||||
|
case 4:
|
||||||
|
return ERROR_TYPE_STATE_MACHINE;
|
||||||
|
case 5:
|
||||||
|
return ERROR_TYPE_META;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.google.protobuf.Internal.EnumLiteMap<ErrorType> internalGetValueMap() {
|
||||||
|
return internalValueMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final com.google.protobuf.Internal.EnumLiteMap<ErrorType> internalValueMap = new com.google.protobuf.Internal.EnumLiteMap<ErrorType>() {
|
||||||
|
public ErrorType findValueByNumber(int number) {
|
||||||
|
return ErrorType
|
||||||
|
.forNumber(number);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public final com.google.protobuf.Descriptors.EnumValueDescriptor getValueDescriptor() {
|
||||||
|
return getDescriptor().getValues().get(ordinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public final com.google.protobuf.Descriptors.EnumDescriptor getDescriptorForType() {
|
||||||
|
return getDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() {
|
||||||
|
return com.alipay.sofa.jraft.entity.EnumOutter.getDescriptor().getEnumTypes().get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ErrorType[] VALUES = values();
|
||||||
|
|
||||||
|
public static ErrorType valueOf(com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
|
||||||
|
if (desc.getType() != getDescriptor()) {
|
||||||
|
throw new java.lang.IllegalArgumentException("EnumValueDescriptor is not for this type.");
|
||||||
|
}
|
||||||
|
return VALUES[desc.getIndex()];
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
private ErrorType(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @@protoc_insertion_point(enum_scope:jraft.ErrorType)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
|
||||||
|
static {
|
||||||
|
java.lang.String[] descriptorData = { "\n\nenum.proto\022\005jraft*l\n\tEntryType\022\026\n\022ENTR"
|
||||||
|
+ "Y_TYPE_UNKNOWN\020\000\022\024\n\020ENTRY_TYPE_NO_OP\020\001\022\023"
|
||||||
|
+ "\n\017ENTRY_TYPE_DATA\020\002\022\034\n\030ENTRY_TYPE_CONFIG"
|
||||||
|
+ "URATION\020\003*\227\001\n\tErrorType\022\023\n\017ERROR_TYPE_NO"
|
||||||
|
+ "NE\020\000\022\022\n\016ERROR_TYPE_LOG\020\001\022\025\n\021ERROR_TYPE_S"
|
||||||
|
+ "TABLE\020\002\022\027\n\023ERROR_TYPE_SNAPSHOT\020\003\022\034\n\030ERRO"
|
||||||
|
+ "R_TYPE_STATE_MACHINE\020\004\022\023\n\017ERROR_TYPE_MET"
|
||||||
|
+ "A\020\005B*\n\034com.alipay.sofa.jraft.entityB\nEnu" + "mOutter" };
|
||||||
|
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||||
|
public com.google.protobuf.ExtensionRegistry assignDescriptors(com.google.protobuf.Descriptors.FileDescriptor root) {
|
||||||
|
descriptor = root;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(descriptorData,
|
||||||
|
new com.google.protobuf.Descriptors.FileDescriptor[] {}, assigner);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @@protoc_insertion_point(outer_class_scope)
|
||||||
|
}
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The leader change context, contains:
|
||||||
|
* <ul>
|
||||||
|
* <li>leaderId: the leader peer id.</li>
|
||||||
|
* <li>term: the leader term.</li>
|
||||||
|
* <li>Status: context status.</li>
|
||||||
|
* </ul>
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-13 3:23:48 PM
|
||||||
|
*/
|
||||||
|
public class LeaderChangeContext {
|
||||||
|
|
||||||
|
private PeerId leaderId;
|
||||||
|
private long term;
|
||||||
|
private Status status;
|
||||||
|
|
||||||
|
public LeaderChangeContext(PeerId leaderId, long term, Status status) {
|
||||||
|
super();
|
||||||
|
this.leaderId = leaderId;
|
||||||
|
this.term = term;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerId getLeaderId() {
|
||||||
|
return this.leaderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLeaderId(PeerId leaderId) {
|
||||||
|
this.leaderId = leaderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTerm() {
|
||||||
|
return this.term;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTerm(long term) {
|
||||||
|
this.term = term;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status getStatus() {
|
||||||
|
return this.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(Status status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + (this.leaderId == null ? 0 : this.leaderId.hashCode());
|
||||||
|
result = prime * result + (this.status == null ? 0 : this.status.hashCode());
|
||||||
|
result = prime * result + (int) (this.term ^ this.term >>> 32);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LeaderChangeContext other = (LeaderChangeContext) obj;
|
||||||
|
if (this.leaderId == null) {
|
||||||
|
if (other.leaderId != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!this.leaderId.equals(other.leaderId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.status == null) {
|
||||||
|
if (other.status != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!this.status.equals(other.status)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.term == other.term;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "LeaderChangeContext [leaderId=" + this.leaderId + ", term=" + this.term + ", status=" + this.status
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,911 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
// source: local_file_meta.proto
|
||||||
|
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
public final class LocalFileMetaOutter {
|
||||||
|
private LocalFileMetaOutter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) {
|
||||||
|
registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protobuf enum {@code jraft.FileSource}
|
||||||
|
*/
|
||||||
|
public enum FileSource implements com.google.protobuf.ProtocolMessageEnum {
|
||||||
|
/**
|
||||||
|
* <code>FILE_SOURCE_LOCAL = 0;</code>
|
||||||
|
*/
|
||||||
|
FILE_SOURCE_LOCAL(0),
|
||||||
|
/**
|
||||||
|
* <code>FILE_SOURCE_REFERENCE = 1;</code>
|
||||||
|
*/
|
||||||
|
FILE_SOURCE_REFERENCE(1), ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>FILE_SOURCE_LOCAL = 0;</code>
|
||||||
|
*/
|
||||||
|
public static final int FILE_SOURCE_LOCAL_VALUE = 0;
|
||||||
|
/**
|
||||||
|
* <code>FILE_SOURCE_REFERENCE = 1;</code>
|
||||||
|
*/
|
||||||
|
public static final int FILE_SOURCE_REFERENCE_VALUE = 1;
|
||||||
|
|
||||||
|
public final int getNumber() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #forNumber(int)} instead.
|
||||||
|
*/
|
||||||
|
@java.lang.Deprecated
|
||||||
|
public static FileSource valueOf(int value) {
|
||||||
|
return forNumber(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileSource forNumber(int value) {
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
return FILE_SOURCE_LOCAL;
|
||||||
|
case 1:
|
||||||
|
return FILE_SOURCE_REFERENCE;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.google.protobuf.Internal.EnumLiteMap<FileSource> internalGetValueMap() {
|
||||||
|
return internalValueMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final com.google.protobuf.Internal.EnumLiteMap<FileSource> internalValueMap = new com.google.protobuf.Internal.EnumLiteMap<FileSource>() {
|
||||||
|
public FileSource findValueByNumber(int number) {
|
||||||
|
return FileSource
|
||||||
|
.forNumber(number);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public final com.google.protobuf.Descriptors.EnumValueDescriptor getValueDescriptor() {
|
||||||
|
return getDescriptor().getValues().get(ordinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public final com.google.protobuf.Descriptors.EnumDescriptor getDescriptorForType() {
|
||||||
|
return getDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final com.google.protobuf.Descriptors.EnumDescriptor getDescriptor() {
|
||||||
|
return com.alipay.sofa.jraft.entity.LocalFileMetaOutter.getDescriptor().getEnumTypes().get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final FileSource[] VALUES = values();
|
||||||
|
|
||||||
|
public static FileSource valueOf(com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
|
||||||
|
if (desc.getType() != getDescriptor()) {
|
||||||
|
throw new java.lang.IllegalArgumentException("EnumValueDescriptor is not for this type.");
|
||||||
|
}
|
||||||
|
return VALUES[desc.getIndex()];
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
private FileSource(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @@protoc_insertion_point(enum_scope:jraft.FileSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface LocalFileMetaOrBuilder extends
|
||||||
|
// @@protoc_insertion_point(interface_extends:jraft.LocalFileMeta)
|
||||||
|
com.google.protobuf.MessageOrBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional bytes user_meta = 1;</code>
|
||||||
|
*/
|
||||||
|
boolean hasUserMeta();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional bytes user_meta = 1;</code>
|
||||||
|
*/
|
||||||
|
com.google.protobuf.ByteString getUserMeta();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional .jraft.FileSource source = 2;</code>
|
||||||
|
*/
|
||||||
|
boolean hasSource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional .jraft.FileSource source = 2;</code>
|
||||||
|
*/
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource getSource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
boolean hasChecksum();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
java.lang.String getChecksum();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
com.google.protobuf.ByteString getChecksumBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protobuf type {@code jraft.LocalFileMeta}
|
||||||
|
*/
|
||||||
|
public static final class LocalFileMeta extends com.google.protobuf.GeneratedMessageV3 implements
|
||||||
|
// @@protoc_insertion_point(message_implements:jraft.LocalFileMeta)
|
||||||
|
LocalFileMetaOrBuilder {
|
||||||
|
private static final long serialVersionUID = 0L;
|
||||||
|
|
||||||
|
// Use LocalFileMeta.newBuilder() to construct.
|
||||||
|
private LocalFileMeta(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
|
||||||
|
super(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalFileMeta() {
|
||||||
|
userMeta_ = com.google.protobuf.ByteString.EMPTY;
|
||||||
|
source_ = 0;
|
||||||
|
checksum_ = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Override
|
||||||
|
public final com.google.protobuf.UnknownFieldSet getUnknownFields() {
|
||||||
|
return this.unknownFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalFileMeta(com.google.protobuf.CodedInputStream input,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
this();
|
||||||
|
if (extensionRegistry == null) {
|
||||||
|
throw new java.lang.NullPointerException();
|
||||||
|
}
|
||||||
|
int mutable_bitField0_ = 0;
|
||||||
|
com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet
|
||||||
|
.newBuilder();
|
||||||
|
try {
|
||||||
|
boolean done = false;
|
||||||
|
while (!done) {
|
||||||
|
int tag = input.readTag();
|
||||||
|
switch (tag) {
|
||||||
|
case 0:
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 10: {
|
||||||
|
bitField0_ |= 0x00000001;
|
||||||
|
userMeta_ = input.readBytes();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 16: {
|
||||||
|
int rawValue = input.readEnum();
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource value = com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource
|
||||||
|
.valueOf(rawValue);
|
||||||
|
if (value == null) {
|
||||||
|
unknownFields.mergeVarintField(2, rawValue);
|
||||||
|
} else {
|
||||||
|
bitField0_ |= 0x00000002;
|
||||||
|
source_ = rawValue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 26: {
|
||||||
|
com.google.protobuf.ByteString bs = input.readBytes();
|
||||||
|
bitField0_ |= 0x00000004;
|
||||||
|
checksum_ = bs;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||||
|
throw e.setUnfinishedMessage(this);
|
||||||
|
} catch (java.io.IOException e) {
|
||||||
|
throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this);
|
||||||
|
} finally {
|
||||||
|
this.unknownFields = unknownFields.build();
|
||||||
|
makeExtensionsImmutable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
|
||||||
|
return com.alipay.sofa.jraft.entity.LocalFileMetaOutter.internal_static_jraft_LocalFileMeta_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() {
|
||||||
|
return com.alipay.sofa.jraft.entity.LocalFileMetaOutter.internal_static_jraft_LocalFileMeta_fieldAccessorTable
|
||||||
|
.ensureFieldAccessorsInitialized(com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta.class,
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int bitField0_;
|
||||||
|
public static final int USER_META_FIELD_NUMBER = 1;
|
||||||
|
private com.google.protobuf.ByteString userMeta_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional bytes user_meta = 1;</code>
|
||||||
|
*/
|
||||||
|
public boolean hasUserMeta() {
|
||||||
|
return ((bitField0_ & 0x00000001) == 0x00000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional bytes user_meta = 1;</code>
|
||||||
|
*/
|
||||||
|
public com.google.protobuf.ByteString getUserMeta() {
|
||||||
|
return userMeta_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final int SOURCE_FIELD_NUMBER = 2;
|
||||||
|
private int source_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional .jraft.FileSource source = 2;</code>
|
||||||
|
*/
|
||||||
|
public boolean hasSource() {
|
||||||
|
return ((bitField0_ & 0x00000002) == 0x00000002);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional .jraft.FileSource source = 2;</code>
|
||||||
|
*/
|
||||||
|
public com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource getSource() {
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource result = com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource
|
||||||
|
.valueOf(source_);
|
||||||
|
return result == null ? com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource.FILE_SOURCE_LOCAL
|
||||||
|
: result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final int CHECKSUM_FIELD_NUMBER = 3;
|
||||||
|
private volatile java.lang.Object checksum_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
public boolean hasChecksum() {
|
||||||
|
return ((bitField0_ & 0x00000004) == 0x00000004);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
public java.lang.String getChecksum() {
|
||||||
|
java.lang.Object ref = checksum_;
|
||||||
|
if (ref instanceof java.lang.String) {
|
||||||
|
return (java.lang.String) ref;
|
||||||
|
} else {
|
||||||
|
com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
|
||||||
|
java.lang.String s = bs.toStringUtf8();
|
||||||
|
if (bs.isValidUtf8()) {
|
||||||
|
checksum_ = s;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
public com.google.protobuf.ByteString getChecksumBytes() {
|
||||||
|
java.lang.Object ref = checksum_;
|
||||||
|
if (ref instanceof java.lang.String) {
|
||||||
|
com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref);
|
||||||
|
checksum_ = b;
|
||||||
|
return b;
|
||||||
|
} else {
|
||||||
|
return (com.google.protobuf.ByteString) ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte memoizedIsInitialized = -1;
|
||||||
|
|
||||||
|
public final boolean isInitialized() {
|
||||||
|
byte isInitialized = memoizedIsInitialized;
|
||||||
|
if (isInitialized == 1)
|
||||||
|
return true;
|
||||||
|
if (isInitialized == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memoizedIsInitialized = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException {
|
||||||
|
if (((bitField0_ & 0x00000001) == 0x00000001)) {
|
||||||
|
output.writeBytes(1, userMeta_);
|
||||||
|
}
|
||||||
|
if (((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||||
|
output.writeEnum(2, source_);
|
||||||
|
}
|
||||||
|
if (((bitField0_ & 0x00000004) == 0x00000004)) {
|
||||||
|
com.google.protobuf.GeneratedMessageV3.writeString(output, 3, checksum_);
|
||||||
|
}
|
||||||
|
unknownFields.writeTo(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSerializedSize() {
|
||||||
|
int size = memoizedSize;
|
||||||
|
if (size != -1)
|
||||||
|
return size;
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
if (((bitField0_ & 0x00000001) == 0x00000001)) {
|
||||||
|
size += com.google.protobuf.CodedOutputStream.computeBytesSize(1, userMeta_);
|
||||||
|
}
|
||||||
|
if (((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||||
|
size += com.google.protobuf.CodedOutputStream.computeEnumSize(2, source_);
|
||||||
|
}
|
||||||
|
if (((bitField0_ & 0x00000004) == 0x00000004)) {
|
||||||
|
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, checksum_);
|
||||||
|
}
|
||||||
|
size += unknownFields.getSerializedSize();
|
||||||
|
memoizedSize = size;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Override
|
||||||
|
public boolean equals(final java.lang.Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta)) {
|
||||||
|
return super.equals(obj);
|
||||||
|
}
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta other = (com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta) obj;
|
||||||
|
|
||||||
|
boolean result = true;
|
||||||
|
result = result && (hasUserMeta() == other.hasUserMeta());
|
||||||
|
if (hasUserMeta()) {
|
||||||
|
result = result && getUserMeta().equals(other.getUserMeta());
|
||||||
|
}
|
||||||
|
result = result && (hasSource() == other.hasSource());
|
||||||
|
if (hasSource()) {
|
||||||
|
result = result && source_ == other.source_;
|
||||||
|
}
|
||||||
|
result = result && (hasChecksum() == other.hasChecksum());
|
||||||
|
if (hasChecksum()) {
|
||||||
|
result = result && getChecksum().equals(other.getChecksum());
|
||||||
|
}
|
||||||
|
result = result && unknownFields.equals(other.unknownFields);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Override
|
||||||
|
public int hashCode() {
|
||||||
|
if (memoizedHashCode != 0) {
|
||||||
|
return memoizedHashCode;
|
||||||
|
}
|
||||||
|
int hash = 41;
|
||||||
|
hash = (19 * hash) + getDescriptor().hashCode();
|
||||||
|
if (hasUserMeta()) {
|
||||||
|
hash = (37 * hash) + USER_META_FIELD_NUMBER;
|
||||||
|
hash = (53 * hash) + getUserMeta().hashCode();
|
||||||
|
}
|
||||||
|
if (hasSource()) {
|
||||||
|
hash = (37 * hash) + SOURCE_FIELD_NUMBER;
|
||||||
|
hash = (53 * hash) + source_;
|
||||||
|
}
|
||||||
|
if (hasChecksum()) {
|
||||||
|
hash = (37 * hash) + CHECKSUM_FIELD_NUMBER;
|
||||||
|
hash = (53 * hash) + getChecksum().hashCode();
|
||||||
|
}
|
||||||
|
hash = (29 * hash) + unknownFields.hashCode();
|
||||||
|
memoizedHashCode = hash;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseFrom(java.nio.ByteBuffer data)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
return PARSER.parseFrom(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseFrom(java.nio.ByteBuffer data,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
return PARSER.parseFrom(data, extensionRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseFrom(com.google.protobuf.ByteString data)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
return PARSER.parseFrom(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseFrom(com.google.protobuf.ByteString data,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
return PARSER.parseFrom(data, extensionRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseFrom(byte[] data)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
return PARSER.parseFrom(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseFrom(byte[] data,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
return PARSER.parseFrom(data, extensionRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseFrom(java.io.InputStream input)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseFrom(java.io.InputStream input,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseDelimitedFrom(java.io.InputStream input)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseDelimitedFrom(java.io.InputStream input,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input,
|
||||||
|
extensionRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseFrom(com.google.protobuf.CodedInputStream input)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parseFrom(com.google.protobuf.CodedInputStream input,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws java.io.IOException {
|
||||||
|
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder newBuilderForType() {
|
||||||
|
return newBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder newBuilder() {
|
||||||
|
return DEFAULT_INSTANCE.toBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder newBuilder(com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta prototype) {
|
||||||
|
return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder toBuilder() {
|
||||||
|
return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Override
|
||||||
|
protected Builder newBuilderForType(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
|
||||||
|
Builder builder = new Builder(parent);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protobuf type {@code jraft.LocalFileMeta}
|
||||||
|
*/
|
||||||
|
public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder<Builder>
|
||||||
|
implements
|
||||||
|
// @@protoc_insertion_point(builder_implements:jraft.LocalFileMeta)
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMetaOrBuilder {
|
||||||
|
public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
|
||||||
|
return com.alipay.sofa.jraft.entity.LocalFileMetaOutter.internal_static_jraft_LocalFileMeta_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() {
|
||||||
|
return com.alipay.sofa.jraft.entity.LocalFileMetaOutter.internal_static_jraft_LocalFileMeta_fieldAccessorTable
|
||||||
|
.ensureFieldAccessorsInitialized(
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta.class,
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct using com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta.newBuilder()
|
||||||
|
private Builder() {
|
||||||
|
maybeForceBuilderInitialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
|
||||||
|
super(parent);
|
||||||
|
maybeForceBuilderInitialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeForceBuilderInitialization() {
|
||||||
|
if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder clear() {
|
||||||
|
super.clear();
|
||||||
|
userMeta_ = com.google.protobuf.ByteString.EMPTY;
|
||||||
|
bitField0_ = (bitField0_ & ~0x00000001);
|
||||||
|
source_ = 0;
|
||||||
|
bitField0_ = (bitField0_ & ~0x00000002);
|
||||||
|
checksum_ = "";
|
||||||
|
bitField0_ = (bitField0_ & ~0x00000004);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() {
|
||||||
|
return com.alipay.sofa.jraft.entity.LocalFileMetaOutter.internal_static_jraft_LocalFileMeta_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta getDefaultInstanceForType() {
|
||||||
|
return com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta.getDefaultInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta build() {
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta result = buildPartial();
|
||||||
|
if (!result.isInitialized()) {
|
||||||
|
throw newUninitializedMessageException(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta buildPartial() {
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta result = new com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta(
|
||||||
|
this);
|
||||||
|
int from_bitField0_ = bitField0_;
|
||||||
|
int to_bitField0_ = 0;
|
||||||
|
if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
|
||||||
|
to_bitField0_ |= 0x00000001;
|
||||||
|
}
|
||||||
|
result.userMeta_ = userMeta_;
|
||||||
|
if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
|
||||||
|
to_bitField0_ |= 0x00000002;
|
||||||
|
}
|
||||||
|
result.source_ = source_;
|
||||||
|
if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
|
||||||
|
to_bitField0_ |= 0x00000004;
|
||||||
|
}
|
||||||
|
result.checksum_ = checksum_;
|
||||||
|
result.bitField0_ = to_bitField0_;
|
||||||
|
onBuilt();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder clone() {
|
||||||
|
return (Builder) super.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) {
|
||||||
|
return (Builder) super.setField(field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) {
|
||||||
|
return (Builder) super.clearField(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) {
|
||||||
|
return (Builder) super.clearOneof(oneof);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, int index,
|
||||||
|
java.lang.Object value) {
|
||||||
|
return (Builder) super.setRepeatedField(field, index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder addRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field,
|
||||||
|
java.lang.Object value) {
|
||||||
|
return (Builder) super.addRepeatedField(field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder mergeFrom(com.google.protobuf.Message other) {
|
||||||
|
if (other instanceof com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta) {
|
||||||
|
return mergeFrom((com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta) other);
|
||||||
|
} else {
|
||||||
|
super.mergeFrom(other);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder mergeFrom(com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta other) {
|
||||||
|
if (other == com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta.getDefaultInstance())
|
||||||
|
return this;
|
||||||
|
if (other.hasUserMeta()) {
|
||||||
|
setUserMeta(other.getUserMeta());
|
||||||
|
}
|
||||||
|
if (other.hasSource()) {
|
||||||
|
setSource(other.getSource());
|
||||||
|
}
|
||||||
|
if (other.hasChecksum()) {
|
||||||
|
bitField0_ |= 0x00000004;
|
||||||
|
checksum_ = other.checksum_;
|
||||||
|
onChanged();
|
||||||
|
}
|
||||||
|
this.mergeUnknownFields(other.unknownFields);
|
||||||
|
onChanged();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isInitialized() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder mergeFrom(com.google.protobuf.CodedInputStream input,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws java.io.IOException {
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta parsedMessage = null;
|
||||||
|
try {
|
||||||
|
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
|
||||||
|
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||||
|
parsedMessage = (com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta) e
|
||||||
|
.getUnfinishedMessage();
|
||||||
|
throw e.unwrapIOException();
|
||||||
|
} finally {
|
||||||
|
if (parsedMessage != null) {
|
||||||
|
mergeFrom(parsedMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int bitField0_;
|
||||||
|
|
||||||
|
private com.google.protobuf.ByteString userMeta_ = com.google.protobuf.ByteString.EMPTY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional bytes user_meta = 1;</code>
|
||||||
|
*/
|
||||||
|
public boolean hasUserMeta() {
|
||||||
|
return ((bitField0_ & 0x00000001) == 0x00000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional bytes user_meta = 1;</code>
|
||||||
|
*/
|
||||||
|
public com.google.protobuf.ByteString getUserMeta() {
|
||||||
|
return userMeta_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional bytes user_meta = 1;</code>
|
||||||
|
*/
|
||||||
|
public Builder setUserMeta(com.google.protobuf.ByteString value) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
bitField0_ |= 0x00000001;
|
||||||
|
userMeta_ = value;
|
||||||
|
onChanged();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional bytes user_meta = 1;</code>
|
||||||
|
*/
|
||||||
|
public Builder clearUserMeta() {
|
||||||
|
bitField0_ = (bitField0_ & ~0x00000001);
|
||||||
|
userMeta_ = getDefaultInstance().getUserMeta();
|
||||||
|
onChanged();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int source_ = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional .jraft.FileSource source = 2;</code>
|
||||||
|
*/
|
||||||
|
public boolean hasSource() {
|
||||||
|
return ((bitField0_ & 0x00000002) == 0x00000002);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional .jraft.FileSource source = 2;</code>
|
||||||
|
*/
|
||||||
|
public com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource getSource() {
|
||||||
|
com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource result = com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource
|
||||||
|
.valueOf(source_);
|
||||||
|
return result == null ? com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource.FILE_SOURCE_LOCAL
|
||||||
|
: result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional .jraft.FileSource source = 2;</code>
|
||||||
|
*/
|
||||||
|
public Builder setSource(com.alipay.sofa.jraft.entity.LocalFileMetaOutter.FileSource value) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
bitField0_ |= 0x00000002;
|
||||||
|
source_ = value.getNumber();
|
||||||
|
onChanged();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional .jraft.FileSource source = 2;</code>
|
||||||
|
*/
|
||||||
|
public Builder clearSource() {
|
||||||
|
bitField0_ = (bitField0_ & ~0x00000002);
|
||||||
|
source_ = 0;
|
||||||
|
onChanged();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private java.lang.Object checksum_ = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
public boolean hasChecksum() {
|
||||||
|
return ((bitField0_ & 0x00000004) == 0x00000004);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
public java.lang.String getChecksum() {
|
||||||
|
java.lang.Object ref = checksum_;
|
||||||
|
if (!(ref instanceof java.lang.String)) {
|
||||||
|
com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
|
||||||
|
java.lang.String s = bs.toStringUtf8();
|
||||||
|
if (bs.isValidUtf8()) {
|
||||||
|
checksum_ = s;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
} else {
|
||||||
|
return (java.lang.String) ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
public com.google.protobuf.ByteString getChecksumBytes() {
|
||||||
|
java.lang.Object ref = checksum_;
|
||||||
|
if (ref instanceof String) {
|
||||||
|
com.google.protobuf.ByteString b = com.google.protobuf.ByteString
|
||||||
|
.copyFromUtf8((java.lang.String) ref);
|
||||||
|
checksum_ = b;
|
||||||
|
return b;
|
||||||
|
} else {
|
||||||
|
return (com.google.protobuf.ByteString) ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
public Builder setChecksum(java.lang.String value) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
bitField0_ |= 0x00000004;
|
||||||
|
checksum_ = value;
|
||||||
|
onChanged();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
public Builder clearChecksum() {
|
||||||
|
bitField0_ = (bitField0_ & ~0x00000004);
|
||||||
|
checksum_ = getDefaultInstance().getChecksum();
|
||||||
|
onChanged();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>optional string checksum = 3;</code>
|
||||||
|
*/
|
||||||
|
public Builder setChecksumBytes(com.google.protobuf.ByteString value) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
bitField0_ |= 0x00000004;
|
||||||
|
checksum_ = value;
|
||||||
|
onChanged();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
|
||||||
|
return super.setUnknownFields(unknownFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
|
||||||
|
return super.mergeUnknownFields(unknownFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @@protoc_insertion_point(builder_scope:jraft.LocalFileMeta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @@protoc_insertion_point(class_scope:jraft.LocalFileMeta)
|
||||||
|
private static final com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta DEFAULT_INSTANCE;
|
||||||
|
static {
|
||||||
|
DEFAULT_INSTANCE = new com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta getDefaultInstance() {
|
||||||
|
return DEFAULT_INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Deprecated
|
||||||
|
public static final com.google.protobuf.Parser<LocalFileMeta> PARSER = new com.google.protobuf.AbstractParser<LocalFileMeta>() {
|
||||||
|
public LocalFileMeta parsePartialFrom(com.google.protobuf.CodedInputStream input,
|
||||||
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
|
return new LocalFileMeta(input,
|
||||||
|
extensionRegistry);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static com.google.protobuf.Parser<LocalFileMeta> parser() {
|
||||||
|
return PARSER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@java.lang.Override
|
||||||
|
public com.google.protobuf.Parser<LocalFileMeta> getParserForType() {
|
||||||
|
return PARSER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta getDefaultInstanceForType() {
|
||||||
|
return DEFAULT_INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final com.google.protobuf.Descriptors.Descriptor internal_static_jraft_LocalFileMeta_descriptor;
|
||||||
|
private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_jraft_LocalFileMeta_fieldAccessorTable;
|
||||||
|
|
||||||
|
public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
|
||||||
|
static {
|
||||||
|
java.lang.String[] descriptorData = { "\n\025local_file_meta.proto\022\005jraft\"W\n\rLocalF"
|
||||||
|
+ "ileMeta\022\021\n\tuser_meta\030\001 \001(\014\022!\n\006source\030\002 \001"
|
||||||
|
+ "(\0162\021.jraft.FileSource\022\020\n\010checksum\030\003 \001(\t*"
|
||||||
|
+ ">\n\nFileSource\022\025\n\021FILE_SOURCE_LOCAL\020\000\022\031\n\025"
|
||||||
|
+ "FILE_SOURCE_REFERENCE\020\001B3\n\034com.alipay.so"
|
||||||
|
+ "fa.jraft.entityB\023LocalFileMetaOutter" };
|
||||||
|
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||||
|
public com.google.protobuf.ExtensionRegistry assignDescriptors(com.google.protobuf.Descriptors.FileDescriptor root) {
|
||||||
|
descriptor = root;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(descriptorData,
|
||||||
|
new com.google.protobuf.Descriptors.FileDescriptor[] {}, assigner);
|
||||||
|
internal_static_jraft_LocalFileMeta_descriptor = getDescriptor().getMessageTypes().get(0);
|
||||||
|
internal_static_jraft_LocalFileMeta_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
|
||||||
|
internal_static_jraft_LocalFileMeta_descriptor,
|
||||||
|
new java.lang.String[] { "UserMeta", "Source", "Checksum", });
|
||||||
|
}
|
||||||
|
|
||||||
|
// @@protoc_insertion_point(outer_class_scope)
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,309 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryDecoder;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryEncoder;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.v1.LogEntryV1CodecFactory;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.v1.V1Decoder;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.v1.V1Encoder;
|
||||||
|
import com.alipay.sofa.jraft.util.CrcUtil;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A replica log entry.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-12 3:13:02 PM
|
||||||
|
*/
|
||||||
|
public class LogEntry implements Checksum {
|
||||||
|
|
||||||
|
public static final ByteBuffer EMPTY_DATA = ByteBuffer.wrap(new byte[0]);
|
||||||
|
|
||||||
|
/** entry type */
|
||||||
|
private EnumOutter.EntryType type;
|
||||||
|
/** log id with index/term */
|
||||||
|
private LogId id = new LogId(0, 0);
|
||||||
|
/** log entry current peers */
|
||||||
|
private List<PeerId> peers;
|
||||||
|
/** log entry old peers */
|
||||||
|
private List<PeerId> oldPeers;
|
||||||
|
/** log entry current learners */
|
||||||
|
private List<PeerId> learners;
|
||||||
|
/** log entry old learners */
|
||||||
|
private List<PeerId> oldLearners;
|
||||||
|
/** entry data */
|
||||||
|
private ByteBuffer data = EMPTY_DATA;
|
||||||
|
/** checksum for log entry*/
|
||||||
|
private long checksum;
|
||||||
|
/** true when the log has checksum **/
|
||||||
|
private boolean hasChecksum;
|
||||||
|
|
||||||
|
public List<PeerId> getLearners() {
|
||||||
|
return this.learners;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLearners(final List<PeerId> learners) {
|
||||||
|
this.learners = learners;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PeerId> getOldLearners() {
|
||||||
|
return this.oldLearners;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOldLearners(final List<PeerId> oldLearners) {
|
||||||
|
this.oldLearners = oldLearners;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogEntry() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogEntry(final EnumOutter.EntryType type) {
|
||||||
|
super();
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasLearners() {
|
||||||
|
return (this.learners != null && !this.learners.isEmpty())
|
||||||
|
|| (this.oldLearners != null && !this.oldLearners.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long checksum() {
|
||||||
|
long c = checksum(this.type.getNumber(), this.id.checksum());
|
||||||
|
c = checksum(this.peers, c);
|
||||||
|
c = checksum(this.oldPeers, c);
|
||||||
|
c = checksum(this.learners, c);
|
||||||
|
c = checksum(this.oldLearners, c);
|
||||||
|
if (this.data != null && this.data.hasRemaining()) {
|
||||||
|
c = checksum(c, CrcUtil.crc64(this.data));
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Please use {@link LogEntryEncoder} instead.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
* @return encoded byte array
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||||
|
@Deprecated
|
||||||
|
public byte[] encode() {
|
||||||
|
return V1Encoder.INSTANCE.encode(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Please use {@link LogEntryDecoder} instead.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
* @return whether success to decode
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||||
|
@Deprecated
|
||||||
|
public boolean decode(final byte[] content) {
|
||||||
|
if (content == null || content.length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (content[0] != LogEntryV1CodecFactory.MAGIC) {
|
||||||
|
// Corrupted log
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
V1Decoder.INSTANCE.decode(this, content);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the log entry has a checksum.
|
||||||
|
* @return true when the log entry has checksum, otherwise returns false.
|
||||||
|
* @since 1.2.26
|
||||||
|
*/
|
||||||
|
public boolean hasChecksum() {
|
||||||
|
return this.hasChecksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the log entry is corrupted, it means that the checksum is mismatch.
|
||||||
|
* @since 1.2.6
|
||||||
|
* @return true when the log entry is corrupted, otherwise returns false
|
||||||
|
*/
|
||||||
|
public boolean isCorrupted() {
|
||||||
|
return this.hasChecksum && this.checksum != checksum();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the checksum of the log entry. You should use {@link #hasChecksum} to check if
|
||||||
|
* it has checksum.
|
||||||
|
* @return checksum value
|
||||||
|
*/
|
||||||
|
public long getChecksum() {
|
||||||
|
return this.checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChecksum(final long checksum) {
|
||||||
|
this.checksum = checksum;
|
||||||
|
this.hasChecksum = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumOutter.EntryType getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(final EnumOutter.EntryType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogId getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(final LogId id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PeerId> getPeers() {
|
||||||
|
return this.peers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeers(final List<PeerId> peers) {
|
||||||
|
this.peers = peers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PeerId> getOldPeers() {
|
||||||
|
return this.oldPeers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOldPeers(final List<PeerId> oldPeers) {
|
||||||
|
this.oldPeers = oldPeers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the log data, it's not read-only, you SHOULD take care it's modification and
|
||||||
|
* thread-safety by yourself.
|
||||||
|
*
|
||||||
|
* @return the log data
|
||||||
|
*/
|
||||||
|
public ByteBuffer getData() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new byte buffer whose content is a shared subsequence of this log entry's data
|
||||||
|
* buffer's content.
|
||||||
|
*
|
||||||
|
* @return The new byte buffer
|
||||||
|
*/
|
||||||
|
public ByteBuffer sliceData() {
|
||||||
|
return this.data != null ? this.data.slice() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new, read-only byte buffer that shares this log entry's data buffer's content.
|
||||||
|
*
|
||||||
|
* @return the new, read-only byte buffer
|
||||||
|
*/
|
||||||
|
public ByteBuffer getReadOnlyData() {
|
||||||
|
return this.data != null ? this.data.asReadOnlyBuffer() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(final ByteBuffer data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "LogEntry [type=" + this.type + ", id=" + this.id + ", peers=" + this.peers + ", oldPeers="
|
||||||
|
+ this.oldPeers + ", learners=" + this.learners + ", oldLearners=" + this.oldLearners + ", data="
|
||||||
|
+ (this.data != null ? this.data.remaining() : 0) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((this.data == null) ? 0 : this.data.hashCode());
|
||||||
|
result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
|
||||||
|
result = prime * result + ((this.learners == null) ? 0 : this.learners.hashCode());
|
||||||
|
result = prime * result + ((this.oldLearners == null) ? 0 : this.oldLearners.hashCode());
|
||||||
|
result = prime * result + ((this.oldPeers == null) ? 0 : this.oldPeers.hashCode());
|
||||||
|
result = prime * result + ((this.peers == null) ? 0 : this.peers.hashCode());
|
||||||
|
result = prime * result + ((this.type == null) ? 0 : this.type.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LogEntry other = (LogEntry) obj;
|
||||||
|
if (this.data == null) {
|
||||||
|
if (other.data != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!this.data.equals(other.data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.id == null) {
|
||||||
|
if (other.id != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!this.id.equals(other.id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.learners == null) {
|
||||||
|
if (other.learners != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!this.learners.equals(other.learners)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.oldLearners == null) {
|
||||||
|
if (other.oldLearners != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!this.oldLearners.equals(other.oldLearners)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.oldPeers == null) {
|
||||||
|
if (other.oldPeers != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!this.oldPeers.equals(other.oldPeers)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.peers == null) {
|
||||||
|
if (other.peers != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!this.peers.equals(other.peers)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.type == other.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.util.Bits;
|
||||||
|
import com.alipay.sofa.jraft.util.Copiable;
|
||||||
|
import com.alipay.sofa.jraft.util.CrcUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log identifier.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-12 3:12:29 PM
|
||||||
|
*/
|
||||||
|
public class LogId implements Comparable<LogId>, Copiable<LogId>, Serializable, Checksum {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -6680425579347357313L;
|
||||||
|
|
||||||
|
private long index;
|
||||||
|
private long term;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogId copy() {
|
||||||
|
return new LogId(this.index, this.term);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long checksum() {
|
||||||
|
byte[] bs = new byte[16];
|
||||||
|
Bits.putLong(bs, 0, this.index);
|
||||||
|
Bits.putLong(bs, 8, this.term);
|
||||||
|
return CrcUtil.crc64(bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogId() {
|
||||||
|
this(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogId(final long index, final long term) {
|
||||||
|
super();
|
||||||
|
setIndex(index);
|
||||||
|
setTerm(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + (int) (this.index ^ (this.index >>> 32));
|
||||||
|
result = prime * result + (int) (this.term ^ (this.term >>> 32));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final LogId other = (LogId) obj;
|
||||||
|
if (this.index != other.index) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// noinspection RedundantIfStatement
|
||||||
|
if (this.term != other.term) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final LogId o) {
|
||||||
|
// Compare term at first
|
||||||
|
final int c = Long.compare(getTerm(), o.getTerm());
|
||||||
|
if (c == 0) {
|
||||||
|
return Long.compare(getIndex(), o.getIndex());
|
||||||
|
} else {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTerm() {
|
||||||
|
return this.term;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTerm(final long term) {
|
||||||
|
this.term = term;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getIndex() {
|
||||||
|
return this.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(final long index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "LogId [index=" + this.index + ", term=" + this.term + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A raft node identifier.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-03 4:08:14 PM
|
||||||
|
*/
|
||||||
|
public final class NodeId implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 4428173460056804264L;
|
||||||
|
|
||||||
|
/** Raft group id*/
|
||||||
|
private final String groupId;
|
||||||
|
/** Node peer id*/
|
||||||
|
private final PeerId peerId;
|
||||||
|
/** cached toString result*/
|
||||||
|
private String str;
|
||||||
|
|
||||||
|
public NodeId(String groupId, PeerId peerId) {
|
||||||
|
super();
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.peerId = peerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return this.groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (str == null) {
|
||||||
|
str = "<" + this.groupId + "/" + this.peerId + ">";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerId getPeerId() {
|
||||||
|
return this.peerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + (this.groupId == null ? 0 : this.groupId.hashCode());
|
||||||
|
result = prime * result + (this.peerId == null ? 0 : this.peerId.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final NodeId other = (NodeId) obj;
|
||||||
|
if (this.groupId == null) {
|
||||||
|
if (other.groupId != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!this.groupId.equals(other.groupId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.peerId == null) {
|
||||||
|
return other.peerId == null;
|
||||||
|
} else {
|
||||||
|
return this.peerId.equals(other.peerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,278 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.core.ElectionPriority;
|
||||||
|
import com.alipay.sofa.jraft.util.AsciiStringUtil;
|
||||||
|
import com.alipay.sofa.jraft.util.Copiable;
|
||||||
|
import com.alipay.sofa.jraft.util.CrcUtil;
|
||||||
|
import com.alipay.sofa.jraft.util.Endpoint;
|
||||||
|
import com.alipay.sofa.jraft.util.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent a participant in a replicating group.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-12 3:27:37 PM
|
||||||
|
*/
|
||||||
|
public class PeerId implements Copiable<PeerId>, Serializable, Checksum {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 8083529734784884641L;
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(PeerId.class);
|
||||||
|
|
||||||
|
/** Peer address. */
|
||||||
|
private Endpoint endpoint = new Endpoint(Utils.IP_ANY, 0);
|
||||||
|
/** Index in same addr, default is 0. */
|
||||||
|
private int idx;
|
||||||
|
/** Cached toString result. */
|
||||||
|
private String str;
|
||||||
|
|
||||||
|
/** Node's local priority value, if node don't support priority election, this value is -1. */
|
||||||
|
private int priority = ElectionPriority.Disabled;
|
||||||
|
|
||||||
|
public static final PeerId ANY_PEER = new PeerId();
|
||||||
|
|
||||||
|
private long checksum;
|
||||||
|
|
||||||
|
public PeerId() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long checksum() {
|
||||||
|
if (this.checksum == 0) {
|
||||||
|
this.checksum = CrcUtil.crc64(AsciiStringUtil.unsafeEncode(toString()));
|
||||||
|
}
|
||||||
|
return this.checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an empty peer.
|
||||||
|
* @return empty peer
|
||||||
|
*/
|
||||||
|
public static PeerId emptyPeer() {
|
||||||
|
return new PeerId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PeerId copy() {
|
||||||
|
return new PeerId(this.endpoint.copy(), this.idx, this.priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a peer from string in the format of "ip:port:idx",
|
||||||
|
* returns null if fail to parse.
|
||||||
|
*
|
||||||
|
* @param s input string with the format of "ip:port:idx"
|
||||||
|
* @return parsed peer
|
||||||
|
*/
|
||||||
|
public static PeerId parsePeer(final String s) {
|
||||||
|
final PeerId peer = new PeerId();
|
||||||
|
if (peer.parse(s)) {
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerId(final Endpoint endpoint, final int idx) {
|
||||||
|
super();
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
this.idx = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerId(final String ip, final int port) {
|
||||||
|
this(ip, port, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerId(final String ip, final int port, final int idx) {
|
||||||
|
super();
|
||||||
|
this.endpoint = new Endpoint(ip, port);
|
||||||
|
this.idx = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerId(final Endpoint endpoint, final int idx, final int priority) {
|
||||||
|
super();
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
this.idx = idx;
|
||||||
|
this.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerId(final String ip, final int port, final int idx, final int priority) {
|
||||||
|
super();
|
||||||
|
this.endpoint = new Endpoint(ip, port);
|
||||||
|
this.idx = idx;
|
||||||
|
this.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Endpoint getEndpoint() {
|
||||||
|
return this.endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIp() {
|
||||||
|
return this.endpoint.getIp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return this.endpoint.getPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIdx() {
|
||||||
|
return this.idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPriority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPriority(int priority) {
|
||||||
|
this.priority = priority;
|
||||||
|
this.str = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when ip is ANY_IP, port is zero and idx is zero too.
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return getIp().equals(Utils.IP_ANY) && getPort() == 0 && this.idx == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (this.str == null) {
|
||||||
|
final StringBuilder buf = new StringBuilder(this.endpoint.toString());
|
||||||
|
|
||||||
|
if (this.idx != 0) {
|
||||||
|
buf.append(':').append(this.idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.priority != ElectionPriority.Disabled) {
|
||||||
|
if (this.idx == 0) {
|
||||||
|
buf.append(':');
|
||||||
|
}
|
||||||
|
buf.append(':').append(this.priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.str = buf.toString();
|
||||||
|
}
|
||||||
|
return this.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse peerId from string that generated by {@link #toString()}
|
||||||
|
* This method can support parameter string values are below:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* PeerId.parse("a:b") = new PeerId("a", "b", 0 , -1)
|
||||||
|
* PeerId.parse("a:b:c") = new PeerId("a", "b", "c", -1)
|
||||||
|
* PeerId.parse("a:b::d") = new PeerId("a", "b", 0, "d")
|
||||||
|
* PeerId.parse("a:b:c:d") = new PeerId("a", "b", "c", "d")
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public boolean parse(final String s) {
|
||||||
|
if (StringUtils.isEmpty(s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] tmps = Utils.parsePeerId(s);
|
||||||
|
if (tmps.length < 2 || tmps.length > 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final int port = Integer.parseInt(tmps[1]);
|
||||||
|
this.endpoint = new Endpoint(tmps[0], port);
|
||||||
|
|
||||||
|
switch (tmps.length) {
|
||||||
|
case 3:
|
||||||
|
this.idx = Integer.parseInt(tmps[2]);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
if (tmps[2].equals("")) {
|
||||||
|
this.idx = 0;
|
||||||
|
} else {
|
||||||
|
this.idx = Integer.parseInt(tmps[2]);
|
||||||
|
}
|
||||||
|
this.priority = Integer.parseInt(tmps[3]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.str = null;
|
||||||
|
return true;
|
||||||
|
} catch (final Exception e) {
|
||||||
|
LOG.error("Parse peer from string failed: {}.", s, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To judge whether this node can participate in election or not.
|
||||||
|
*
|
||||||
|
* @return the restul that whether this node can participate in election or not.
|
||||||
|
*/
|
||||||
|
public boolean isPriorityNotElected() {
|
||||||
|
return this.priority == ElectionPriority.NotElected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To judge whether the priority election function is disabled or not in this node.
|
||||||
|
*
|
||||||
|
* @return the result that whether this node has priority election function or not.
|
||||||
|
*/
|
||||||
|
public boolean isPriorityDisabled() {
|
||||||
|
return this.priority <= ElectionPriority.Disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + (this.endpoint == null ? 0 : this.endpoint.hashCode());
|
||||||
|
result = prime * result + this.idx;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final PeerId other = (PeerId) obj;
|
||||||
|
if (this.endpoint == null) {
|
||||||
|
if (other.endpoint != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!this.endpoint.equals(other.endpoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.idx == other.idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.closure.ReadIndexClosure;
|
||||||
|
import com.alipay.sofa.jraft.util.Bytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReadIndex state
|
||||||
|
* @author dennis
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ReadIndexState {
|
||||||
|
|
||||||
|
/** The committed log index*/
|
||||||
|
private long index = -1;
|
||||||
|
/** User request context*/
|
||||||
|
private final Bytes requestContext;
|
||||||
|
/** User ReadIndex closure*/
|
||||||
|
private final ReadIndexClosure done;
|
||||||
|
/** Request start timestamp*/
|
||||||
|
private final long startTimeMs;
|
||||||
|
|
||||||
|
public ReadIndexState(Bytes requestContext, ReadIndexClosure done, long startTimeMs) {
|
||||||
|
super();
|
||||||
|
this.requestContext = requestContext;
|
||||||
|
this.done = done;
|
||||||
|
this.startTimeMs = startTimeMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getStartTimeMs() {
|
||||||
|
return startTimeMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(long index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bytes getRequestContext() {
|
||||||
|
return requestContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadIndexClosure getDone() {
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.rpc.RpcRequests.ReadIndexRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReadIndex requests statuses.
|
||||||
|
* @author dennis
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ReadIndexStatus {
|
||||||
|
|
||||||
|
private final ReadIndexRequest request; // raw request
|
||||||
|
private final List<ReadIndexState> states; // read index requests in batch.
|
||||||
|
private final long index; // committed log index.
|
||||||
|
|
||||||
|
public ReadIndexStatus(List<ReadIndexState> states, ReadIndexRequest request, long index) {
|
||||||
|
super();
|
||||||
|
this.index = index;
|
||||||
|
this.request = request;
|
||||||
|
this.states = states;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isApplied(long appliedIndex) {
|
||||||
|
return appliedIndex >= this.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOverMaxReadIndexLag(long applyIndex, int maxReadIndexLag) {
|
||||||
|
if (maxReadIndexLag < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.index - applyIndex > maxReadIndexLag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadIndexRequest getRequest() {
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ReadIndexState> getStates() {
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.closure.JoinableClosure;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic message structure of jraft, contains:
|
||||||
|
* <ul>
|
||||||
|
* <li>data: associated task data</li>
|
||||||
|
* <li>done: task closure, called when the data is successfully committed to the raft group.</li>
|
||||||
|
* <li>expectedTerm: Reject this task if expectedTerm doesn't match the current term of this Node if the value is not -1, default is -1.</li>
|
||||||
|
* </ul>
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-13 3:08:12 PM
|
||||||
|
*/
|
||||||
|
public class Task implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2971309899898274575L;
|
||||||
|
|
||||||
|
/** Associated task data*/
|
||||||
|
private ByteBuffer data = LogEntry.EMPTY_DATA;
|
||||||
|
/** task closure, called when the data is successfully committed to the raft group or failures happen.*/
|
||||||
|
private Closure done;
|
||||||
|
/** Reject this task if expectedTerm doesn't match the current term of this Node if the value is not -1, default is -1.*/
|
||||||
|
private long expectedTerm = -1;
|
||||||
|
|
||||||
|
public Task() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a task with data/done.
|
||||||
|
*/
|
||||||
|
public Task(final ByteBuffer data, final Closure done) {
|
||||||
|
super();
|
||||||
|
this.data = data;
|
||||||
|
this.done = done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a task with data/done/expectedTerm.
|
||||||
|
*/
|
||||||
|
public Task(final ByteBuffer data, final Closure done, final long expectedTerm) {
|
||||||
|
super();
|
||||||
|
this.data = data;
|
||||||
|
this.done = done;
|
||||||
|
this.expectedTerm = expectedTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteBuffer getData() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(final ByteBuffer data) {
|
||||||
|
Requires.requireNonNull(data, "data should not be null, you can use LogEntry.EMPTY_DATA instead.");
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Closure getDone() {
|
||||||
|
return this.done;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDone(final Closure done) {
|
||||||
|
this.done = done;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getExpectedTerm() {
|
||||||
|
return this.expectedTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpectedTerm(final long expectedTerm) {
|
||||||
|
this.expectedTerm = expectedTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waiting for the task to complete, to note that throughput may be reduced,
|
||||||
|
* which is generally not recommended.
|
||||||
|
*
|
||||||
|
* @return done closure
|
||||||
|
* @throws InterruptedException if the current thread is interrupted while waiting
|
||||||
|
* @since 1.3.1
|
||||||
|
*/
|
||||||
|
public Closure join() throws InterruptedException {
|
||||||
|
final JoinableClosure joinable = castToJoinalbe(this.done);
|
||||||
|
joinable.join();
|
||||||
|
return joinable.getClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waiting for the task to complete with a timeout millis, to note that throughput
|
||||||
|
* may be reduced, which is generally not recommended.
|
||||||
|
*
|
||||||
|
* @param timeoutMillis the maximum millis to wait
|
||||||
|
* @return done closure
|
||||||
|
* @throws InterruptedException if the current thread is interrupted while waiting
|
||||||
|
* @throws TimeoutException if timeout
|
||||||
|
* @since 1.3.1
|
||||||
|
*/
|
||||||
|
public Closure join(final long timeoutMillis) throws InterruptedException, TimeoutException {
|
||||||
|
final JoinableClosure joinable = castToJoinalbe(this.done);
|
||||||
|
joinable.join(timeoutMillis);
|
||||||
|
return joinable.getClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waiting for all tasks to complete.
|
||||||
|
*
|
||||||
|
* @param tasks task list
|
||||||
|
* @return the closure list in tasks
|
||||||
|
* @throws InterruptedException if the current thread is interrupted while waiting
|
||||||
|
* @since 1.3.1
|
||||||
|
*/
|
||||||
|
public static List<Closure> joinAll(final List<Task> tasks) throws InterruptedException {
|
||||||
|
final List<Closure> closures = new ArrayList<>(tasks.size());
|
||||||
|
for (final Task t : tasks) {
|
||||||
|
closures.add(t.join());
|
||||||
|
}
|
||||||
|
return closures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waiting for all tasks to complete with a timeout millis.
|
||||||
|
*
|
||||||
|
* @param tasks task list
|
||||||
|
* @param timeoutMillis the maximum millis to wait
|
||||||
|
* @return the closure list in the tasks
|
||||||
|
* @throws InterruptedException if the current thread is interrupted while waiting
|
||||||
|
* @throws TimeoutException if timeout
|
||||||
|
* @since 1.3.1
|
||||||
|
*/
|
||||||
|
public static List<Closure> joinAll(final List<Task> tasks, long timeoutMillis) throws InterruptedException,
|
||||||
|
TimeoutException {
|
||||||
|
final List<Closure> closures = new ArrayList<>(tasks.size());
|
||||||
|
for (final Task t : tasks) {
|
||||||
|
final long start = System.nanoTime();
|
||||||
|
closures.add(t.join(timeoutMillis));
|
||||||
|
timeoutMillis -= TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
|
||||||
|
if (timeoutMillis <= 0) {
|
||||||
|
throw new TimeoutException("joined timeout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closures;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JoinableClosure castToJoinalbe(final Closure closure) {
|
||||||
|
if (closure instanceof JoinableClosure) {
|
||||||
|
return (JoinableClosure) closure;
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException("Unsupported join");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User log entry.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 10:03:28 AM
|
||||||
|
*/
|
||||||
|
public class UserLog {
|
||||||
|
|
||||||
|
/** log index*/
|
||||||
|
private long index;
|
||||||
|
/** log data*/
|
||||||
|
private ByteBuffer data;
|
||||||
|
|
||||||
|
public UserLog(long index, ByteBuffer data) {
|
||||||
|
super();
|
||||||
|
this.index = index;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getIndex() {
|
||||||
|
return this.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(long index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteBuffer getData() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(ByteBuffer data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.data.clear();
|
||||||
|
this.index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "UserLog [index=" + this.index + ", data=" + this.data + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity.codec;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.LogEntry;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.v1.V1Decoder;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.v2.LogEntryV2CodecFactory;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.v2.V2Decoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decoder that supports both v1 and v2 log entry codec protocol.
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AutoDetectDecoder implements LogEntryDecoder {
|
||||||
|
|
||||||
|
private AutoDetectDecoder() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final AutoDetectDecoder INSTANCE = new AutoDetectDecoder();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogEntry decode(final byte[] bs) {
|
||||||
|
if (bs == null || bs.length < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs[0] == LogEntryV2CodecFactory.MAGIC_BYTES[0]) {
|
||||||
|
return V2Decoder.INSTANCE.decode(bs);
|
||||||
|
} else {
|
||||||
|
return V1Decoder.INSTANCE.decode(bs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity.codec;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.LogEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default log entry codec factory
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class DefaultLogEntryCodecFactory implements LogEntryCodecFactory {
|
||||||
|
|
||||||
|
private DefaultLogEntryCodecFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DefaultLogEntryCodecFactory INSTANCE = new DefaultLogEntryCodecFactory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a singleton instance of DefaultLogEntryCodecFactory.
|
||||||
|
* @return a singleton instance
|
||||||
|
*/
|
||||||
|
public static DefaultLogEntryCodecFactory getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private static LogEntryEncoder ENCODER = LogEntry::encode;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private static LogEntryDecoder DECODER = bs -> {
|
||||||
|
final LogEntry log = new LogEntry();
|
||||||
|
if (log.decode(bs)) {
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogEntryEncoder encoder() {
|
||||||
|
return ENCODER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogEntryDecoder decoder() {
|
||||||
|
return DECODER;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity.codec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log entry codec factory to create encoder/decoder for LogEntry.
|
||||||
|
*
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
* @since 1.2.6
|
||||||
|
*/
|
||||||
|
public interface LogEntryCodecFactory {
|
||||||
|
/**
|
||||||
|
* Returns a log entry encoder.
|
||||||
|
* @return encoder instance
|
||||||
|
*/
|
||||||
|
LogEntryEncoder encoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a log entry decoder.
|
||||||
|
* @return encoder instance
|
||||||
|
*/
|
||||||
|
LogEntryDecoder decoder();
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity.codec;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.LogEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log entry decoder
|
||||||
|
*
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
* @since 1.2.6
|
||||||
|
*/
|
||||||
|
public interface LogEntryDecoder {
|
||||||
|
/**
|
||||||
|
* Decode a log entry from byte array,
|
||||||
|
* return null when fail to decode.
|
||||||
|
* @param bs
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
LogEntry decode(byte[] bs);
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity.codec;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.LogEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log entry encoder
|
||||||
|
*
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
* @since 1.2.6
|
||||||
|
*/
|
||||||
|
public interface LogEntryEncoder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a log entry into a byte array.
|
||||||
|
* @param log log entry
|
||||||
|
* @return encoded byte array
|
||||||
|
*/
|
||||||
|
byte[] encode(LogEntry log);
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity.codec.v1;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryCodecFactory;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryDecoder;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Old V1 log entry codec implementation.
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class LogEntryV1CodecFactory implements LogEntryCodecFactory {
|
||||||
|
|
||||||
|
//"Beeep boop beep beep boop beeeeeep" -BB8
|
||||||
|
public static final byte MAGIC = (byte) 0xB8;
|
||||||
|
|
||||||
|
private LogEntryV1CodecFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final LogEntryV1CodecFactory INSTANCE = new LogEntryV1CodecFactory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a singleton instance of DefaultLogEntryCodecFactory.
|
||||||
|
* @return a singleton instance
|
||||||
|
*/
|
||||||
|
public static LogEntryV1CodecFactory getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogEntryEncoder encoder() {
|
||||||
|
return V1Encoder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogEntryDecoder decoder() {
|
||||||
|
return V1Decoder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity.codec.v1;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.EnumOutter;
|
||||||
|
import com.alipay.sofa.jraft.entity.LogEntry;
|
||||||
|
import com.alipay.sofa.jraft.entity.LogId;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryDecoder;
|
||||||
|
import com.alipay.sofa.jraft.util.AsciiStringUtil;
|
||||||
|
import com.alipay.sofa.jraft.util.Bits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* V1 log entry decoder
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public final class V1Decoder implements LogEntryDecoder {
|
||||||
|
|
||||||
|
private V1Decoder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final V1Decoder INSTANCE = new V1Decoder();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogEntry decode(final byte[] content) {
|
||||||
|
if (content == null || content.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (content[0] != LogEntryV1CodecFactory.MAGIC) {
|
||||||
|
// Corrupted log
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LogEntry log = new LogEntry();
|
||||||
|
decode(log, content);
|
||||||
|
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decode(final LogEntry log, final byte[] content) {
|
||||||
|
// 1-5 type
|
||||||
|
final int iType = Bits.getInt(content, 1);
|
||||||
|
log.setType(EnumOutter.EntryType.forNumber(iType));
|
||||||
|
// 5-13 index
|
||||||
|
// 13-21 term
|
||||||
|
final long index = Bits.getLong(content, 5);
|
||||||
|
final long term = Bits.getLong(content, 13);
|
||||||
|
log.setId(new LogId(index, term));
|
||||||
|
// 21-25 peer count
|
||||||
|
int peerCount = Bits.getInt(content, 21);
|
||||||
|
// peers
|
||||||
|
int pos = 25;
|
||||||
|
if (peerCount > 0) {
|
||||||
|
List<PeerId> peers = new ArrayList<>(peerCount);
|
||||||
|
while (peerCount-- > 0) {
|
||||||
|
final short len = Bits.getShort(content, pos);
|
||||||
|
final byte[] bs = new byte[len];
|
||||||
|
System.arraycopy(content, pos + 2, bs, 0, len);
|
||||||
|
// peer len (short in 2 bytes)
|
||||||
|
// peer str
|
||||||
|
pos += 2 + len;
|
||||||
|
final PeerId peer = new PeerId();
|
||||||
|
peer.parse(AsciiStringUtil.unsafeDecode(bs));
|
||||||
|
peers.add(peer);
|
||||||
|
}
|
||||||
|
log.setPeers(peers);
|
||||||
|
}
|
||||||
|
// old peers
|
||||||
|
int oldPeerCount = Bits.getInt(content, pos);
|
||||||
|
pos += 4;
|
||||||
|
if (oldPeerCount > 0) {
|
||||||
|
List<PeerId> oldPeers = new ArrayList<>(oldPeerCount);
|
||||||
|
while (oldPeerCount-- > 0) {
|
||||||
|
final short len = Bits.getShort(content, pos);
|
||||||
|
final byte[] bs = new byte[len];
|
||||||
|
System.arraycopy(content, pos + 2, bs, 0, len);
|
||||||
|
// peer len (short in 2 bytes)
|
||||||
|
// peer str
|
||||||
|
pos += 2 + len;
|
||||||
|
final PeerId peer = new PeerId();
|
||||||
|
peer.parse(AsciiStringUtil.unsafeDecode(bs));
|
||||||
|
oldPeers.add(peer);
|
||||||
|
}
|
||||||
|
log.setOldPeers(oldPeers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// data
|
||||||
|
if (content.length > pos) {
|
||||||
|
final int len = content.length - pos;
|
||||||
|
ByteBuffer data = ByteBuffer.allocate(len);
|
||||||
|
data.put(content, pos, len);
|
||||||
|
data.flip();
|
||||||
|
log.setData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity.codec.v1;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.EnumOutter.EntryType;
|
||||||
|
import com.alipay.sofa.jraft.entity.LogEntry;
|
||||||
|
import com.alipay.sofa.jraft.entity.LogId;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryEncoder;
|
||||||
|
import com.alipay.sofa.jraft.util.AsciiStringUtil;
|
||||||
|
import com.alipay.sofa.jraft.util.Bits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* V1 log entry encoder
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public final class V1Encoder implements LogEntryEncoder {
|
||||||
|
|
||||||
|
private V1Encoder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final LogEntryEncoder INSTANCE = new V1Encoder();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] encode(final LogEntry log) {
|
||||||
|
if (log.hasLearners()) {
|
||||||
|
throw new IllegalArgumentException("V1 log entry encoder doesn't support learners");
|
||||||
|
}
|
||||||
|
EntryType type = log.getType();
|
||||||
|
LogId id = log.getId();
|
||||||
|
List<PeerId> peers = log.getPeers();
|
||||||
|
List<PeerId> oldPeers = log.getOldPeers();
|
||||||
|
ByteBuffer data = log.getData();
|
||||||
|
|
||||||
|
// magic number 1 byte
|
||||||
|
int totalLen = 1;
|
||||||
|
final int iType = type.getNumber();
|
||||||
|
final long index = id.getIndex();
|
||||||
|
final long term = id.getTerm();
|
||||||
|
// type(4) + index(8) + term(8)
|
||||||
|
totalLen += 4 + 8 + 8;
|
||||||
|
int peerCount = 0;
|
||||||
|
// peer count
|
||||||
|
totalLen += 4;
|
||||||
|
final List<String> peerStrs = new ArrayList<>(peerCount);
|
||||||
|
if (peers != null) {
|
||||||
|
peerCount = peers.size();
|
||||||
|
for (final PeerId peer : peers) {
|
||||||
|
final String peerStr = peer.toString();
|
||||||
|
// peer len (short in 2 bytes)
|
||||||
|
// peer str
|
||||||
|
totalLen += 2 + peerStr.length();
|
||||||
|
peerStrs.add(peerStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int oldPeerCount = 0;
|
||||||
|
// old peer count
|
||||||
|
totalLen += 4;
|
||||||
|
final List<String> oldPeerStrs = new ArrayList<>(oldPeerCount);
|
||||||
|
if (oldPeers != null) {
|
||||||
|
oldPeerCount = oldPeers.size();
|
||||||
|
for (final PeerId peer : oldPeers) {
|
||||||
|
final String peerStr = peer.toString();
|
||||||
|
// peer len (short in 2 bytes)
|
||||||
|
// peer str
|
||||||
|
totalLen += 2 + peerStr.length();
|
||||||
|
oldPeerStrs.add(peerStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final int bodyLen = data != null ? data.remaining() : 0;
|
||||||
|
totalLen += bodyLen;
|
||||||
|
|
||||||
|
final byte[] content = new byte[totalLen];
|
||||||
|
// {0} magic
|
||||||
|
content[0] = LogEntryV1CodecFactory.MAGIC;
|
||||||
|
// 1-5 type
|
||||||
|
Bits.putInt(content, 1, iType);
|
||||||
|
// 5-13 index
|
||||||
|
Bits.putLong(content, 5, index);
|
||||||
|
// 13-21 term
|
||||||
|
Bits.putLong(content, 13, term);
|
||||||
|
// peers
|
||||||
|
// 21-25 peer count
|
||||||
|
Bits.putInt(content, 21, peerCount);
|
||||||
|
int pos = 25;
|
||||||
|
for (final String peerStr : peerStrs) {
|
||||||
|
final byte[] ps = AsciiStringUtil.unsafeEncode(peerStr);
|
||||||
|
Bits.putShort(content, pos, (short) peerStr.length());
|
||||||
|
System.arraycopy(ps, 0, content, pos + 2, ps.length);
|
||||||
|
pos += 2 + ps.length;
|
||||||
|
}
|
||||||
|
// old peers
|
||||||
|
// old peers count
|
||||||
|
Bits.putInt(content, pos, oldPeerCount);
|
||||||
|
pos += 4;
|
||||||
|
for (final String peerStr : oldPeerStrs) {
|
||||||
|
final byte[] ps = AsciiStringUtil.unsafeEncode(peerStr);
|
||||||
|
Bits.putShort(content, pos, (short) peerStr.length());
|
||||||
|
System.arraycopy(ps, 0, content, pos + 2, ps.length);
|
||||||
|
pos += 2 + ps.length;
|
||||||
|
}
|
||||||
|
// data
|
||||||
|
if (data != null) {
|
||||||
|
System.arraycopy(data.array(), data.position(), content, pos, data.remaining());
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity.codec.v2;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.AutoDetectDecoder;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryCodecFactory;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryDecoder;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* V2(Now) log entry codec implementation, header format:
|
||||||
|
*
|
||||||
|
* 0 1 2 3 4 5
|
||||||
|
* +-+-+-+-+-+-+-+-++-+-+-+
|
||||||
|
* |Magic|Version|Reserved|
|
||||||
|
* +-+-+-+-+-+-+-+-++-+-+-+
|
||||||
|
*
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
* @since 1.2.6
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LogEntryV2CodecFactory implements LogEntryCodecFactory {
|
||||||
|
|
||||||
|
private static final LogEntryV2CodecFactory INSTANCE = new LogEntryV2CodecFactory();
|
||||||
|
|
||||||
|
public static LogEntryV2CodecFactory getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BB-8 and R2D2 are good friends.
|
||||||
|
public static final byte[] MAGIC_BYTES = new byte[] { (byte) 0xBB, (byte) 0xD2 };
|
||||||
|
// Codec version
|
||||||
|
public static final byte VERSION = 1;
|
||||||
|
|
||||||
|
public static final byte[] RESERVED = new byte[3];
|
||||||
|
|
||||||
|
public static final int HEADER_SIZE = MAGIC_BYTES.length + 1 + RESERVED.length;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogEntryEncoder encoder() {
|
||||||
|
return V2Encoder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogEntryDecoder decoder() {
|
||||||
|
return AutoDetectDecoder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LogEntryV2CodecFactory() {
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity.codec.v2;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.JRaftUtils;
|
||||||
|
import com.alipay.sofa.jraft.entity.LogEntry;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryDecoder;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.v2.LogOutter.PBLogEntry;
|
||||||
|
import com.alipay.sofa.jraft.util.AsciiStringUtil;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
import com.google.protobuf.ZeroByteStringHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* V2 log entry decoder based on protobuf, see src/main/resources/log.proto
|
||||||
|
*
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*/
|
||||||
|
public class V2Decoder implements LogEntryDecoder {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(V2Decoder.class);
|
||||||
|
|
||||||
|
public static final V2Decoder INSTANCE = new V2Decoder();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LogEntry decode(final byte[] bs) {
|
||||||
|
if (bs == null || bs.length < LogEntryV2CodecFactory.HEADER_SIZE) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (; i < LogEntryV2CodecFactory.MAGIC_BYTES.length; i++) {
|
||||||
|
if (bs[i] != LogEntryV2CodecFactory.MAGIC_BYTES[i]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs[i++] != LogEntryV2CodecFactory.VERSION) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Ignored reserved
|
||||||
|
i += LogEntryV2CodecFactory.RESERVED.length;
|
||||||
|
try {
|
||||||
|
final PBLogEntry entry = PBLogEntry.parseFrom(ZeroByteStringHelper.wrap(bs, i, bs.length - i));
|
||||||
|
|
||||||
|
final LogEntry log = new LogEntry();
|
||||||
|
log.setType(entry.getType());
|
||||||
|
log.getId().setIndex(entry.getIndex());
|
||||||
|
log.getId().setTerm(entry.getTerm());
|
||||||
|
|
||||||
|
if (entry.hasChecksum()) {
|
||||||
|
log.setChecksum(entry.getChecksum());
|
||||||
|
}
|
||||||
|
if (entry.getPeersCount() > 0) {
|
||||||
|
final List<PeerId> peers = new ArrayList<>(entry.getPeersCount());
|
||||||
|
for (final ByteString bstring : entry.getPeersList()) {
|
||||||
|
peers.add(JRaftUtils.getPeerId(AsciiStringUtil.unsafeDecode(bstring)));
|
||||||
|
}
|
||||||
|
log.setPeers(peers);
|
||||||
|
}
|
||||||
|
if (entry.getOldPeersCount() > 0) {
|
||||||
|
final List<PeerId> peers = new ArrayList<>(entry.getOldPeersCount());
|
||||||
|
for (final ByteString bstring : entry.getOldPeersList()) {
|
||||||
|
peers.add(JRaftUtils.getPeerId(AsciiStringUtil.unsafeDecode(bstring)));
|
||||||
|
}
|
||||||
|
log.setOldPeers(peers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.getLearnersCount() > 0) {
|
||||||
|
final List<PeerId> peers = new ArrayList<>(entry.getLearnersCount());
|
||||||
|
for (final ByteString bstring : entry.getLearnersList()) {
|
||||||
|
peers.add(JRaftUtils.getPeerId(AsciiStringUtil.unsafeDecode(bstring)));
|
||||||
|
}
|
||||||
|
log.setLearners(peers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.getOldLearnersCount() > 0) {
|
||||||
|
final List<PeerId> peers = new ArrayList<>(entry.getOldLearnersCount());
|
||||||
|
for (final ByteString bstring : entry.getOldLearnersList()) {
|
||||||
|
peers.add(JRaftUtils.getPeerId(AsciiStringUtil.unsafeDecode(bstring)));
|
||||||
|
}
|
||||||
|
log.setOldLearners(peers);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ByteString data = entry.getData();
|
||||||
|
if (!data.isEmpty()) {
|
||||||
|
log.setData(ByteBuffer.wrap(ZeroByteStringHelper.getByteArray(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return log;
|
||||||
|
} catch (final InvalidProtocolBufferException e) {
|
||||||
|
LOG.error("Fail to decode pb log entry", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private V2Decoder() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.entity.codec.v2;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.entity.LogEntry;
|
||||||
|
import com.alipay.sofa.jraft.entity.LogId;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryEncoder;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.v2.LogOutter.PBLogEntry;
|
||||||
|
import com.alipay.sofa.jraft.error.LogEntryCorruptedException;
|
||||||
|
import com.alipay.sofa.jraft.util.AsciiStringUtil;
|
||||||
|
import com.alipay.sofa.jraft.util.Requires;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import com.google.protobuf.CodedOutputStream;
|
||||||
|
import com.google.protobuf.ZeroByteStringHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* V2 log entry encoder based on protobuf, see src/main/resources/log.proto
|
||||||
|
*
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*/
|
||||||
|
public class V2Encoder implements LogEntryEncoder {
|
||||||
|
|
||||||
|
public static final V2Encoder INSTANCE = new V2Encoder();
|
||||||
|
|
||||||
|
private static boolean hasPeers(final Collection<PeerId> peers) {
|
||||||
|
return peers != null && !peers.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void encodePeers(final PBLogEntry.Builder builder, final List<PeerId> peers) {
|
||||||
|
final int size = peers.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
builder.addPeers(ZeroByteStringHelper.wrap(AsciiStringUtil.unsafeEncode(peers.get(i).toString())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void encodeOldPeers(final PBLogEntry.Builder builder, final List<PeerId> peers) {
|
||||||
|
final int size = peers.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
builder.addOldPeers(ZeroByteStringHelper.wrap(AsciiStringUtil.unsafeEncode(peers.get(i).toString())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void encodeLearners(final PBLogEntry.Builder builder, final List<PeerId> learners) {
|
||||||
|
final int size = learners.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
builder.addLearners(ZeroByteStringHelper.wrap(AsciiStringUtil.unsafeEncode(learners.get(i).toString())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void encodeOldLearners(final PBLogEntry.Builder builder, final List<PeerId> learners) {
|
||||||
|
final int size = learners.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
builder.addOldLearners(ZeroByteStringHelper.wrap(AsciiStringUtil.unsafeEncode(learners.get(i).toString())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] encode(final LogEntry log) {
|
||||||
|
Requires.requireNonNull(log, "Null log");
|
||||||
|
|
||||||
|
final LogId logId = log.getId();
|
||||||
|
final PBLogEntry.Builder builder = PBLogEntry.newBuilder() //
|
||||||
|
.setType(log.getType()) //
|
||||||
|
.setIndex(logId.getIndex()) //
|
||||||
|
.setTerm(logId.getTerm());
|
||||||
|
|
||||||
|
final List<PeerId> peers = log.getPeers();
|
||||||
|
if (hasPeers(peers)) {
|
||||||
|
encodePeers(builder, peers);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<PeerId> oldPeers = log.getOldPeers();
|
||||||
|
if (hasPeers(oldPeers)) {
|
||||||
|
encodeOldPeers(builder, oldPeers);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<PeerId> learners = log.getLearners();
|
||||||
|
if (hasPeers(learners)) {
|
||||||
|
encodeLearners(builder, learners);
|
||||||
|
}
|
||||||
|
final List<PeerId> oldLearners = log.getOldLearners();
|
||||||
|
if (hasPeers(oldLearners)) {
|
||||||
|
encodeOldLearners(builder, oldLearners);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log.hasChecksum()) {
|
||||||
|
builder.setChecksum(log.getChecksum());
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setData(log.getData() != null ? ZeroByteStringHelper.wrap(log.getData()) : ByteString.EMPTY);
|
||||||
|
|
||||||
|
final PBLogEntry pbLogEntry = builder.build();
|
||||||
|
final int bodyLen = pbLogEntry.getSerializedSize();
|
||||||
|
final byte[] ret = new byte[LogEntryV2CodecFactory.HEADER_SIZE + bodyLen];
|
||||||
|
|
||||||
|
// write header
|
||||||
|
int i = 0;
|
||||||
|
for (; i < LogEntryV2CodecFactory.MAGIC_BYTES.length; i++) {
|
||||||
|
ret[i] = LogEntryV2CodecFactory.MAGIC_BYTES[i];
|
||||||
|
}
|
||||||
|
ret[i++] = LogEntryV2CodecFactory.VERSION;
|
||||||
|
// avoid memory copy for only 3 bytes
|
||||||
|
for (; i < LogEntryV2CodecFactory.HEADER_SIZE; i++) {
|
||||||
|
ret[i] = LogEntryV2CodecFactory.RESERVED[i - LogEntryV2CodecFactory.MAGIC_BYTES.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// write body
|
||||||
|
writeToByteArray(pbLogEntry, ret, i, bodyLen);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeToByteArray(final PBLogEntry pbLogEntry, final byte[] array, final int offset, final int len) {
|
||||||
|
final CodedOutputStream output = CodedOutputStream.newInstance(array, offset, len);
|
||||||
|
try {
|
||||||
|
pbLogEntry.writeTo(output);
|
||||||
|
output.checkNoSpaceLeft();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
throw new LogEntryCorruptedException(
|
||||||
|
"Serializing PBLogEntry to a byte array threw an IOException (should never happen).", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private V2Encoder() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author jiachun.fjc
|
||||||
|
*/
|
||||||
|
public class InvokeTimeoutException extends RemotingException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -4710810309766380565L;
|
||||||
|
|
||||||
|
public InvokeTimeoutException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeTimeoutException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeTimeoutException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeTimeoutException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvokeTimeoutException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jiachun.fjc
|
||||||
|
*/
|
||||||
|
public class JRaftException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 0L;
|
||||||
|
|
||||||
|
public JRaftException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public JRaftException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JRaftException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JRaftException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JRaftException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LogEntry corrupted exception.
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LogEntryCorruptedException extends JRaftException {
|
||||||
|
private static final long serialVersionUID = 5664520219607766929L;
|
||||||
|
|
||||||
|
public LogEntryCorruptedException() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogEntryCorruptedException(final String message, final Throwable cause, final boolean enableSuppression,
|
||||||
|
final boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogEntryCorruptedException(final String message, final Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogEntryCorruptedException(final String message) {
|
||||||
|
super(message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogEntryCorruptedException(final Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log index out of bounds
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-23 3:06:38 PM
|
||||||
|
*/
|
||||||
|
public class LogIndexOutOfBoundsException extends IndexOutOfBoundsException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -1096992049027390572L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an <code>LogIndexOutOfBoundsException</code> with no
|
||||||
|
* detail message.
|
||||||
|
*/
|
||||||
|
public LogIndexOutOfBoundsException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new <code>LogIndexOutOfBoundsException</code>
|
||||||
|
* class with an argument indicating the illegal index.
|
||||||
|
*
|
||||||
|
* @param index the illegal index.
|
||||||
|
*/
|
||||||
|
public LogIndexOutOfBoundsException(int index) {
|
||||||
|
super("Array index out of range: " + index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an <code>LogIndexOutOfBoundsException</code> class
|
||||||
|
* with the specified detail message.
|
||||||
|
*
|
||||||
|
* @param s the detail message.
|
||||||
|
*/
|
||||||
|
public LogIndexOutOfBoundsException(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log not found exception, the log may be deleted.
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-23 3:07:56 PM
|
||||||
|
*/
|
||||||
|
public class LogNotFoundException extends IllegalStateException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -140969527148366390L;
|
||||||
|
|
||||||
|
public LogNotFoundException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogNotFoundException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogNotFoundException(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogNotFoundException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protobuf message class not found exception.
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-19 5:17:38 PM
|
||||||
|
*/
|
||||||
|
public class MessageClassNotFoundException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 4684584394785943114L;
|
||||||
|
|
||||||
|
public MessageClassNotFoundException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageClassNotFoundException(String message, Throwable cause, boolean enableSuppression,
|
||||||
|
boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageClassNotFoundException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageClassNotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageClassNotFoundException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Threw when Node is overloaded.
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OverloadException extends JRaftException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -5505054326197103575L;
|
||||||
|
|
||||||
|
public OverloadException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OverloadException(final String message, final Throwable cause, final boolean enableSuppression,
|
||||||
|
final boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OverloadException(final String message, final Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OverloadException(final String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OverloadException(final Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,284 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.error;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raft error code.
|
||||||
|
*/
|
||||||
|
public enum RaftError {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unknown error
|
||||||
|
*/
|
||||||
|
UNKNOWN(-1),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Success, no error.
|
||||||
|
*/
|
||||||
|
SUCCESS(0),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* All Kinds of Timeout(Including Election_timeout, Timeout_now, Stepdown_timeout)
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>ERAFTTIMEDOUT = 10001;</code>
|
||||||
|
*/
|
||||||
|
ERAFTTIMEDOUT(10001),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Bad User State Machine
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>ESTATEMACHINE = 10002;</code>
|
||||||
|
*/
|
||||||
|
ESTATEMACHINE(10002),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Catchup Failed
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>ECATCHUP = 10003;</code>
|
||||||
|
*/
|
||||||
|
ECATCHUP(10003),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Trigger step_down(Not All)
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>ELEADERREMOVED = 10004;</code>
|
||||||
|
*/
|
||||||
|
ELEADERREMOVED(10004),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Leader Is Not In The New Configuration
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>ESETPEER = 10005;</code>
|
||||||
|
*/
|
||||||
|
ESETPEER(10005),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Shut_down
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>ENODESHUTDOWN = 10006;</code>
|
||||||
|
*/
|
||||||
|
ENODESHUTDOWN(10006),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Receive Higher Term Requests
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>EHIGHERTERMREQUEST = 10007;</code>
|
||||||
|
*/
|
||||||
|
EHIGHERTERMREQUEST(10007),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Receive Higher Term Response
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>EHIGHERTERMRESPONSE = 10008;</code>
|
||||||
|
*/
|
||||||
|
EHIGHERTERMRESPONSE(10008),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Node Is In Error
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>EBADNODE = 10009;</code>
|
||||||
|
*/
|
||||||
|
EBADNODE(10009),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Node Votes For Some Candidate
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>EVOTEFORCANDIDATE = 10010;</code>
|
||||||
|
*/
|
||||||
|
EVOTEFORCANDIDATE(10010),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Follower(without leader) or Candidate Receives
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>ENEWLEADER = 10011;</code>
|
||||||
|
*/
|
||||||
|
ENEWLEADER(10011),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Append_entries/Install_snapshot Request from a new leader
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>ELEADERCONFLICT = 10012;</code>
|
||||||
|
*/
|
||||||
|
ELEADERCONFLICT(10012),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Trigger on_leader_stop
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>ETRANSFERLEADERSHIP = 10013;</code>
|
||||||
|
*/
|
||||||
|
ETRANSFERLEADERSHIP(10013),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* The log at the given index is deleted
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>ELOGDELETED = 10014;</code>
|
||||||
|
*/
|
||||||
|
ELOGDELETED(10014),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* No available user log to read
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* <code>ENOMOREUSERLOG = 10015;</code>
|
||||||
|
*/
|
||||||
|
ENOMOREUSERLOG(10015),
|
||||||
|
|
||||||
|
/* other non-raft error codes 1000~10000 */
|
||||||
|
/**
|
||||||
|
* Invalid rpc request
|
||||||
|
*/
|
||||||
|
EREQUEST(1000),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task is stopped
|
||||||
|
*/
|
||||||
|
ESTOP(1001),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retry again
|
||||||
|
*/
|
||||||
|
EAGAIN(1002),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interrupted
|
||||||
|
*/
|
||||||
|
EINTR(1003),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal exception
|
||||||
|
*/
|
||||||
|
EINTERNAL(1004),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task is canceled
|
||||||
|
*/
|
||||||
|
ECANCELED(1005),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Host is down
|
||||||
|
*/
|
||||||
|
EHOSTDOWN(1006),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service is shutdown
|
||||||
|
*/
|
||||||
|
ESHUTDOWN(1007),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission issue
|
||||||
|
*/
|
||||||
|
EPERM(1008),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server is in busy state
|
||||||
|
*/
|
||||||
|
EBUSY(1009),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timed out
|
||||||
|
*/
|
||||||
|
ETIMEDOUT(1010),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data is stale
|
||||||
|
*/
|
||||||
|
ESTALE(1011),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Something not found
|
||||||
|
*/
|
||||||
|
ENOENT(1012),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File/folder already exists
|
||||||
|
*/
|
||||||
|
EEXISTS(1013),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IO error
|
||||||
|
*/
|
||||||
|
EIO(1014),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid value.
|
||||||
|
*/
|
||||||
|
EINVAL(1015),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission denied
|
||||||
|
*/
|
||||||
|
EACCES(1016);
|
||||||
|
|
||||||
|
private static final Map<Integer, RaftError> RAFT_ERROR_MAP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (final RaftError error : RaftError.values()) {
|
||||||
|
RAFT_ERROR_MAP.put(error.getNumber(), error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getNumber() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RaftError forNumber(final int value) {
|
||||||
|
return RAFT_ERROR_MAP.getOrDefault(value, UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String describeCode(final int code) {
|
||||||
|
RaftError e = forNumber(code);
|
||||||
|
return e != null ? e.name() : "<Unknown:" + code + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
RaftError(final int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.error;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Status;
|
||||||
|
import com.alipay.sofa.jraft.entity.EnumOutter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A raft exception.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-14 10:41:21 AM
|
||||||
|
*/
|
||||||
|
public class RaftException extends Throwable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -1533343555230409704L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error type
|
||||||
|
*/
|
||||||
|
private EnumOutter.ErrorType type;
|
||||||
|
/** Error status*/
|
||||||
|
private Status status = Status.OK();
|
||||||
|
|
||||||
|
public RaftException(EnumOutter.ErrorType type) {
|
||||||
|
super(type.name());
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RaftException(EnumOutter.ErrorType type, Status status) {
|
||||||
|
super(status != null ? status.getErrorMsg() : type.name());
|
||||||
|
this.type = type;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RaftException(EnumOutter.ErrorType type, RaftError err, String fmt, Object... args) {
|
||||||
|
super(String.format(fmt, args));
|
||||||
|
this.type = type;
|
||||||
|
this.status = new Status(err, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RaftException() {
|
||||||
|
this.type = EnumOutter.ErrorType.ERROR_TYPE_NONE;
|
||||||
|
this.status = Status.OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumOutter.ErrorType getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(EnumOutter.ErrorType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status getStatus() {
|
||||||
|
return this.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(Status status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Error [type=" + this.type + ", status=" + this.status + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception for default remoting problems.
|
||||||
|
*
|
||||||
|
* @author jiachun.fjc
|
||||||
|
*/
|
||||||
|
public class RemotingException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -6326244159775972292L;
|
||||||
|
|
||||||
|
public RemotingException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemotingException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemotingException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemotingException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemotingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retry request exception without stack tracing.
|
||||||
|
*
|
||||||
|
* @author dennis
|
||||||
|
*/
|
||||||
|
public class RetryAgainException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 8690835003361525337L;
|
||||||
|
|
||||||
|
public RetryAgainException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RetryAgainException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RetryAgainException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RetryAgainException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RetryAgainException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Throwable fillInStackTrace() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply task in blocking or non-blocking mode.
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum ApplyTaskMode {
|
||||||
|
Blocking, NonBlocking
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.FSMCaller;
|
||||||
|
import com.alipay.sofa.jraft.closure.ClosureQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ballot box options.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 2:58:36 PM
|
||||||
|
*/
|
||||||
|
public class BallotBoxOptions {
|
||||||
|
|
||||||
|
private FSMCaller waiter;
|
||||||
|
private ClosureQueue closureQueue;
|
||||||
|
|
||||||
|
public FSMCaller getWaiter() {
|
||||||
|
return this.waiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWaiter(FSMCaller waiter) {
|
||||||
|
this.waiter = waiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClosureQueue getClosureQueue() {
|
||||||
|
return this.closureQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClosureQueue(ClosureQueue closureQueue) {
|
||||||
|
this.closureQueue = closureQueue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.JRaftServiceFactory;
|
||||||
|
import com.alipay.sofa.jraft.StateMachine;
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
import com.alipay.sofa.jraft.util.JRaftServiceLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrap options
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 2:58:45 PM
|
||||||
|
*/
|
||||||
|
public class BootstrapOptions {
|
||||||
|
|
||||||
|
public static final JRaftServiceFactory defaultServiceFactory = JRaftServiceLoader.load(JRaftServiceFactory.class) //
|
||||||
|
.first();
|
||||||
|
|
||||||
|
// Containing the initial member of this raft group
|
||||||
|
// Default: empty conf
|
||||||
|
private Configuration groupConf;
|
||||||
|
|
||||||
|
// The index of the last index which the dumping snapshot contains
|
||||||
|
// Default: 0
|
||||||
|
private long lastLogIndex = 0L;
|
||||||
|
|
||||||
|
// The specific StateMachine which is going to dump the first snapshot
|
||||||
|
// If last_log_index isn't 0, fsm must be a valid instance.
|
||||||
|
// Default: NULL
|
||||||
|
private StateMachine fsm;
|
||||||
|
|
||||||
|
// Describe a specific LogStorage in format ${type}://${parameters}
|
||||||
|
private String logUri;
|
||||||
|
|
||||||
|
// Describe a specific RaftMetaStorage in format ${type}://${parameters}
|
||||||
|
private String raftMetaUri;
|
||||||
|
|
||||||
|
// Describe a specific SnapshotStorage in format ${type}://${parameters}
|
||||||
|
private String snapshotUri;
|
||||||
|
|
||||||
|
// Whether to enable metrics for node.
|
||||||
|
private boolean enableMetrics = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom service factory.
|
||||||
|
*/
|
||||||
|
private JRaftServiceFactory serviceFactory = defaultServiceFactory;
|
||||||
|
|
||||||
|
public JRaftServiceFactory getServiceFactory() {
|
||||||
|
return serviceFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServiceFactory(JRaftServiceFactory serviceFactory) {
|
||||||
|
this.serviceFactory = serviceFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableMetrics(boolean enableMetrics) {
|
||||||
|
this.enableMetrics = enableMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnableMetrics() {
|
||||||
|
return enableMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Configuration getGroupConf() {
|
||||||
|
return this.groupConf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupConf(Configuration groupConf) {
|
||||||
|
this.groupConf = groupConf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastLogIndex() {
|
||||||
|
return this.lastLogIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastLogIndex(long lastLogIndex) {
|
||||||
|
this.lastLogIndex = lastLogIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateMachine getFsm() {
|
||||||
|
return this.fsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFsm(StateMachine fsm) {
|
||||||
|
this.fsm = fsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogUri() {
|
||||||
|
return this.logUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogUri(String logUri) {
|
||||||
|
this.logUri = logUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRaftMetaUri() {
|
||||||
|
return this.raftMetaUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRaftMetaUri(String raftMetaUri) {
|
||||||
|
this.raftMetaUri = raftMetaUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSnapshotUri() {
|
||||||
|
return this.snapshotUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnapshotUri(String snapshotUri) {
|
||||||
|
this.snapshotUri = snapshotUri;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cli service options.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-09 3:25:59 PM
|
||||||
|
*/
|
||||||
|
public class CliOptions extends RpcOptions {
|
||||||
|
|
||||||
|
private int timeoutMs;
|
||||||
|
private int maxRetry;
|
||||||
|
|
||||||
|
public int getTimeoutMs() {
|
||||||
|
return this.timeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeoutMs(int timeoutMs) {
|
||||||
|
this.timeoutMs = timeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxRetry() {
|
||||||
|
return this.maxRetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxRetry(int maxRetry) {
|
||||||
|
this.maxRetry = maxRetry;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copier options.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 2:58:53 PM
|
||||||
|
*/
|
||||||
|
public class CopyOptions {
|
||||||
|
|
||||||
|
private int maxRetry = 3;
|
||||||
|
private long retryIntervalMs = 1000L;
|
||||||
|
private int timeoutMs = 10 * 1000;
|
||||||
|
|
||||||
|
public int getMaxRetry() {
|
||||||
|
return this.maxRetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxRetry(int maxRetry) {
|
||||||
|
this.maxRetry = maxRetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRetryIntervalMs() {
|
||||||
|
return this.retryIntervalMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRetryIntervalMs(long retryIntervalMs) {
|
||||||
|
this.retryIntervalMs = retryIntervalMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTimeoutMs() {
|
||||||
|
return this.timeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeoutMs(int timeoutMs) {
|
||||||
|
this.timeoutMs = timeoutMs;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.Closure;
|
||||||
|
import com.alipay.sofa.jraft.StateMachine;
|
||||||
|
import com.alipay.sofa.jraft.closure.ClosureQueue;
|
||||||
|
import com.alipay.sofa.jraft.core.NodeImpl;
|
||||||
|
import com.alipay.sofa.jraft.entity.LogId;
|
||||||
|
import com.alipay.sofa.jraft.storage.LogManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FSM caller options.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 2:59:02 PM
|
||||||
|
*/
|
||||||
|
public class FSMCallerOptions {
|
||||||
|
private LogManager logManager;
|
||||||
|
private StateMachine fsm;
|
||||||
|
private Closure afterShutdown;
|
||||||
|
private LogId bootstrapId;
|
||||||
|
private ClosureQueue closureQueue;
|
||||||
|
private NodeImpl node;
|
||||||
|
/**
|
||||||
|
* disruptor buffer size.
|
||||||
|
*/
|
||||||
|
private int disruptorBufferSize = 1024;
|
||||||
|
|
||||||
|
public int getDisruptorBufferSize() {
|
||||||
|
return this.disruptorBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisruptorBufferSize(int disruptorBufferSize) {
|
||||||
|
this.disruptorBufferSize = disruptorBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeImpl getNode() {
|
||||||
|
return this.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNode(NodeImpl node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClosureQueue getClosureQueue() {
|
||||||
|
return this.closureQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClosureQueue(ClosureQueue closureQueue) {
|
||||||
|
this.closureQueue = closureQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogManager getLogManager() {
|
||||||
|
return this.logManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogManager(LogManager logManager) {
|
||||||
|
this.logManager = logManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateMachine getFsm() {
|
||||||
|
return this.fsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFsm(StateMachine fsm) {
|
||||||
|
this.fsm = fsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Closure getAfterShutdown() {
|
||||||
|
return this.afterShutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAfterShutdown(Closure afterShutdown) {
|
||||||
|
this.afterShutdown = afterShutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogId getBootstrapId() {
|
||||||
|
return this.bootstrapId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBootstrapId(LogId bootstrapId) {
|
||||||
|
this.bootstrapId = bootstrapId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.FSMCaller;
|
||||||
|
import com.alipay.sofa.jraft.conf.ConfigurationManager;
|
||||||
|
import com.alipay.sofa.jraft.core.NodeMetrics;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryCodecFactory;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.v2.LogEntryV2CodecFactory;
|
||||||
|
import com.alipay.sofa.jraft.storage.LogStorage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for log manager.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Mar-13 5:15:15 PM
|
||||||
|
*/
|
||||||
|
public class LogManagerOptions {
|
||||||
|
|
||||||
|
private LogStorage logStorage;
|
||||||
|
private ConfigurationManager configurationManager;
|
||||||
|
private FSMCaller fsmCaller;
|
||||||
|
private int disruptorBufferSize = 1024;
|
||||||
|
private RaftOptions raftOptions;
|
||||||
|
private NodeMetrics nodeMetrics;
|
||||||
|
private LogEntryCodecFactory logEntryCodecFactory = LogEntryV2CodecFactory.getInstance();
|
||||||
|
|
||||||
|
public LogEntryCodecFactory getLogEntryCodecFactory() {
|
||||||
|
return this.logEntryCodecFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogEntryCodecFactory(final LogEntryCodecFactory logEntryCodecFactory) {
|
||||||
|
this.logEntryCodecFactory = logEntryCodecFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeMetrics getNodeMetrics() {
|
||||||
|
return this.nodeMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeMetrics(final NodeMetrics nodeMetrics) {
|
||||||
|
this.nodeMetrics = nodeMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RaftOptions getRaftOptions() {
|
||||||
|
return this.raftOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRaftOptions(final RaftOptions raftOptions) {
|
||||||
|
this.raftOptions = raftOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDisruptorBufferSize() {
|
||||||
|
return this.disruptorBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisruptorBufferSize(final int disruptorBufferSize) {
|
||||||
|
this.disruptorBufferSize = disruptorBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogStorage getLogStorage() {
|
||||||
|
return this.logStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogStorage(final LogStorage logStorage) {
|
||||||
|
this.logStorage = logStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationManager getConfigurationManager() {
|
||||||
|
return this.configurationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfigurationManager(final ConfigurationManager configurationManager) {
|
||||||
|
this.configurationManager = configurationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FSMCaller getFsmCaller() {
|
||||||
|
return this.fsmCaller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFsmCaller(final FSMCaller fsmCaller) {
|
||||||
|
this.fsmCaller = fsmCaller;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.conf.ConfigurationManager;
|
||||||
|
import com.alipay.sofa.jraft.entity.codec.LogEntryCodecFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log storage initialize options
|
||||||
|
* @author boyan(boyan@antfin.com)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LogStorageOptions {
|
||||||
|
|
||||||
|
private ConfigurationManager configurationManager;
|
||||||
|
|
||||||
|
private LogEntryCodecFactory logEntryCodecFactory;
|
||||||
|
|
||||||
|
public ConfigurationManager getConfigurationManager() {
|
||||||
|
return this.configurationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfigurationManager(final ConfigurationManager configurationManager) {
|
||||||
|
this.configurationManager = configurationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogEntryCodecFactory getLogEntryCodecFactory() {
|
||||||
|
return this.logEntryCodecFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogEntryCodecFactory(final LogEntryCodecFactory logEntryCodecFactory) {
|
||||||
|
this.logEntryCodecFactory = logEntryCodecFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,458 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
import com.alipay.remoting.util.StringUtils;
|
||||||
|
import com.alipay.sofa.jraft.JRaftServiceFactory;
|
||||||
|
import com.alipay.sofa.jraft.StateMachine;
|
||||||
|
import com.alipay.sofa.jraft.conf.Configuration;
|
||||||
|
import com.alipay.sofa.jraft.core.ElectionPriority;
|
||||||
|
import com.alipay.sofa.jraft.storage.SnapshotThrottle;
|
||||||
|
import com.alipay.sofa.jraft.util.Copiable;
|
||||||
|
import com.alipay.sofa.jraft.util.JRaftServiceLoader;
|
||||||
|
import com.alipay.sofa.jraft.util.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node options.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-04 2:59:12 PM
|
||||||
|
*/
|
||||||
|
public class NodeOptions extends RpcOptions implements Copiable<NodeOptions> {
|
||||||
|
|
||||||
|
public static final JRaftServiceFactory defaultServiceFactory = JRaftServiceLoader.load(JRaftServiceFactory.class) //
|
||||||
|
.first();
|
||||||
|
|
||||||
|
// A follower would become a candidate if it doesn't receive any message
|
||||||
|
// from the leader in |election_timeout_ms| milliseconds
|
||||||
|
// Default: 1000 (1s)
|
||||||
|
private int electionTimeoutMs = 1000; // follower to candidate timeout
|
||||||
|
|
||||||
|
// One node's local priority value would be set to | electionPriority |
|
||||||
|
// value when it starts up.If this value is set to 0,the node will never be a leader.
|
||||||
|
// If this node doesn't support priority election,then set this value to -1.
|
||||||
|
// Default: -1
|
||||||
|
private int electionPriority = ElectionPriority.Disabled;
|
||||||
|
|
||||||
|
// If next leader is not elected until next election timeout, it exponentially
|
||||||
|
// decay its local target priority, for example target_priority = target_priority - gap
|
||||||
|
// Default: 10
|
||||||
|
private int decayPriorityGap = 10;
|
||||||
|
|
||||||
|
// Leader lease time's ratio of electionTimeoutMs,
|
||||||
|
// To minimize the effects of clock drift, we should make that:
|
||||||
|
// clockDrift + leaderLeaseTimeoutMs < electionTimeout
|
||||||
|
// Default: 90, Max: 100
|
||||||
|
private int leaderLeaseTimeRatio = 90;
|
||||||
|
|
||||||
|
// A snapshot saving would be triggered every |snapshot_interval_s| seconds
|
||||||
|
// if this was reset as a positive number
|
||||||
|
// If |snapshot_interval_s| <= 0, the time based snapshot would be disabled.
|
||||||
|
//
|
||||||
|
// Default: 3600 (1 hour)
|
||||||
|
private int snapshotIntervalSecs = 3600;
|
||||||
|
|
||||||
|
// A snapshot saving would be triggered every |snapshot_interval_s| seconds,
|
||||||
|
// and at this moment when state machine's lastAppliedIndex value
|
||||||
|
// minus lastSnapshotId value is greater than snapshotLogIndexMargin value,
|
||||||
|
// the snapshot action will be done really.
|
||||||
|
// If |snapshotLogIndexMargin| <= 0, the distance based snapshot would be disable.
|
||||||
|
//
|
||||||
|
// Default: 0
|
||||||
|
private int snapshotLogIndexMargin = 0;
|
||||||
|
|
||||||
|
// We will regard a adding peer as caught up if the margin between the
|
||||||
|
// last_log_index of this peer and the last_log_index of leader is less than
|
||||||
|
// |catchup_margin|
|
||||||
|
//
|
||||||
|
// Default: 1000
|
||||||
|
private int catchupMargin = 1000;
|
||||||
|
|
||||||
|
// If node is starting from a empty environment (both LogStorage and
|
||||||
|
// SnapshotStorage are empty), it would use |initial_conf| as the
|
||||||
|
// configuration of the group, otherwise it would load configuration from
|
||||||
|
// the existing environment.
|
||||||
|
//
|
||||||
|
// Default: A empty group
|
||||||
|
private Configuration initialConf = new Configuration();
|
||||||
|
|
||||||
|
// The specific StateMachine implemented your business logic, which must be
|
||||||
|
// a valid instance.
|
||||||
|
private StateMachine fsm;
|
||||||
|
|
||||||
|
// Describe a specific LogStorage in format ${type}://${parameters}
|
||||||
|
private String logUri;
|
||||||
|
|
||||||
|
// Describe a specific RaftMetaStorage in format ${type}://${parameters}
|
||||||
|
private String raftMetaUri;
|
||||||
|
|
||||||
|
// Describe a specific SnapshotStorage in format ${type}://${parameters}
|
||||||
|
private String snapshotUri;
|
||||||
|
|
||||||
|
// If enable, we will filter duplicate files before copy remote snapshot,
|
||||||
|
// to avoid useless transmission. Two files in local and remote are duplicate,
|
||||||
|
// only if they has the same filename and the same checksum (stored in file meta).
|
||||||
|
// Default: false
|
||||||
|
private boolean filterBeforeCopyRemote = false;
|
||||||
|
|
||||||
|
// If non-null, we will pass this throughput_snapshot_throttle to SnapshotExecutor
|
||||||
|
// Default: NULL
|
||||||
|
// scoped_refptr<SnapshotThrottle>* snapshot_throttle;
|
||||||
|
|
||||||
|
// If true, RPCs through raft_cli will be denied.
|
||||||
|
// Default: false
|
||||||
|
private boolean disableCli = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether use global timer pool, if true, the {@code timerPoolSize} will be invalid.
|
||||||
|
*/
|
||||||
|
private boolean sharedTimerPool = false;
|
||||||
|
/**
|
||||||
|
* Timer manager thread pool size
|
||||||
|
*/
|
||||||
|
private int timerPoolSize = Utils.cpus() * 3 > 20 ? 20 : Utils.cpus() * 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CLI service request RPC executor pool size, use default executor if -1.
|
||||||
|
*/
|
||||||
|
private int cliRpcThreadPoolSize = Utils.cpus();
|
||||||
|
/**
|
||||||
|
* RAFT request RPC executor pool size, use default executor if -1.
|
||||||
|
*/
|
||||||
|
private int raftRpcThreadPoolSize = Utils.cpus() * 6;
|
||||||
|
/**
|
||||||
|
* Whether to enable metrics for node.
|
||||||
|
*/
|
||||||
|
private boolean enableMetrics = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If non-null, we will pass this SnapshotThrottle to SnapshotExecutor
|
||||||
|
* Default: NULL
|
||||||
|
*/
|
||||||
|
private SnapshotThrottle snapshotThrottle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether use global election timer
|
||||||
|
*/
|
||||||
|
private boolean sharedElectionTimer = false;
|
||||||
|
/**
|
||||||
|
* Whether use global vote timer
|
||||||
|
*/
|
||||||
|
private boolean sharedVoteTimer = false;
|
||||||
|
/**
|
||||||
|
* Whether use global step down timer
|
||||||
|
*/
|
||||||
|
private boolean sharedStepDownTimer = false;
|
||||||
|
/**
|
||||||
|
* Whether use global snapshot timer
|
||||||
|
*/
|
||||||
|
private boolean sharedSnapshotTimer = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom service factory.
|
||||||
|
*/
|
||||||
|
private JRaftServiceFactory serviceFactory = defaultServiceFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply task in blocking or non-blocking mode, ApplyTaskMode.NonBlocking by default.
|
||||||
|
*/
|
||||||
|
private ApplyTaskMode applyTaskMode = ApplyTaskMode.NonBlocking;
|
||||||
|
|
||||||
|
public ApplyTaskMode getApplyTaskMode() {
|
||||||
|
return this.applyTaskMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApplyTaskMode(final ApplyTaskMode applyTaskMode) {
|
||||||
|
this.applyTaskMode = applyTaskMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JRaftServiceFactory getServiceFactory() {
|
||||||
|
return this.serviceFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServiceFactory(final JRaftServiceFactory serviceFactory) {
|
||||||
|
this.serviceFactory = serviceFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SnapshotThrottle getSnapshotThrottle() {
|
||||||
|
return this.snapshotThrottle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnapshotThrottle(final SnapshotThrottle snapshotThrottle) {
|
||||||
|
this.snapshotThrottle = snapshotThrottle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableMetrics(final boolean enableMetrics) {
|
||||||
|
this.enableMetrics = enableMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raft options
|
||||||
|
*/
|
||||||
|
private RaftOptions raftOptions = new RaftOptions();
|
||||||
|
|
||||||
|
public int getCliRpcThreadPoolSize() {
|
||||||
|
return this.cliRpcThreadPoolSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCliRpcThreadPoolSize(final int cliRpcThreadPoolSize) {
|
||||||
|
this.cliRpcThreadPoolSize = cliRpcThreadPoolSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnableMetrics() {
|
||||||
|
return this.enableMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRaftRpcThreadPoolSize() {
|
||||||
|
return this.raftRpcThreadPoolSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRaftRpcThreadPoolSize(final int raftRpcThreadPoolSize) {
|
||||||
|
this.raftRpcThreadPoolSize = raftRpcThreadPoolSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSharedTimerPool() {
|
||||||
|
return this.sharedTimerPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSharedTimerPool(final boolean sharedTimerPool) {
|
||||||
|
this.sharedTimerPool = sharedTimerPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTimerPoolSize() {
|
||||||
|
return this.timerPoolSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimerPoolSize(final int timerPoolSize) {
|
||||||
|
this.timerPoolSize = timerPoolSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RaftOptions getRaftOptions() {
|
||||||
|
return this.raftOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRaftOptions(final RaftOptions raftOptions) {
|
||||||
|
this.raftOptions = raftOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validate() {
|
||||||
|
if (StringUtils.isBlank(this.logUri)) {
|
||||||
|
throw new IllegalArgumentException("Blank logUri");
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(this.raftMetaUri)) {
|
||||||
|
throw new IllegalArgumentException("Blank raftMetaUri");
|
||||||
|
}
|
||||||
|
if (this.fsm == null) {
|
||||||
|
throw new IllegalArgumentException("Null stateMachine");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getElectionPriority() {
|
||||||
|
return this.electionPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setElectionPriority(final int electionPriority) {
|
||||||
|
this.electionPriority = electionPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDecayPriorityGap() {
|
||||||
|
return this.decayPriorityGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecayPriorityGap(final int decayPriorityGap) {
|
||||||
|
this.decayPriorityGap = decayPriorityGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getElectionTimeoutMs() {
|
||||||
|
return this.electionTimeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setElectionTimeoutMs(final int electionTimeoutMs) {
|
||||||
|
this.electionTimeoutMs = electionTimeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLeaderLeaseTimeRatio() {
|
||||||
|
return this.leaderLeaseTimeRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLeaderLeaseTimeRatio(final int leaderLeaseTimeRatio) {
|
||||||
|
if (leaderLeaseTimeRatio <= 0 || leaderLeaseTimeRatio > 100) {
|
||||||
|
throw new IllegalArgumentException("leaderLeaseTimeRatio: " + leaderLeaseTimeRatio
|
||||||
|
+ " (expected: 0 < leaderLeaseTimeRatio <= 100)");
|
||||||
|
}
|
||||||
|
this.leaderLeaseTimeRatio = leaderLeaseTimeRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLeaderLeaseTimeoutMs() {
|
||||||
|
return this.electionTimeoutMs * this.leaderLeaseTimeRatio / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSnapshotIntervalSecs() {
|
||||||
|
return this.snapshotIntervalSecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnapshotIntervalSecs(final int snapshotIntervalSecs) {
|
||||||
|
this.snapshotIntervalSecs = snapshotIntervalSecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSnapshotLogIndexMargin() {
|
||||||
|
return this.snapshotLogIndexMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnapshotLogIndexMargin(final int snapshotLogIndexMargin) {
|
||||||
|
this.snapshotLogIndexMargin = snapshotLogIndexMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCatchupMargin() {
|
||||||
|
return this.catchupMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCatchupMargin(final int catchupMargin) {
|
||||||
|
this.catchupMargin = catchupMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Configuration getInitialConf() {
|
||||||
|
return this.initialConf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInitialConf(final Configuration initialConf) {
|
||||||
|
this.initialConf = initialConf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateMachine getFsm() {
|
||||||
|
return this.fsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFsm(final StateMachine fsm) {
|
||||||
|
this.fsm = fsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogUri() {
|
||||||
|
return this.logUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogUri(final String logUri) {
|
||||||
|
this.logUri = logUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRaftMetaUri() {
|
||||||
|
return this.raftMetaUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRaftMetaUri(final String raftMetaUri) {
|
||||||
|
this.raftMetaUri = raftMetaUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSnapshotUri() {
|
||||||
|
return this.snapshotUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSnapshotUri(final String snapshotUri) {
|
||||||
|
this.snapshotUri = snapshotUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFilterBeforeCopyRemote() {
|
||||||
|
return this.filterBeforeCopyRemote;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilterBeforeCopyRemote(final boolean filterBeforeCopyRemote) {
|
||||||
|
this.filterBeforeCopyRemote = filterBeforeCopyRemote;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDisableCli() {
|
||||||
|
return this.disableCli;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisableCli(final boolean disableCli) {
|
||||||
|
this.disableCli = disableCli;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSharedElectionTimer() {
|
||||||
|
return this.sharedElectionTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSharedElectionTimer(final boolean sharedElectionTimer) {
|
||||||
|
this.sharedElectionTimer = sharedElectionTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSharedVoteTimer() {
|
||||||
|
return this.sharedVoteTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSharedVoteTimer(final boolean sharedVoteTimer) {
|
||||||
|
this.sharedVoteTimer = sharedVoteTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSharedStepDownTimer() {
|
||||||
|
return this.sharedStepDownTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSharedStepDownTimer(final boolean sharedStepDownTimer) {
|
||||||
|
this.sharedStepDownTimer = sharedStepDownTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSharedSnapshotTimer() {
|
||||||
|
return this.sharedSnapshotTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSharedSnapshotTimer(final boolean sharedSnapshotTimer) {
|
||||||
|
this.sharedSnapshotTimer = sharedSnapshotTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NodeOptions copy() {
|
||||||
|
final NodeOptions nodeOptions = new NodeOptions();
|
||||||
|
nodeOptions.setElectionTimeoutMs(this.electionTimeoutMs);
|
||||||
|
nodeOptions.setElectionPriority(this.electionPriority);
|
||||||
|
nodeOptions.setDecayPriorityGap(this.decayPriorityGap);
|
||||||
|
nodeOptions.setSnapshotIntervalSecs(this.snapshotIntervalSecs);
|
||||||
|
nodeOptions.setSnapshotLogIndexMargin(this.snapshotLogIndexMargin);
|
||||||
|
nodeOptions.setCatchupMargin(this.catchupMargin);
|
||||||
|
nodeOptions.setFilterBeforeCopyRemote(this.filterBeforeCopyRemote);
|
||||||
|
nodeOptions.setDisableCli(this.disableCli);
|
||||||
|
nodeOptions.setSharedTimerPool(this.sharedTimerPool);
|
||||||
|
nodeOptions.setTimerPoolSize(this.timerPoolSize);
|
||||||
|
nodeOptions.setCliRpcThreadPoolSize(this.cliRpcThreadPoolSize);
|
||||||
|
nodeOptions.setRaftRpcThreadPoolSize(this.raftRpcThreadPoolSize);
|
||||||
|
nodeOptions.setEnableMetrics(this.enableMetrics);
|
||||||
|
nodeOptions.setRaftOptions(this.raftOptions == null ? new RaftOptions() : this.raftOptions.copy());
|
||||||
|
nodeOptions.setSharedElectionTimer(this.sharedElectionTimer);
|
||||||
|
nodeOptions.setSharedVoteTimer(this.sharedVoteTimer);
|
||||||
|
nodeOptions.setSharedStepDownTimer(this.sharedStepDownTimer);
|
||||||
|
nodeOptions.setSharedSnapshotTimer(this.sharedSnapshotTimer);
|
||||||
|
return nodeOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "NodeOptions{" + "electionTimeoutMs=" + this.electionTimeoutMs + ", electionPriority="
|
||||||
|
+ this.electionPriority + ", decayPriorityGap=" + this.decayPriorityGap + ", leaderLeaseTimeRatio="
|
||||||
|
+ this.leaderLeaseTimeRatio + ", snapshotIntervalSecs=" + this.snapshotIntervalSecs
|
||||||
|
+ ", snapshotLogIndexMargin=" + this.snapshotLogIndexMargin + ", catchupMargin=" + this.catchupMargin
|
||||||
|
+ ", initialConf=" + this.initialConf + ", fsm=" + this.fsm + ", logUri='" + this.logUri + '\''
|
||||||
|
+ ", raftMetaUri='" + this.raftMetaUri + '\'' + ", snapshotUri='" + this.snapshotUri + '\''
|
||||||
|
+ ", filterBeforeCopyRemote=" + this.filterBeforeCopyRemote + ", disableCli=" + this.disableCli
|
||||||
|
+ ", sharedTimerPool=" + this.sharedTimerPool + ", timerPoolSize=" + this.timerPoolSize
|
||||||
|
+ ", cliRpcThreadPoolSize=" + this.cliRpcThreadPoolSize + ", raftRpcThreadPoolSize="
|
||||||
|
+ this.raftRpcThreadPoolSize + ", enableMetrics=" + this.enableMetrics + ", snapshotThrottle="
|
||||||
|
+ this.snapshotThrottle + ", sharedElectionTimer=" + this.sharedElectionTimer + ", sharedVoteTimer="
|
||||||
|
+ this.sharedVoteTimer + ", sharedStepDownTimer=" + this.sharedStepDownTimer + ", sharedSnapshotTimer="
|
||||||
|
+ this.sharedSnapshotTimer + ", serviceFactory=" + this.serviceFactory + ", applyTaskMode="
|
||||||
|
+ this.applyTaskMode + ", raftOptions=" + this.raftOptions + "} " + super.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.core.NodeImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raft meta storage options
|
||||||
|
* @author dennis
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class RaftMetaStorageOptions {
|
||||||
|
private NodeImpl node;
|
||||||
|
|
||||||
|
public NodeImpl getNode() {
|
||||||
|
return this.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNode(NodeImpl node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,291 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.util.Copiable;
|
||||||
|
import com.alipay.sofa.jraft.util.RpcFactoryHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raft options.
|
||||||
|
*
|
||||||
|
* @author boyan (boyan@alibaba-inc.com)
|
||||||
|
*
|
||||||
|
* 2018-Apr-03 4:38:40 PM
|
||||||
|
*/
|
||||||
|
public class RaftOptions implements Copiable<RaftOptions> {
|
||||||
|
|
||||||
|
/** Maximum of block size per RPC */
|
||||||
|
private int maxByteCountPerRpc = 128 * 1024;
|
||||||
|
/** File service check hole switch, default disable */
|
||||||
|
private boolean fileCheckHole = false;
|
||||||
|
/** The maximum number of entries in AppendEntriesRequest */
|
||||||
|
private int maxEntriesSize = 1024;
|
||||||
|
/** The maximum byte size of AppendEntriesRequest */
|
||||||
|
private int maxBodySize = 512 * 1024;
|
||||||
|
/** Flush buffer to LogStorage if the buffer size reaches the limit */
|
||||||
|
private int maxAppendBufferSize = 256 * 1024;
|
||||||
|
/** Maximum election delay time allowed by user */
|
||||||
|
private int maxElectionDelayMs = 1000;
|
||||||
|
/** Raft election:heartbeat timeout factor */
|
||||||
|
private int electionHeartbeatFactor = 10;
|
||||||
|
/** Maximum number of tasks that can be applied in a batch */
|
||||||
|
private int applyBatch = 32;
|
||||||
|
/** Call fsync when need */
|
||||||
|
private boolean sync = true;
|
||||||
|
/** Sync log meta, snapshot meta and raft meta */
|
||||||
|
private boolean syncMeta = false;
|
||||||
|
/** Statistics to analyze the performance of db */
|
||||||
|
private boolean openStatistics = true;
|
||||||
|
/** Whether to enable replicator pipeline. */
|
||||||
|
private boolean replicatorPipeline = true;
|
||||||
|
/** The maximum replicator pipeline in-flight requests/responses, only valid when enable replicator pipeline. */
|
||||||
|
private int maxReplicatorInflightMsgs = 256;
|
||||||
|
/** Internal disruptor buffers size for Node/FSMCaller/LogManager etc. */
|
||||||
|
private int disruptorBufferSize = 16384;
|
||||||
|
/**
|
||||||
|
* The maximum timeout in seconds to wait when publishing events into disruptor, default is 10 seconds.
|
||||||
|
* If the timeout happens, it may halt the node.
|
||||||
|
* */
|
||||||
|
private int disruptorPublishEventWaitTimeoutSecs = 10;
|
||||||
|
/**
|
||||||
|
* When true, validate log entry checksum when transferring the log entry from disk or network, default is false.
|
||||||
|
* If true, it would hurt the performance of JRAft but gain the data safety.
|
||||||
|
* @since 1.2.6
|
||||||
|
*/
|
||||||
|
private boolean enableLogEntryChecksum = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReadOnlyOption specifies how the read only request is processed.
|
||||||
|
*
|
||||||
|
* {@link ReadOnlyOption#ReadOnlySafe} guarantees the linearizability of the read only request by
|
||||||
|
* communicating with the quorum. It is the default and suggested option.
|
||||||
|
|
||||||
|
* {@link ReadOnlyOption#ReadOnlyLeaseBased} ensures linearizability of the read only request by
|
||||||
|
* relying on the leader lease. It can be affected by clock drift.
|
||||||
|
* If the clock drift is unbounded, leader might keep the lease longer than it
|
||||||
|
* should (clock can move backward/pause without any bound). ReadIndex is not safe
|
||||||
|
* in that case.
|
||||||
|
*/
|
||||||
|
private ReadOnlyOption readOnlyOptions = ReadOnlyOption.ReadOnlySafe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read index read need compare current node's apply index with leader's commit index.
|
||||||
|
* Only current node's apply index catch up leader's commit index, then call back success to read index closure.
|
||||||
|
* Therefore, there is a waiting time. The default wait timeout is 2s. It means that the waiting time
|
||||||
|
* over 2s, then call back failure to read index closure. If current node occur problem, it's apply index maybe
|
||||||
|
* behind leader's commit index. In read index timeout, it can't catch up, the timeout is waste.
|
||||||
|
* Here supply a config to fix it. If the gap greater than maxReadIndexLag, fail fast to call back failure
|
||||||
|
* read index closure.
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
private int maxReadIndexLag = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Candidate steps down when election reaching timeout, default is true(enabled).
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
private boolean stepDownWhenVoteTimedout = true;
|
||||||
|
|
||||||
|
public boolean isStepDownWhenVoteTimedout() {
|
||||||
|
return this.stepDownWhenVoteTimedout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStepDownWhenVoteTimedout(final boolean stepDownWhenVoteTimeout) {
|
||||||
|
this.stepDownWhenVoteTimedout = stepDownWhenVoteTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDisruptorPublishEventWaitTimeoutSecs() {
|
||||||
|
return this.disruptorPublishEventWaitTimeoutSecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisruptorPublishEventWaitTimeoutSecs(final int disruptorPublishEventWaitTimeoutSecs) {
|
||||||
|
this.disruptorPublishEventWaitTimeoutSecs = disruptorPublishEventWaitTimeoutSecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnableLogEntryChecksum() {
|
||||||
|
return this.enableLogEntryChecksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableLogEntryChecksum(final boolean enableLogEntryChecksumValidation) {
|
||||||
|
this.enableLogEntryChecksum = enableLogEntryChecksumValidation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyOption getReadOnlyOptions() {
|
||||||
|
return this.readOnlyOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReadOnlyOptions(final ReadOnlyOption readOnlyOptions) {
|
||||||
|
this.readOnlyOptions = readOnlyOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxReadIndexLag() {
|
||||||
|
return maxReadIndexLag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxReadIndexLag(int maxReadIndexLag) {
|
||||||
|
this.maxReadIndexLag = maxReadIndexLag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReplicatorPipeline() {
|
||||||
|
return this.replicatorPipeline && RpcFactoryHelper.rpcFactory().isReplicatorPipelineEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReplicatorPipeline(final boolean replicatorPipeline) {
|
||||||
|
this.replicatorPipeline = replicatorPipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxReplicatorInflightMsgs() {
|
||||||
|
return this.maxReplicatorInflightMsgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxReplicatorInflightMsgs(final int maxReplicatorPiplelinePendingResponses) {
|
||||||
|
this.maxReplicatorInflightMsgs = maxReplicatorPiplelinePendingResponses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDisruptorBufferSize() {
|
||||||
|
return this.disruptorBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisruptorBufferSize(final int disruptorBufferSize) {
|
||||||
|
this.disruptorBufferSize = disruptorBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxByteCountPerRpc() {
|
||||||
|
return this.maxByteCountPerRpc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxByteCountPerRpc(final int maxByteCountPerRpc) {
|
||||||
|
this.maxByteCountPerRpc = maxByteCountPerRpc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFileCheckHole() {
|
||||||
|
return this.fileCheckHole;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileCheckHole(final boolean fileCheckHole) {
|
||||||
|
this.fileCheckHole = fileCheckHole;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxEntriesSize() {
|
||||||
|
return this.maxEntriesSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxEntriesSize(final int maxEntriesSize) {
|
||||||
|
this.maxEntriesSize = maxEntriesSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxBodySize() {
|
||||||
|
return this.maxBodySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxBodySize(final int maxBodySize) {
|
||||||
|
this.maxBodySize = maxBodySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxAppendBufferSize() {
|
||||||
|
return this.maxAppendBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxAppendBufferSize(final int maxAppendBufferSize) {
|
||||||
|
this.maxAppendBufferSize = maxAppendBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxElectionDelayMs() {
|
||||||
|
return this.maxElectionDelayMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxElectionDelayMs(final int maxElectionDelayMs) {
|
||||||
|
this.maxElectionDelayMs = maxElectionDelayMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getElectionHeartbeatFactor() {
|
||||||
|
return this.electionHeartbeatFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setElectionHeartbeatFactor(final int electionHeartbeatFactor) {
|
||||||
|
this.electionHeartbeatFactor = electionHeartbeatFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getApplyBatch() {
|
||||||
|
return this.applyBatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApplyBatch(final int applyBatch) {
|
||||||
|
this.applyBatch = applyBatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSync() {
|
||||||
|
return this.sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSync(final boolean sync) {
|
||||||
|
this.sync = sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSyncMeta() {
|
||||||
|
return this.sync || this.syncMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSyncMeta(final boolean syncMeta) {
|
||||||
|
this.syncMeta = syncMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOpenStatistics() {
|
||||||
|
return this.openStatistics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOpenStatistics(final boolean openStatistics) {
|
||||||
|
this.openStatistics = openStatistics;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RaftOptions copy() {
|
||||||
|
final RaftOptions raftOptions = new RaftOptions();
|
||||||
|
raftOptions.setMaxByteCountPerRpc(this.maxByteCountPerRpc);
|
||||||
|
raftOptions.setFileCheckHole(this.fileCheckHole);
|
||||||
|
raftOptions.setMaxEntriesSize(this.maxEntriesSize);
|
||||||
|
raftOptions.setMaxBodySize(this.maxBodySize);
|
||||||
|
raftOptions.setMaxAppendBufferSize(this.maxAppendBufferSize);
|
||||||
|
raftOptions.setMaxElectionDelayMs(this.maxElectionDelayMs);
|
||||||
|
raftOptions.setElectionHeartbeatFactor(this.electionHeartbeatFactor);
|
||||||
|
raftOptions.setApplyBatch(this.applyBatch);
|
||||||
|
raftOptions.setSync(this.sync);
|
||||||
|
raftOptions.setSyncMeta(this.syncMeta);
|
||||||
|
raftOptions.setOpenStatistics(this.openStatistics);
|
||||||
|
raftOptions.setReplicatorPipeline(this.replicatorPipeline);
|
||||||
|
raftOptions.setMaxReplicatorInflightMsgs(this.maxReplicatorInflightMsgs);
|
||||||
|
raftOptions.setDisruptorBufferSize(this.disruptorBufferSize);
|
||||||
|
raftOptions.setDisruptorPublishEventWaitTimeoutSecs(this.disruptorPublishEventWaitTimeoutSecs);
|
||||||
|
raftOptions.setEnableLogEntryChecksum(this.enableLogEntryChecksum);
|
||||||
|
raftOptions.setReadOnlyOptions(this.readOnlyOptions);
|
||||||
|
return raftOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RaftOptions{" + "maxByteCountPerRpc=" + this.maxByteCountPerRpc + ", fileCheckHole="
|
||||||
|
+ this.fileCheckHole + ", maxEntriesSize=" + this.maxEntriesSize + ", maxBodySize=" + this.maxBodySize
|
||||||
|
+ ", maxAppendBufferSize=" + this.maxAppendBufferSize + ", maxElectionDelayMs="
|
||||||
|
+ this.maxElectionDelayMs + ", electionHeartbeatFactor=" + this.electionHeartbeatFactor
|
||||||
|
+ ", applyBatch=" + this.applyBatch + ", sync=" + this.sync + ", syncMeta=" + this.syncMeta
|
||||||
|
+ ", openStatistics=" + this.openStatistics + ", replicatorPipeline=" + this.replicatorPipeline
|
||||||
|
+ ", maxReplicatorInflightMsgs=" + this.maxReplicatorInflightMsgs + ", disruptorBufferSize="
|
||||||
|
+ this.disruptorBufferSize + ", disruptorPublishEventWaitTimeoutSecs="
|
||||||
|
+ this.disruptorPublishEventWaitTimeoutSecs + ", enableLogEntryChecksum=" + this.enableLogEntryChecksum
|
||||||
|
+ ", readOnlyOptions=" + this.readOnlyOptions + '}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read only options.
|
||||||
|
*
|
||||||
|
* @author dennis
|
||||||
|
*/
|
||||||
|
public enum ReadOnlyOption {
|
||||||
|
|
||||||
|
// ReadOnlySafe guarantees the linearizability of the read only request by
|
||||||
|
// communicating with the quorum. It is the default and suggested option.
|
||||||
|
ReadOnlySafe,
|
||||||
|
// ReadOnlyLeaseBased ensures linearizability of the read only request by
|
||||||
|
// relying on the leader lease. It can be affected by clock drift.
|
||||||
|
// If the clock drift is unbounded, leader might keep the lease longer than it
|
||||||
|
// should (clock can move backward/pause without any bound). ReadIndex is not safe
|
||||||
|
// in that case.
|
||||||
|
ReadOnlyLeaseBased
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alipay.sofa.jraft.option;
|
||||||
|
|
||||||
|
import com.alipay.sofa.jraft.FSMCaller;
|
||||||
|
import com.alipay.sofa.jraft.core.NodeImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read-Only service options.
|
||||||
|
*
|
||||||
|
* @author dennis
|
||||||
|
*/
|
||||||
|
public class ReadOnlyServiceOptions {
|
||||||
|
|
||||||
|
private RaftOptions raftOptions;
|
||||||
|
private NodeImpl node;
|
||||||
|
private FSMCaller fsmCaller;
|
||||||
|
|
||||||
|
public NodeImpl getNode() {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNode(NodeImpl node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RaftOptions getRaftOptions() {
|
||||||
|
return raftOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRaftOptions(RaftOptions raftOptions) {
|
||||||
|
this.raftOptions = raftOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FSMCaller getFsmCaller() {
|
||||||
|
return fsmCaller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFsmCaller(FSMCaller fsm) {
|
||||||
|
this.fsmCaller = fsm;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue