├── .github
├── dependabot.yml
└── workflows
│ └── maven.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── bin
├── analyze-log.sh
├── client.sh
├── compile.sh
├── counter-perf.sh
├── counter.sh
├── jmh.sh
├── migrate_leveldb.java
├── probe.sh
├── release-perform.sh
├── release-prepare.sh
├── remove-elections.sh
├── replication-perf.sh
├── rsm-client.sh
├── rsm.sh
├── run.sh
└── test-run.sh
├── conf
├── jgroups-raft.yaml
├── jni.json
├── log4j2-test.xml
├── log4j2.xml
├── raft.xml
├── reflection.json
└── rsm.yaml
├── doc
├── design.adoc
├── design
│ ├── AppendEntries.txt
│ ├── Election.txt
│ ├── Election2.adoc
│ ├── LearnerNodes.adoc
│ └── Log.txt
├── manual
│ ├── blocks.adoc
│ ├── manual.adoc
│ ├── migration.adoc
│ ├── overview.adoc
│ ├── protocols-template.adoc
│ └── using.adoc
└── readme.adoc
├── pom.xml
├── src
└── org
│ └── jgroups
│ ├── protocols
│ └── raft
│ │ ├── AppendEntriesRequest.java
│ │ ├── AppendEntriesResponse.java
│ │ ├── AppendResult.java
│ │ ├── CLIENT.java
│ │ ├── DynamicMembership.java
│ │ ├── ELECTION.java
│ │ ├── ELECTION2.java
│ │ ├── FileBasedLog.java
│ │ ├── Follower.java
│ │ ├── InMemoryLog.java
│ │ ├── InstallSnapshotRequest.java
│ │ ├── InternalCommand.java
│ │ ├── Leader.java
│ │ ├── Learner.java
│ │ ├── LevelDBLog.java
│ │ ├── Log.java
│ │ ├── LogEntries.java
│ │ ├── LogEntry.java
│ │ ├── NO_DUPES.java
│ │ ├── PersistentState.java
│ │ ├── RAFT.java
│ │ ├── REDIRECT.java
│ │ ├── RaftHeader.java
│ │ ├── RaftImpl.java
│ │ ├── RaftLeaderException.java
│ │ ├── Role.java
│ │ ├── election
│ │ ├── BaseElection.java
│ │ ├── LeaderElected.java
│ │ ├── PreVoteRequest.java
│ │ ├── PreVoteResponse.java
│ │ ├── VoteRequest.java
│ │ └── VoteResponse.java
│ │ └── state
│ │ └── RaftState.java
│ └── raft
│ ├── Options.java
│ ├── RaftHandle.java
│ ├── Settable.java
│ ├── StateMachine.java
│ ├── blocks
│ ├── AsyncCounterImpl.java
│ ├── CounterService.java
│ ├── RaftAsyncCounter.java
│ ├── RaftCounter.java
│ ├── RaftSyncCounter.java
│ └── ReplicatedStateMachine.java
│ ├── client
│ ├── Client.java
│ ├── ClientStub.java
│ └── ReplicatedStateMachineClient.java
│ ├── demos
│ ├── CounterServiceDemo.java
│ ├── ProgrammaticRSM.java
│ └── ReplicatedStateMachineDemo.java
│ ├── filelog
│ ├── FilePositionCache.java
│ ├── FileStorage.java
│ ├── LogEntryStorage.java
│ └── MetadataStorage.java
│ ├── testfwk
│ ├── BlockingMessageInterceptor.java
│ ├── MockRaftCluster.java
│ ├── PartitionedRaftCluster.java
│ ├── RaftCluster.java
│ ├── RaftNode.java
│ └── RaftTestUtils.java
│ └── util
│ ├── AnalyzeLog.java
│ ├── ArrayRingBuffer.java
│ ├── CommitTable.java
│ ├── CounterStateMachine.java
│ ├── LogCache.java
│ ├── LongHelper.java
│ ├── PropsToAsciidoc.java
│ ├── ReplStateMachine.java
│ ├── RequestTable.java
│ ├── Utils.java
│ └── pmem
│ ├── FileProvider.java
│ └── PmemUtilWrapper.java
└── tests
├── benchmark
└── org
│ └── jgroups
│ └── perf
│ ├── CommandLineOptions.java
│ ├── Main.java
│ ├── counter
│ ├── AsyncCounterBenchmark.java
│ ├── CounterBenchmark.java
│ ├── CounterPerf.java
│ ├── HistogramUtil.java
│ └── SyncBenchmark.java
│ ├── harness
│ ├── AbstractRaftBenchmark.java
│ └── RaftBenchmark.java
│ ├── jmh
│ ├── DataReplicationBenchmark.java
│ ├── LogJmhBenchmark.java
│ └── StorageAppenderBenchmark.java
│ └── replication
│ ├── AsyncReplicationBenchmark.java
│ ├── ReplicationPerf.java
│ └── SyncReplicationBenchmark.java
├── junit-functional
└── org
│ └── jgroups
│ └── tests
│ ├── AppendEntriesTest.java
│ ├── CommitTableTest.java
│ ├── CompletableFutureTest.java
│ ├── DummyStateMachine.java
│ ├── DynamicMembershipTest.java
│ ├── ElectionsTest.java
│ ├── LearnerMemberTest.java
│ ├── LogEntriesTest.java
│ ├── LogTest.java
│ ├── LongHelperTest.java
│ ├── MaintenanceClusterTest.java
│ ├── MergeTest.java
│ ├── PartialConnectivityTest.java
│ ├── RaftHeaderTest.java
│ ├── RaftTest.java
│ ├── ReplicatedStateMachineTest.java
│ ├── RequestTableTest.java
│ ├── SyncElectionTests.java
│ ├── SyncElectionWithRestrictionTest.java
│ ├── SyncLeaderCrashTest.java
│ ├── SynchronousTests.java
│ ├── TimeoutTest.java
│ ├── UtilsTest.java
│ ├── VoteTest.java
│ ├── blocks
│ └── CounterTest.java
│ ├── election
│ ├── DelayedElectedLeaderMessageTest.java
│ ├── DetermineLeaderBreakdownTest.java
│ ├── LeaderLeavingTest.java
│ ├── NetworkPartitionChannelTest.java
│ ├── NetworkPartitionElectionTest.java
│ ├── ViewChangeElectionTest.java
│ └── VotingThreadBreakdownTest.java
│ ├── harness
│ ├── AbstractRaftTest.java
│ ├── BaseRaftChannelTest.java
│ ├── BaseRaftClusterTest.java
│ ├── BaseRaftElectionTest.java
│ ├── BaseStateMachineTest.java
│ ├── CheckPoint.java
│ └── RaftAssertion.java
│ └── utils
│ ├── ArrayRingBufferTest.java
│ └── JUnitXMLReporter.java
└── resources
└── raft-benchmark.xml
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: maven
9 | directory: "/"
10 | schedule:
11 | interval: daily
12 | timezone: Brazil/East
13 | time: "15:00"
14 | open-pull-requests-limit: 10
15 | assignees:
16 | - "jabolina"
17 | reviewers:
18 | - "jabolina"
19 | - package-ecosystem: github-actions
20 | directory: "/"
21 | schedule:
22 | interval: daily
23 | open-pull-requests-limit: 10
24 |
--------------------------------------------------------------------------------
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | name: Java CI with Maven
2 |
3 | on:
4 | push:
5 | branches:
6 | - '*'
7 | pull_request:
8 | branches:
9 | - '*'
10 | workflow_dispatch:
11 |
12 | permissions:
13 | contents: read
14 |
15 | jobs:
16 | build:
17 | runs-on: ${{ matrix.os }}
18 | strategy:
19 | fail-fast: false
20 | matrix:
21 | os:
22 | - "ubuntu-latest"
23 | - "windows-latest"
24 | - "macos-latest"
25 | # Keep this list as: all supported LTS JDKs, the latest GA JDK, and optionally the latest EA JDK (if available).
26 | # https://www.oracle.com/java/technologies/java-se-support-roadmap.html
27 | java: [ 11, 17, 21, 23 ]
28 | steps:
29 | - name: Checkout
30 | uses: actions/checkout@v4
31 | - name: Set up JDK ${{ matrix.java }}
32 | uses: actions/setup-java@v4
33 | with:
34 | java-version: ${{ matrix.java }}
35 | distribution: temurin
36 | cache: maven
37 | - name: Build with Maven
38 | timeout-minutes: 10
39 | id: test_runner
40 | run: mvn --batch-mode --no-transfer-progress package -Dgroups=functional
41 | - name: Generate test reports
42 | # Only generate reports if tests failed.
43 | if: failure()
44 | run: mvn --batch-mode --no-transfer-progress surefire-report:report-only
45 | - name: Generate and upload test reports
46 | # Only upload reports if tests failed.
47 | if: failure()
48 | uses: actions/upload-artifact@v4
49 | with:
50 | name: Test reports
51 | # Once SUREFIRE-2219 is finished we can upload only the html report.
52 | path: |
53 | tmp/html/
54 | tmp/test-results/xml/
55 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.iws
3 | *.ipr
4 | *.iml
5 | *.html
6 | .project
7 | .classpath
8 | .factorypath
9 | .settings/
10 | .idea/
11 | .idea
12 | .DS_Store
13 | classes/
14 | build.properties
15 | dist/
16 | atlassian*
17 | keystore/
18 | tmp/
19 | bla*.java
20 | doc/manual/target
21 | doc/manual/*.css
22 | doc/manual/build/*
23 | doc/manual/*.tmp
24 | doc/manual/*-generated.adoc
25 | doc/tutorial/target
26 | conf/MANIFEST.MF
27 | target/
28 | lib/
29 | *.log
30 | *.db
31 | *.properties
32 | .ant-targets-build.xml
33 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ## Builds an image containing jgroups-raft
2 |
3 | ## ***************************************************************
4 | ## Make sure you have jgroups-raft compiled (mvn clean package) before doing so!
5 | ## ***************************************************************
6 |
7 | ## The first stage is used to prepare/update the OS.
8 | ## The second stage copies the local files (lib:classes) to the image
9 | # Build: docker build -f Dockerfile -t belaban/jgroups-raft .
10 | # Push: docker push belaban/jgroups-raft
11 |
12 |
13 | FROM adoptopenjdk/openjdk11:jre as build-stage
14 | RUN apt-get update ; apt-get install -y git ant net-tools netcat iputils-ping
15 |
16 | # For the runtime, we only need a JRE (smaller footprint)
17 | FROM adoptopenjdk/openjdk11:jre
18 | LABEL maintainer="Bela Ban (belaban@mailbox.org)"
19 | RUN useradd --uid 1000 --home /opt/jgroups --create-home --shell /bin/bash jgroups
20 | RUN echo root:root | chpasswd ; echo jgroups:jgroups | chpasswd
21 | RUN printf "\njgroups ALL=(ALL) NOPASSWD: ALL\n" >> /etc/sudoers
22 | # EXPOSE 7800-7900:7800-7900 9000-9100:9000-9100
23 | EXPOSE 1965-1975:2065-2075 8787
24 | ENV HOME /opt/jgroups
25 | ENV PATH $PATH:$HOME/jgroups-raft/bin
26 | ENV JGROUPS_RAFT_HOME=$HOME/jgroups-raft
27 | WORKDIR /opt/jgroups
28 |
29 | COPY --from=build-stage /bin/ping /bin/netstat /bin/nc /bin/
30 | COPY --from=build-stage /sbin/ifconfig /sbin/
31 | COPY README.md $JGROUPS_RAFT_HOME/
32 | COPY ./target/classes $JGROUPS_RAFT_HOME/classes
33 | COPY ./target/libs $JGROUPS_RAFT_HOME/lib
34 | COPY ./bin $JGROUPS_RAFT_HOME/bin
35 | COPY ./conf $JGROUPS_RAFT_HOME/conf
36 |
37 | RUN mkdir /mnt/data ; chown -R jgroups.jgroups /mnt/data $HOME/*
38 |
39 | # Run everything below as the jgroups user. Unfortunately, USER is only observed by RUN, *not* by ADD or COPY !!
40 | USER jgroups
41 |
42 | RUN chmod u+x $HOME/*
43 | CMD clear && cat $HOME/jgroups-raft/README.md && /bin/bash
44 |
45 |
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | jgroups-raft
2 | ============
3 |
4 | [](https://github.com/jgroups-extras/jgroups-raft/actions/workflows/maven.yml)
5 | [](https://central.sonatype.com/artifact/org.jgroups/jgroups-raft)
6 | [](https://www.apache.org/licenses/LICENSE-2.0)
7 |
8 | jgroups-raft is an implementation of the [Raft](https://raft.github.io/) consensus algorithm in [JGroups](http://jgroups.org/).
9 | Users can use jgroups-raft embedded in their applications to build strongly consistent, highly available, fault-tolerant systems.
10 |
11 |
12 | ## Overview
13 | jgroups-raft is a library offering all the guarantees the Raft algorithm provides, with features including:
14 |
15 | * Configurable and alternatives for leader election;
16 | * Dynamic membership changes in single-step;
17 | * Configurable log implementations for storage;
18 | * Member deduplication and request redirection;
19 | * Ready-to-use building blocks.
20 |
21 | By building on top of JGroups, jgroups-raft takes advantage of additional features with a mature and battle-tested network stack.
22 | jgroups-raft is [verified with Jepsen](https://github.com/jgroups-extras/jepsen-jgroups-raft) to identify linearizability violations in the building blocks.
23 |
24 |
25 | ## Getting Started
26 |
27 | To get started developing with jgroups-raft:
28 |
29 | * Take a look at the complete [documentation](https://belaban.github.io/jgroups-raft/manual/index.html);
30 | * Details about the implementation are available in the [design documents](https://github.com/jgroups-extras/jgroups-raft/tree/main/doc/design).
31 |
32 |
33 | ## Contributing
34 |
35 | * Get in touch through the [discussion group](https://groups.google.com/forum/#!forum/jgroups-raft) or [GitHub discussions](https://github.com/jgroups-extras/jgroups-raft/discussions);
36 | * Open bug reports on [GitHub issues](https://github.com/jgroups-extras/jgroups-raft/issues);
37 | * Feel like coding? Look at the issues page and get in touch with any questions.
38 |
--------------------------------------------------------------------------------
/bin/analyze-log.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 |
4 | `dirname $0`/run.sh -ea -Dlog4j.configurationFile=log4j2.xml org.jgroups.raft.util.AnalyzeLog $*
5 |
--------------------------------------------------------------------------------
/bin/client.sh:
--------------------------------------------------------------------------------
1 | ### Calls Client
2 |
3 | #!/bin/bash
4 |
5 |
6 | `dirname $0`/run.sh org.jgroups.raft.client.Client $*
7 |
8 |
--------------------------------------------------------------------------------
/bin/compile.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ## Creates a native image using the GraalVM compiler (needs to be on the path)
4 |
5 | LIB=`dirname $0`/../lib
6 | CLASSES=`dirname $0`/../classes
7 | CONF=`dirname $0`/../conf
8 | CLASSPATH="$CLASSES:$LIB/*"
9 |
10 | OPTIONS="-H:+JNI --no-server -H:+ReportExceptionStackTraces --features=org.graalvm.home.HomeFinderFeature"
11 |
12 | OPTIONS="$OPTIONS -H:+AllowVMInspection -H:TraceClassInitialization=true --no-fallback --allow-incomplete-classpath"
13 |
14 | OPTIONS="$OPTIONS -H:ReflectionConfigurationFiles=$CONF/reflection.json"
15 |
16 | OPTIONS="$OPTIONS -H:JNIConfigurationFiles=$CONF/jni.json" #,$CONF/jni-config.json"
17 |
18 | OPTIONS="$OPTIONS -H:ConfigurationFileDirectories=conf2/"
19 |
20 | # OPTIONS="$OPTIONS -H:+PrintAnalysisCallTree"
21 |
22 |
23 | OPTIONS="$OPTIONS -Dgraal.CompilationFailureAction=Diagnose"
24 |
25 | # OPTIONS="$OPTIONS -H:IncludeResources=/home/bela/logging.properties -Dfoo=bar -Dcom.sun.management.jmxremote"
26 |
27 | #OPTIONS="$OPTIONS --debug-attach=*:5000"
28 |
29 | #OPTIONS="$OPTIONS -J-server -J-XX:+UseG1GC -J-XX:+UseAdaptiveSizePolicy -J-XX:MinHeapFreeRatio=20 -J-XX:MaxHeapFreeRatio=20"
30 |
31 | OPTIONS="$OPTIONS --initialize-at-build-time="
32 |
33 | OPTIONS="$OPTIONS -Dlog4j2.disable.jmx=true" ## Prevents log4j2 from creating an MBeanServer
34 |
35 | OPTIONS="$OPTIONS -Djgroups.use.jdk_logger=true" ## prevents log4j2 from being used
36 |
37 | #OPTIONS="$OPTIONS -H:GenerateDebugInfo=1"
38 |
39 | #OPTIONS="$OPTIONS --initialize-at-run-time=com.sun.jmx.mbeanserver.JmxMBeanServer"
40 |
41 | #OPTIONS="$OPTIONS --initialize-at-run-time=org.jgroups.protocols.FD_SOCK"
42 |
43 | native-image -cp $CLASSPATH $OPTIONS $*
--------------------------------------------------------------------------------
/bin/counter-perf.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BASEDIR=$(dirname "$0")
4 |
5 | # shellcheck disable=SC2086,SC2048
6 | "$BASEDIR"/test-run.sh -ea org.jgroups.perf.counter.CounterPerf -props raft.xml $*
7 |
8 |
--------------------------------------------------------------------------------
/bin/counter.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 |
4 | `dirname $0`/run.sh org.jgroups.raft.demos.CounterServiceDemo -props raft.xml $*
5 |
6 |
--------------------------------------------------------------------------------
/bin/jmh.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BASEDIR=$(dirname "$0")
4 |
5 | function help() {
6 | echo ""
7 | echo "tip: pass '-h' for JMH options"
8 | exit 1
9 | }
10 |
11 | function list_benchmarks() {
12 | echo "Available benchmarks:"
13 | printf "\tDataReplicationBenchmark\n"
14 | printf "\tLogJmhBenchmark\n"
15 | printf "\tStorageAppenderBenchmark\n"
16 | help;
17 | }
18 |
19 | BENCHMARK="$1"
20 | shift;
21 |
22 | if [ "$BENCHMARK" = "-list" ]; then
23 | list_benchmarks
24 | exit 0;
25 | fi
26 |
27 | # shellcheck disable=SC2086,SC2048
28 | "$BASEDIR"/test-run.sh -ea org.openjdk.jmh.Main "$BENCHMARK" $*
29 |
--------------------------------------------------------------------------------
/bin/migrate_leveldb.java:
--------------------------------------------------------------------------------
1 | ///usr/bin/env jbang "$0" "$@" ; exit $?
2 | //DEPS info.picocli:picocli:4.6.3
3 | //DEPS org.jgroups:jgroups-raft:1.0.14.Final
4 |
5 | import org.jgroups.protocols.raft.LevelDBLog;
6 | import org.jgroups.protocols.raft.FileBasedLog;
7 | import org.jgroups.protocols.raft.LogEntries;
8 |
9 | import java.io.IOException;
10 | import java.nio.ByteBuffer;
11 | import java.nio.file.FileVisitResult;
12 | import java.nio.file.Files;
13 | import java.nio.file.Path;
14 | import java.nio.file.SimpleFileVisitor;
15 | import java.nio.file.attribute.BasicFileAttributes;
16 | import java.util.concurrent.Callable;
17 |
18 | import picocli.CommandLine;
19 | import picocli.CommandLine.Command;
20 | import picocli.CommandLine.Option;
21 | import picocli.CommandLine.Parameters;
22 |
23 | @Command(name = "migrate_leveldb", mixinStandardHelpOptions = true, version = "migrate_leveldb 0.1",
24 | description = "Migrates data created by LevelDBLog to the FileBasedLog format.")
25 | class migrate_leveldb implements Callable
16 | * Performs leader election. This implementation takes full advantage of JGroup's membership events with {@link View}.
17 | * When the current node is the view coordinator, it starts a voting thread to ask all members to send their information.
18 | * The voting thread stops when a new leader is elected.
19 | *
20 | * The process that starts the voting thread is not trying to elect itself. The process running the voting process
21 | * increases its term and asks all nodes about their term and log index information to select the new leader, in the
22 | * form of {@link org.jgroups.protocols.raft.election.VoteResponse}. For safety reasons, only the nodes with the most
23 | * up-to-date log can be elected a leader. With a response from the majority processes, the leader with the higher term
24 | * and log index is elected. The oldest process (view coordinator) in the system has a priority. Once decided, the
25 | * process sends a message reliably to everyone identifying the new leader, with the
26 | * {@link org.jgroups.protocols.raft.election.LeaderElected} message.
27 | *
28 | * After a leader is elected, a new election round starts on view changes only if the leader left the cluster. In case
29 | * of losing a majority, the leader steps down.
30 | *
31 | * This implementation is more robust than building with heartbeats, leading to fewer disruptions in the cluster with
32 | * unnecessary (competing) election rounds. This also means the leader is capable of stepping down. Referred to in
33 | * §6.2 of Ongaro's dissertation to prevent stale leadership information.
34 | *
35 | * More information is available in the design docs.
36 | *
37 | * @author Bela Ban
38 | * @since 0.1
39 | * @see Ongaro's dissertation
40 | */
41 | @MBean(description="Protocol performing leader election according to the RAFT paper")
42 | public class ELECTION extends BaseElection {
43 | protected static final short ELECTION_ID = 520;
44 |
45 | static {
46 | ClassConfigurator.addProtocol(ELECTION_ID, ELECTION.class);
47 | }
48 |
49 | @Override
50 | protected void handleView(View v) {
51 | View previousView = this.view;
52 | this.view = v;
53 | Majority result=Utils.computeMajority(previousView, v, raft);
54 | log.debug("%s: existing view: %s, new view: %s, result: %s", local_addr, previousView, v, result);
55 | List
7 | * A learner nodes operates the same way as a {@link Follower}. However, the learner does not have voting rights for
8 | * committing an entry, for electing a leader, or to become a leader.
9 | *
17 | * Format: | num-elements | log-entry0 | log-entry1 ... | log-entryN |
18 | * @author Bela Ban
19 | * @since 1.0.8
20 | */
21 | public class LogEntries implements SizeStreamable, Iterable
The data in this class is serialized and stored at the beginning of a snapshot file. This class does not 19 | * hold information that the {@link RAFT} protocol requires to be persistent, e.g., terms and votes.
20 | * 21 | * @author Jose Bolina 22 | * @since 1.0.11 23 | */ 24 | public class PersistentState implements SizeStreamable { 25 | private final List18 | * This class offers the possibility of creating partitions in the cluster. The partitions are created by view updates. 19 | * 20 | * @since 1.0.12 21 | * @author José Bolina 22 | */ 23 | public class PartitionedRaftCluster extends MockRaftCluster { 24 | protected final Map
> partitions = new ConcurrentHashMap<>(); 25 | protected final Map nodes = new ConcurrentHashMap<>(); 26 | 27 | private final AtomicBoolean viewChanging = new AtomicBoolean(false); 28 | private final BlockingQueue11 | * If a Persistence Memory drive is available, support is provided by https://github.com/jhalliday/mashona 12 | * 13 | * @author Pedro Ruivo 14 | * @since 0.5.4 15 | */ 16 | public class FileProvider { 17 | 18 | private static final boolean ATTEMPT_PMEM; 19 | 20 | static { 21 | boolean attemptPmem = false; 22 | try { 23 | Class.forName("io.mashona.logwriting.PmemUtil"); 24 | // use persistent memory if available, otherwise fallback to regular file. 25 | attemptPmem = true; 26 | } catch (ClassNotFoundException e) { 27 | //no op 28 | } 29 | ATTEMPT_PMEM = attemptPmem; 30 | } 31 | 32 | public static boolean isPMEMAvailable() { 33 | return ATTEMPT_PMEM; 34 | } 35 | 36 | public static FileChannel openPMEMChannel(File file, 37 | int length, 38 | boolean create, 39 | boolean readSharedMetadata) throws IOException { 40 | if (!isPMEMAvailable()) { 41 | return null; 42 | } 43 | return PmemUtilWrapper.pmemChannelFor(file, length, create, readSharedMetadata); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/org/jgroups/raft/util/pmem/PmemUtilWrapper.java: -------------------------------------------------------------------------------- 1 | package org.jgroups.raft.util.pmem; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.nio.channels.FileChannel; 6 | 7 | import io.mashona.logwriting.PmemUtil; 8 | 9 | /** 10 | * This class is here solely for the purpose of encapsulating the {@link PmemUtil} class so we do not load it unless 11 | * necessary, allowing this to be an optional dependency. Any code that invokes a method in this class should first 12 | * check if the {@link PmemUtil} can be loaded via {@link Class#forName(String)} otherwise a {@link ClassNotFoundException} 13 | * may be thrown when loading this class. 14 | */ 15 | public class PmemUtilWrapper { 16 | /** 17 | * Same as {@link PmemUtil#pmemChannelFor(File, int, boolean, boolean)}. 18 | */ 19 | static public FileChannel pmemChannelFor(File file, int length, boolean create, boolean readSharedMetadata) throws FileNotFoundException { 20 | return PmemUtil.pmemChannelFor(file, length, create, readSharedMetadata); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/benchmark/org/jgroups/perf/CommandLineOptions.java: -------------------------------------------------------------------------------- 1 | package org.jgroups.perf; 2 | 3 | /** 4 | * Base command line arguments for Raft benchmarks. 5 | *
6 | * The first argument must be the FQN of the benchmark class. The remaining arguments can be provided in any order: 7 | *
-props
: The XML file to configure the protocol stack;-name
: The raft-id of the current node;-nohup
: Disable the event loop;-histogram
: Path to write the HdrHistogram file with the collected metrics for this node.12 | * The command line arguments are parsed and the benchmark class is instantiated. The arguments must be provided in the 13 | * correct order. The very first argument is the benchmark class to run, followed by the arguments. 14 | *
15 | */ 16 | public class Main { 17 | 18 | public static void main(String[] args) throws Throwable { 19 | CommandLineOptions cmd = CommandLineOptions.parse(args); 20 | AbstractRaftBenchmark benchmark = instantiate(cmd); 21 | 22 | // Initializes the benchmark. 23 | // Causes the nodes to retrieve the benchmark configuration from the coordinator. 24 | benchmark.init(); 25 | 26 | if (cmd.shouldRunEventLoop()) { 27 | benchmark.eventLoop(); 28 | } else { 29 | for (;;) Util.sleep(60_000); 30 | } 31 | 32 | benchmark.stop(); 33 | } 34 | 35 | @SuppressWarnings("unchecked") 36 | private static AbstractRaftBenchmark instantiate(CommandLineOptions cmd) 37 | throws InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException { 38 | 39 | Class extends AbstractRaftBenchmark> clazz = (Class extends AbstractRaftBenchmark>) Class.forName(cmd.getBenchmark()); 40 | Constructor>[] constructors = clazz.getConstructors(); 41 | 42 | if (constructors.length > 1) 43 | throw new IllegalStateException("Multiple constructors declared!"); 44 | 45 | Constructor extends AbstractRaftBenchmark> c = (Constructor extends AbstractRaftBenchmark>) constructors[0]; 46 | return c.newInstance(cmd); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/benchmark/org/jgroups/perf/counter/AsyncCounterBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.jgroups.perf.counter; 2 | 3 | import org.HdrHistogram.AbstractHistogram; 4 | import org.HdrHistogram.AtomicHistogram; 5 | import org.HdrHistogram.Histogram; 6 | import org.jgroups.blocks.atomic.AsyncCounter; 7 | import org.jgroups.raft.Options; 8 | import org.jgroups.raft.blocks.RaftCounter; 9 | import org.jgroups.util.CompletableFutures; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.concurrent.CompletableFuture; 14 | import java.util.concurrent.CompletionStage; 15 | import java.util.concurrent.ThreadFactory; 16 | import java.util.concurrent.atomic.AtomicBoolean; 17 | import java.util.concurrent.atomic.LongAdder; 18 | import java.util.function.Function; 19 | import java.util.function.LongSupplier; 20 | 21 | /** 22 | * Basic {@link org.jgroups.blocks.atomic.AsyncCounter} benchmark 23 | */ 24 | public class AsyncCounterBenchmark implements CounterBenchmark { 25 | 26 | private List
17 | * A new instance is created when "start benchmark" command is created.
18 | * After creation, {@link #init(int, ThreadFactory, LongSupplier, RaftCounter)} is invoked with the benchmark settings follow by {@link #start()}.
19 | * The benchmark runs for some time and then {@link #stop()} and {@link #join()} are invoked.
20 | */
21 | public interface CounterBenchmark extends AutoCloseable {
22 |
23 | /**
24 | * Initializes with the benchmark settings.
25 | * @param concurrency The number of concurrent updaters.
26 | * @param threadFactory The thread factory (if it needs to create threads).
27 | * @param deltaSupplier For each "add" operation, the delta from this {@link LongSupplier} must be used.
28 | * @param counter The {@link RaftCounter} to benchmark. Note that the {@link RaftSyncCounter}
29 | * or {@link RaftAsyncCounter} instances can be gotten by calling
30 | * {@link RaftCounter#sync()} or {@link RaftCounter#async()}, respectively
31 | */
32 | void init(int concurrency, ThreadFactory threadFactory, LongSupplier deltaSupplier, RaftCounter counter);
33 |
34 | /**
35 | * Signals the test start.
36 | */
37 | void start();
38 |
39 | /**
40 | * Signals the test end.
41 | */
42 | void stop();
43 |
44 | /**
45 | * Wait until all updaters finish their work.
46 | *
47 | * @throws InterruptedException If interrupted.
48 | */
49 | void join() throws InterruptedException;
50 |
51 | /**
52 | * @return The total number of "add" operation invoked.
53 | */
54 | long getTotalUpdates();
55 |
56 | /**
57 | * Returns the results of the run.
58 | *
59 | * @param printUpdaters If supported and if {@code true}, print to {@link System#out} each updater result.
60 | * @param timePrinter {@link Function} to use to print each updater {@link AbstractHistogram} result.
61 | * @return The {@link Histogram} with the results of all updaters.
62 | */
63 | Histogram getResults(boolean printUpdaters, Function
24 | * Run multiple asynchronous requests in parallel. A new request initiates as soon as a request finishes. Utilize the
25 | * {@link org.jgroups.raft.RaftHandle#setAsync(byte[], int, int)} API.
26 | *
22 | * This benchmark utilizes the base {@link RaftHandle} to verify the replication performance of a configurable-sized
23 | * byte array. The test verifies only the replication part, where the state machine does not interpret the bytes.
24 | * Since the {@link StateMachine} implementation is application-specific, we don't measure it in our tests.
25 | *
27 | * The benchmark accepts configuration for the payload size, whether fsync the log.
28 | *