All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.aeron.cluster.ConsensusModule Maven / Gradle / Ivy

There is a newer version: 1.48.0
Show newest version
/*
 * Copyright 2014-2024 Real Logic Limited.
 *
 * Licensed 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
 *
 * https://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 io.aeron.cluster;

import io.aeron.*;
import io.aeron.archive.Archive;
import io.aeron.archive.client.AeronArchive;
import io.aeron.cluster.client.AeronCluster;
import io.aeron.cluster.client.ClusterException;
import io.aeron.cluster.codecs.AdminRequestDecoder;
import io.aeron.cluster.codecs.AdminRequestType;
import io.aeron.cluster.codecs.BackupQueryDecoder;
import io.aeron.cluster.codecs.HeartbeatRequestDecoder;
import io.aeron.cluster.codecs.MessageHeaderDecoder;
import io.aeron.cluster.codecs.StandbySnapshotDecoder;
import io.aeron.cluster.codecs.mark.ClusterComponentType;
import io.aeron.cluster.service.ClusterMarkFile;
import io.aeron.cluster.service.ClusterCounters;
import io.aeron.cluster.service.ClusteredServiceContainer;
import io.aeron.cluster.service.ClusterClock;
import io.aeron.cluster.service.SnapshotDurationTracker;

import io.aeron.config.Config;
import io.aeron.config.DefaultType;
import io.aeron.driver.DutyCycleTracker;
import io.aeron.driver.NameResolver;
import io.aeron.driver.status.DutyCycleStallTracker;
import io.aeron.exceptions.ConcurrentConcludeException;
import io.aeron.exceptions.ConfigurationException;
import io.aeron.security.Authenticator;
import io.aeron.security.AuthenticatorSupplier;
import io.aeron.security.AuthorisationService;
import io.aeron.security.AuthorisationServiceSupplier;
import io.aeron.security.DefaultAuthenticatorSupplier;
import io.aeron.version.Versioned;
import org.agrona.*;
import org.agrona.concurrent.*;
import org.agrona.concurrent.errors.DistinctErrorLog;
import org.agrona.concurrent.status.AtomicCounter;
import org.agrona.concurrent.status.CountersReader;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.Random;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.function.Function;
import java.util.function.LongConsumer;
import java.util.function.Supplier;

import static io.aeron.AeronCounters.*;
import static io.aeron.CommonContext.*;
import static io.aeron.cluster.ConsensusModule.Configuration.CLUSTER_CLIENT_TIMEOUT_COUNT_TYPE_ID;
import static io.aeron.cluster.ConsensusModule.Configuration.CLUSTER_NODE_ROLE_TYPE_ID;
import static io.aeron.cluster.ConsensusModule.Configuration.COMMIT_POSITION_TYPE_ID;
import static io.aeron.cluster.ConsensusModule.Configuration.*;
import static java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater;
import static org.agrona.BitUtil.findNextPositivePowerOfTwo;
import static org.agrona.SystemUtil.*;

/**
 * Component which resides on each node and is responsible for coordinating consensus within a cluster in concert
 * with the lifecycle of clustered services.
 */
@Versioned
public final class ConsensusModule implements AutoCloseable
{
    /**
     * Default set of flags when taking a snapshot
     */
    public static final int CLUSTER_ACTION_FLAGS_DEFAULT = 0;

    /**
     * Flag for a snapshot taken on a standby node.
     */
    public static final int CLUSTER_ACTION_FLAGS_STANDBY_SNAPSHOT = 1;

    /**
     * Possible states for the {@link ConsensusModule}.
     * These will be reflected in the {@link Context#moduleStateCounter()} counter.
     */
    public enum State
    {
        /**
         * Starting up and recovering state.
         */
        INIT(0),

        /**
         * Active state with ingress and expired timers appended to the log.
         */
        ACTIVE(1),

        /**
         * Suspended processing of ingress and expired timers.
         */
        SUSPENDED(2),

        /**
         * In the process of taking a snapshot.
         */
        SNAPSHOT(3),

        /**
         * Quitting the cluster and shutting down as soon as services ack without taking a snapshot.
         */
        QUITTING(4),

        /**
         * In the process of terminating the node.
         */
        TERMINATING(5),

        /**
         * Terminal state.
         */
        CLOSED(6);

        static final State[] STATES = values();

        private final int code;

        State(final int code)
        {
            if (code != ordinal())
            {
                throw new IllegalArgumentException(name() + " - code must equal ordinal value: code=" + code);
            }

            this.code = code;
        }

        /**
         * Code to be stored in an {@link AtomicCounter} for the enum value.
         *
         * @return code to be stored in an {@link AtomicCounter} for the enum value.
         */
        public final int code()
        {
            return code;
        }

        /**
         * Get the {@link State} encoded in an {@link AtomicCounter}.
         *
         * @param counter to get the current state for.
         * @return the state for the {@link ConsensusModule}.
         * @throws ClusterException if the counter is not one of the valid values.
         */
        public static State get(final AtomicCounter counter)
        {
            if (counter.isClosed())
            {
                return CLOSED;
            }

            return get(counter.get());
        }

        /**
         * Get the {@link State} corresponding to a particular code.
         *
         * @param code representing a {@link State}.
         * @return the {@link State} corresponding to the provided code.
         * @throws ClusterException if the code does not correspond to a valid State.
         */
        public static State get(final long code)
        {
            if (code < 0 || code > (STATES.length - 1))
            {
                throw new ClusterException("invalid state counter code: " + code);
            }

            return STATES[(int)code];
        }

        /**
         * Get the current state of the {@link ConsensusModule}.
         *
         * @param counters  to search within.
         * @param clusterId to which the allocated counter belongs.
         * @return the state of the ConsensusModule or null if not found.
         */
        public static State find(final CountersReader counters, final int clusterId)
        {
            final int counterId = ClusterCounters.find(counters, CONSENSUS_MODULE_STATE_TYPE_ID, clusterId);
            if (Aeron.NULL_VALUE != counterId)
            {
                return State.get(counters.getCounterValue(counterId));
            }

            return null;
        }
    }

    /**
     * Launch an {@link ConsensusModule} with that communicates with an out of process {@link io.aeron.archive.Archive}
     * and {@link io.aeron.driver.MediaDriver} then awaits shutdown signal.
     *
     * @param args command line argument which is a list for properties files as URLs or filenames.
     */
    public static void main(final String[] args)
    {
        loadPropertiesFiles(args);

        try (ConsensusModule consensusModule = launch())
        {
            consensusModule.context().shutdownSignalBarrier().await();
            System.out.println("Shutdown ConsensusModule...");
        }
    }

    private final Context ctx;
    private final ConsensusModuleAgent conductor;
    private final AgentRunner conductorRunner;
    private final AgentInvoker conductorInvoker;

    ConsensusModule(final Context ctx)
    {
        try
        {
            ctx.conclude();
            this.ctx = ctx;

            conductor = new ConsensusModuleAgent(ctx);

            if (ctx.useAgentInvoker())
            {
                conductorInvoker = new AgentInvoker(ctx.errorHandler(), ctx.errorCounter(), conductor);
                conductorRunner = null;
            }
            else
            {
                conductorRunner = new AgentRunner(
                    ctx.idleStrategy(), ctx.errorHandler(), ctx.errorCounter(), conductor);
                conductorInvoker = null;
            }
        }
        catch (final ConcurrentConcludeException ex)
        {
            throw ex;
        }
        catch (final Exception ex)
        {
            if (null != ctx.clusterMarkFile())
            {
                ctx.clusterMarkFile().signalFailedStart();
                ctx.clusterMarkFile().force();
            }

            CloseHelper.quietClose(ctx::close);
            throw ex;
        }
    }

    /**
     * Launch an {@link ConsensusModule} using a default configuration.
     *
     * @return a new instance of an {@link ConsensusModule}.
     */
    public static ConsensusModule launch()
    {
        return launch(new Context());
    }

    /**
     * Launch an {@link ConsensusModule} by providing a configuration context.
     *
     * @param ctx for the configuration parameters.
     * @return a new instance of an {@link ConsensusModule}.
     */
    public static ConsensusModule launch(final Context ctx)
    {
        final ConsensusModule consensusModule = new ConsensusModule(ctx);

        if (null != consensusModule.conductorRunner)
        {
            AgentRunner.startOnThread(consensusModule.conductorRunner, ctx.threadFactory());
        }
        else
        {
            consensusModule.conductorInvoker.start();
        }

        return consensusModule;
    }

    /**
     * Get the {@link ConsensusModule.Context} that is used by this {@link ConsensusModule}.
     *
     * @return the {@link ConsensusModule.Context} that is used by this {@link ConsensusModule}.
     */
    public Context context()
    {
        return ctx;
    }

    /**
     * Get the {@link AgentInvoker} for the consensus module.
     *
     * @return the {@link AgentInvoker} for the consensus module.
     */
    public AgentInvoker conductorAgentInvoker()
    {
        return conductorInvoker;
    }

    /**
     * {@inheritDoc}
     */
    public void close()
    {
        CloseHelper.closeAll(conductorRunner, conductorInvoker);
    }

    /**
     * Configuration options for cluster.
     */
    @Config(existsInC = false)
    public static final class Configuration
    {
        /**
         * Major version of the network protocol from consensus module to consensus module. If these don't match then
         * consensus modules are not compatible.
         */
        public static final int PROTOCOL_MAJOR_VERSION = 1;

        /**
         * Minor version of the network protocol from consensus module to consensus module. If these don't match then
         * some features may not be available.
         */
        public static final int PROTOCOL_MINOR_VERSION = 0;

        /**
         * Patch version of the network protocol from consensus module to consensus module. If these don't match then
         * bug fixes may not have been applied.
         */
        public static final int PROTOCOL_PATCH_VERSION = 0;

        /**
         * Combined semantic version for the client to consensus module protocol.
         *
         * @see SemanticVersion
         */
        public static final int PROTOCOL_SEMANTIC_VERSION = SemanticVersion.compose(
            PROTOCOL_MAJOR_VERSION, PROTOCOL_MINOR_VERSION, PROTOCOL_PATCH_VERSION);

        /**
         * Type of snapshot for this component.
         */
        public static final long SNAPSHOT_TYPE_ID = 1;

        /**
         * Property name for the limit for fragments to be consumed on each poll of ingress.
         */
        @Config
        public static final String CLUSTER_INGRESS_FRAGMENT_LIMIT_PROP_NAME = "aeron.cluster.ingress.fragment.limit";

        /**
         * Default for the limit for fragments to be consumed on each poll of ingress.
         */
        @Config
        public static final int CLUSTER_INGRESS_FRAGMENT_LIMIT_DEFAULT = 50;

        /**
         * Property name for whether IPC ingress is allowed or not.
         */
        @Config
        public static final String CLUSTER_INGRESS_IPC_ALLOWED_PROP_NAME = "aeron.cluster.ingress.ipc.allowed";

        /**
         * Default for whether IPC ingress is allowed or not.
         */
        @Config
        public static final String CLUSTER_INGRESS_IPC_ALLOWED_DEFAULT = "false";

        /**
         * Service ID to identify a snapshot in the {@link RecordingLog} for the consensus module.
         */
        public static final int SERVICE_ID = Aeron.NULL_VALUE;

        /**
         * Property name for the identity of the cluster member.
         */
        @Config
        public static final String CLUSTER_MEMBER_ID_PROP_NAME = "aeron.cluster.member.id";

        /**
         * Default property for the cluster member identity.
         */
        @Config
        public static final int CLUSTER_MEMBER_ID_DEFAULT = 0;

        /**
         * Property name for the identity of the appointed leader. This is when automated leader elections are
         * not employed.
         * 

* This feature is for testing and not recommended for production usage. */ @Config public static final String APPOINTED_LEADER_ID_PROP_NAME = "aeron.cluster.appointed.leader.id"; /** * Default property for the appointed cluster leader id. A value of {@link Aeron#NULL_VALUE} means no leader * has been appointed and thus an automated leader election should occur. */ @Config public static final int APPOINTED_LEADER_ID_DEFAULT = Aeron.NULL_VALUE; /** * Property name for the comma separated list of cluster member endpoints. *

* * 0,ingress:port,consensus:port,log:port,catchup:port,archive:port| \ * 1,ingress:port,consensus:port,log:port,catchup:port,archive:port| ... * *

* The ingress endpoints will be used as the endpoint substituted into the * {@link io.aeron.cluster.client.AeronCluster.Configuration#INGRESS_CHANNEL_PROP_NAME} if the endpoint * is not provided when unicast. */ @Config(defaultType = DefaultType.STRING, defaultString = "") public static final String CLUSTER_MEMBERS_PROP_NAME = "aeron.cluster.members"; /** * Property name for the comma separated list of cluster consensus endpoints used for dynamic join, cluster * backup and cluster standby nodes. */ @Config public static final String CLUSTER_CONSENSUS_ENDPOINTS_PROP_NAME = "aeron.cluster.consensus.endpoints"; /** * Default property for the list of cluster consensus endpoints. */ @Config public static final String CLUSTER_CONSENSUS_ENDPOINTS_DEFAULT = ""; /** * Property name for whether cluster member information in snapshots should be ignored on load or not. */ @Config public static final String CLUSTER_MEMBERS_IGNORE_SNAPSHOT_PROP_NAME = "aeron.cluster.members.ignore.snapshot"; /** * Default property for whether cluster member information in snapshots should be ignored or not. */ @Config public static final String CLUSTER_MEMBERS_IGNORE_SNAPSHOT_DEFAULT = "false"; /** * Channel for the clustered log. */ @Config public static final String LOG_CHANNEL_PROP_NAME = "aeron.cluster.log.channel"; /** * Channel for the clustered log. This channel can exist for a potentially long time given cluster operation * so attention should be given to configuration such as term-length and mtu. */ @Config public static final String LOG_CHANNEL_DEFAULT = "aeron:udp?term-length=64m"; /** * Property name for the comma separated list of member endpoints. *

* * ingress:port,consensus:port,log:port,catchup:port,archive:port * * * @see #CLUSTER_MEMBERS_PROP_NAME */ @Config public static final String MEMBER_ENDPOINTS_PROP_NAME = "aeron.cluster.member.endpoints"; /** * Default property for member endpoints. */ @Config public static final String MEMBER_ENDPOINTS_DEFAULT = ""; /** * Stream id within a channel for the clustered log. */ @Config public static final String LOG_STREAM_ID_PROP_NAME = "aeron.cluster.log.stream.id"; /** * Stream id within a channel for the clustered log. */ @Config public static final int LOG_STREAM_ID_DEFAULT = 100; /** * Channel to be used for archiving snapshots. */ public static final String SNAPSHOT_CHANNEL_DEFAULT = "aeron:ipc?alias=snapshot"; /** * Stream id for the archived snapshots within a channel. */ public static final int SNAPSHOT_STREAM_ID_DEFAULT = 107; /** * Message detail to be sent when max concurrent session limit is reached. */ public static final String SESSION_LIMIT_MSG = "concurrent session limit"; /** * Message detail to be sent when a session is rejected due to authentication. */ public static final String SESSION_REJECTED_MSG = "session failed authentication"; /** * Message detail to be sent when a session has an invalid client version. */ public static final String SESSION_INVALID_VERSION_MSG = "invalid client version"; /** * Channel to be used communicating cluster consensus to each other. This can be used for default * configuration with the endpoints replaced with those provided by {@link #CLUSTER_MEMBERS_PROP_NAME}. */ @Config public static final String CONSENSUS_CHANNEL_PROP_NAME = "aeron.cluster.consensus.channel"; /** * Channel to be used for communicating cluster consensus to each other. This can be used for default * configuration with the endpoints replaced with those provided by {@link #CLUSTER_MEMBERS_PROP_NAME}. */ @Config public static final String CONSENSUS_CHANNEL_DEFAULT = "aeron:udp?term-length=64k"; /** * Stream id within a channel for communicating consensus messages. */ @Config public static final String CONSENSUS_STREAM_ID_PROP_NAME = "aeron.cluster.consensus.stream.id"; /** * Stream id for the communicating consensus messages. */ @Config public static final int CONSENSUS_STREAM_ID_DEFAULT = 108; /** * Channel to be used for replicating logs and snapshots from other archives to the local one. */ @Config(defaultType = DefaultType.STRING, defaultString = "") public static final String REPLICATION_CHANNEL_PROP_NAME = "aeron.cluster.replication.channel"; /** * Channel template used for replaying logs to a follower using the {@link ClusterMember#catchupEndpoint()}. */ @Config public static final String FOLLOWER_CATCHUP_CHANNEL_PROP_NAME = "aeron.cluster.follower.catchup.channel"; /** * Default channel template used for replaying logs to a follower using the * {@link ClusterMember#catchupEndpoint()}. */ @Config public static final String FOLLOWER_CATCHUP_CHANNEL_DEFAULT = UDP_CHANNEL; /** * Channel used to build the control request channel for the leader Archive. */ @Config public static final String LEADER_ARCHIVE_CONTROL_CHANNEL_PROP_NAME = "aeron.cluster.leader.archive.control.channel"; /** * Default channel used to build the control request channel for the leader Archive. */ @Config public static final String LEADER_ARCHIVE_CONTROL_CHANNEL_DEFAULT = "aeron:udp?term-length=64k"; /** * Counter type id for the consensus module state. */ public static final int CONSENSUS_MODULE_STATE_TYPE_ID = AeronCounters.CLUSTER_CONSENSUS_MODULE_STATE_TYPE_ID; /** * Counter type id for the cluster node role. */ public static final int CLUSTER_NODE_ROLE_TYPE_ID = ClusteredServiceContainer.Configuration.CLUSTER_NODE_ROLE_TYPE_ID; /** * Counter type id for the control toggle. */ public static final int CONTROL_TOGGLE_TYPE_ID = ClusterControl.CONTROL_TOGGLE_TYPE_ID; /** * Counter type id of a commit position. */ public static final int COMMIT_POSITION_TYPE_ID = ClusteredServiceContainer.Configuration.COMMIT_POSITION_TYPE_ID; /** * Type id of a recovery state counter. */ public static final int RECOVERY_STATE_TYPE_ID = AeronCounters.CLUSTER_RECOVERY_STATE_TYPE_ID; /** * Counter type id for count of snapshots taken. */ public static final int SNAPSHOT_COUNTER_TYPE_ID = AeronCounters.CLUSTER_SNAPSHOT_COUNTER_TYPE_ID; /** * Type id for election state counter. */ public static final int ELECTION_STATE_TYPE_ID = AeronCounters.CLUSTER_ELECTION_STATE_TYPE_ID; /** * Counter type id for the consensus module error count. */ public static final int CONSENSUS_MODULE_ERROR_COUNT_TYPE_ID = AeronCounters.CLUSTER_CONSENSUS_MODULE_ERROR_COUNT_TYPE_ID; /** * Counter type id for the number of cluster clients which have been timed out. */ public static final int CLUSTER_CLIENT_TIMEOUT_COUNT_TYPE_ID = AeronCounters.CLUSTER_CLIENT_TIMEOUT_COUNT_TYPE_ID; /** * Counter type id for the number of invalid requests which the cluster has received. */ public static final int CLUSTER_INVALID_REQUEST_COUNT_TYPE_ID = AeronCounters.CLUSTER_INVALID_REQUEST_COUNT_TYPE_ID; /** * The number of services in this cluster instance. * * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SERVICE_ID_PROP_NAME */ @Config public static final String SERVICE_COUNT_PROP_NAME = "aeron.cluster.service.count"; /** * The number of services in this cluster instance. */ @Config public static final int SERVICE_COUNT_DEFAULT = 1; /** * Maximum number of cluster sessions that can be active concurrently. */ @Config public static final String MAX_CONCURRENT_SESSIONS_PROP_NAME = "aeron.cluster.max.sessions"; /** * Maximum number of cluster sessions that can be active concurrently. */ @Config public static final int MAX_CONCURRENT_SESSIONS_DEFAULT = 10; /** * Timeout for a session if no activity is observed. */ @Config public static final String SESSION_TIMEOUT_PROP_NAME = "aeron.cluster.session.timeout"; /** * Timeout for a session if no activity is observed. */ @Config(defaultType = DefaultType.LONG, defaultLong = 10L * 1000 * 1000 * 1000) public static final long SESSION_TIMEOUT_DEFAULT_NS = TimeUnit.SECONDS.toNanos(10); /** * Timeout for a leader if no heartbeat is received by another member. */ @Config public static final String LEADER_HEARTBEAT_TIMEOUT_PROP_NAME = "aeron.cluster.leader.heartbeat.timeout"; /** * Timeout for a leader if no heartbeat is received by another member. */ @Config(defaultType = DefaultType.LONG, defaultLong = 10L * 1000 * 1000 * 1000) public static final long LEADER_HEARTBEAT_TIMEOUT_DEFAULT_NS = TimeUnit.SECONDS.toNanos(10); /** * Interval at which a leader will send heartbeats if the log is not progressing. */ @Config public static final String LEADER_HEARTBEAT_INTERVAL_PROP_NAME = "aeron.cluster.leader.heartbeat.interval"; /** * Interval at which a leader will send heartbeats if the log is not progressing. */ @Config(defaultType = DefaultType.LONG, defaultLong = 200L * 1000 * 1000) public static final long LEADER_HEARTBEAT_INTERVAL_DEFAULT_NS = TimeUnit.MILLISECONDS.toNanos(200); /** * Timeout after which an election vote will be attempted after startup while waiting to canvass the status * of members if a majority has been heard from. */ @Config public static final String STARTUP_CANVASS_TIMEOUT_PROP_NAME = "aeron.cluster.startup.canvass.timeout"; /** * Default timeout after which an election vote will be attempted on startup when waiting to canvass the * status of all members before going for a majority if possible. */ @Config(defaultType = DefaultType.LONG, defaultLong = 60L * 1000 * 1000 * 1000) public static final long STARTUP_CANVASS_TIMEOUT_DEFAULT_NS = TimeUnit.SECONDS.toNanos(60); /** * Timeout after which an election fails if the candidate does not get a majority of votes. */ @Config public static final String ELECTION_TIMEOUT_PROP_NAME = "aeron.cluster.election.timeout"; /** * Default timeout after which an election fails if the candidate does not get a majority of votes. */ @Config(defaultType = DefaultType.LONG, defaultLong = 1000L * 1000 * 1000) public static final long ELECTION_TIMEOUT_DEFAULT_NS = TimeUnit.SECONDS.toNanos(1); /** * Interval at which a member will send out status updates during election phases. */ @Config public static final String ELECTION_STATUS_INTERVAL_PROP_NAME = "aeron.cluster.election.status.interval"; /** * Default interval at which a member will send out status updates during election phases. */ @Config(defaultType = DefaultType.LONG, defaultLong = 100L * 1000 * 1000) public static final long ELECTION_STATUS_INTERVAL_DEFAULT_NS = TimeUnit.MILLISECONDS.toNanos(100); /** * Interval at which a dynamic joining member will send add cluster member and snapshot recording * queries. */ @Config(hasContext = false) public static final String DYNAMIC_JOIN_INTERVAL_PROP_NAME = "aeron.cluster.dynamic.join.interval"; /** * Default interval at which a dynamic joining member will send add cluster member and snapshot recording * queries. */ @Config(defaultType = DefaultType.LONG, defaultLong = 1000L * 1000 * 1000) public static final long DYNAMIC_JOIN_INTERVAL_DEFAULT_NS = TimeUnit.SECONDS.toNanos(1); /** * Name of the system property for specifying a supplier of {@link Authenticator} for the cluster. */ @Config(defaultType = DefaultType.STRING, defaultString = "io.aeron.security.DefaultAuthenticatorSupplier") public static final String AUTHENTICATOR_SUPPLIER_PROP_NAME = "aeron.cluster.authenticator.supplier"; /** * Name of the system property for specifying a supplier of {@link AuthorisationService} for the cluster. */ @Config(defaultType = DefaultType.STRING, defaultString = "") public static final String AUTHORISATION_SERVICE_SUPPLIER_PROP_NAME = "aeron.cluster.authorisation.service.supplier"; static final AuthorisationService ALLOW_ONLY_BACKUP_QUERIES = (protocolId, actionId, type, encodedPrincipal) -> MessageHeaderDecoder.SCHEMA_ID == protocolId && BackupQueryDecoder.TEMPLATE_ID == actionId; /** * Default {@link AuthorisationServiceSupplier} that returns {@link AuthorisationService} that forbids all * command from being executed (i.e. {@link AuthorisationService#DENY_ALL}). */ public static final AuthorisationServiceSupplier DEFAULT_AUTHORISATION_SERVICE_SUPPLIER = () -> ALLOW_ONLY_BACKUP_QUERIES; /** * Size in bytes of the error buffer for the cluster. */ @Config public static final String ERROR_BUFFER_LENGTH_PROP_NAME = "aeron.cluster.error.buffer.length"; /** * Size in bytes of the error buffer for the cluster. */ @Config public static final int ERROR_BUFFER_LENGTH_DEFAULT = ClusterMarkFile.ERROR_BUFFER_MIN_LENGTH; /** * Timeout a leader will wait on getting termination ACKs from followers. */ @Config public static final String TERMINATION_TIMEOUT_PROP_NAME = "aeron.cluster.termination.timeout"; /** * Property name for threshold value for the consensus module agent work cycle threshold to track * for being exceeded. */ @Config public static final String CYCLE_THRESHOLD_PROP_NAME = "aeron.cluster.cycle.threshold"; /** * Default threshold value for the consensus module agent work cycle threshold to track for being exceeded. */ @Config(defaultType = DefaultType.LONG, defaultLong = 1000L * 1000 * 1000) public static final long CYCLE_THRESHOLD_DEFAULT_NS = TimeUnit.MILLISECONDS.toNanos(1000); /** * Property name for threshold value, which is used for tracking total snapshot duration breaches. */ @Config public static final String TOTAL_SNAPSHOT_DURATION_THRESHOLD_PROP_NAME = "aeron.cluster.total.snapshot.threshold"; /** * Default threshold value, which is used for tracking total snapshot duration breaches. */ @Config(defaultType = DefaultType.LONG, defaultLong = 1000L * 1000 * 1000) public static final long TOTAL_SNAPSHOT_DURATION_THRESHOLD_DEFAULT_NS = TimeUnit.MILLISECONDS.toNanos(1000); /** * Default timeout a leader will wait on getting termination ACKs from followers. */ @Config(defaultType = DefaultType.LONG, defaultLong = 10L * 1000 * 1000 * 1000) public static final long TERMINATION_TIMEOUT_DEFAULT_NS = TimeUnit.SECONDS.toNanos(10); /** * Resolution in nanoseconds for each tick of the timer wheel for scheduling deadlines. */ @Config public static final String WHEEL_TICK_RESOLUTION_PROP_NAME = "aeron.cluster.wheel.tick.resolution"; /** * Resolution in nanoseconds for each tick of the timer wheel for scheduling deadlines. Defaults to 8ms. */ @Config(defaultType = DefaultType.LONG, defaultLong = 4L * 1000 * 1000) public static final long WHEEL_TICK_RESOLUTION_DEFAULT_NS = TimeUnit.MILLISECONDS.toNanos(8); /** * Number of ticks, or spokes, on the timer wheel. Higher number of ticks reduces potential conflicts * traded off against memory usage. */ @Config public static final String TICKS_PER_WHEEL_PROP_NAME = "aeron.cluster.ticks.per.wheel"; /** * Number of ticks, or spokes, on the timer wheel. Higher number of ticks reduces potential conflicts * traded off against memory usage. Defaults to 128 per wheel. */ @Config public static final int TICKS_PER_WHEEL_DEFAULT = 128; /** * The level at which files should be sync'ed to disk. *

    *
  • 0 - normal writes.
  • *
  • 1 - sync file data.
  • *
  • 2 - sync file data + metadata.
  • *
*/ @Config public static final String FILE_SYNC_LEVEL_PROP_NAME = "aeron.cluster.file.sync.level"; /** * Default file sync level of normal writes. */ @Config public static final int FILE_SYNC_LEVEL_DEFAULT = 0; /** * {@link TimerServiceSupplier} to be used for creating the {@link TimerService} used by consensus module. */ @Config public static final String TIMER_SERVICE_SUPPLIER_PROP_NAME = "aeron.cluster.timer.service.supplier"; /** * Name of the {@link TimerServiceSupplier} that creates {@link TimerService} based on the timer wheel * implementation. */ public static final String TIMER_SERVICE_SUPPLIER_WHEEL = "io.aeron.cluster.WheelTimerServiceSupplier"; /** * Name of the {@link TimerServiceSupplier} that creates a sequence-preserving {@link TimerService} based * on a priority heap implementation. */ public static final String TIMER_SERVICE_SUPPLIER_PRIORITY_HEAP = "io.aeron.cluster.PriorityHeapTimerServiceSupplier"; /** * Default {@link TimerServiceSupplier}. */ @Config public static final String TIMER_SERVICE_SUPPLIER_DEFAULT = TIMER_SERVICE_SUPPLIER_WHEEL; /** * Property name for the name returned from {@link Agent#roleName()} for the consensus module. */ @Config(defaultType = DefaultType.STRING, defaultString = "") public static final String CLUSTER_CONSENSUS_MODULE_AGENT_ROLE_NAME_PROP_NAME = "aeron.cluster.consensus.module.agent.role.name"; /** * Property name for replication progress timeout. * * @since 1.41.0 */ @Config public static final String CLUSTER_REPLICATION_PROGRESS_TIMEOUT_PROP_NAME = "aeron.cluster.replication.progress.timeout"; /** * Default timeout for replication progress in nanoseconds. * * @since 1.41.0 */ @Config(defaultType = DefaultType.LONG, defaultLong = 10L * 1000 * 1000 * 1000) public static final long CLUSTER_REPLICATION_PROGRESS_TIMEOUT_DEFAULT_NS = TimeUnit.SECONDS.toNanos(10); /** * Property name for replication progress interval. * * @since 1.41.0 */ @Config(defaultType = DefaultType.LONG, defaultLong = 0) public static final String CLUSTER_REPLICATION_PROGRESS_INTERVAL_PROP_NAME = "aeron.cluster.replication.progress.interval"; /** * Property name of enabling the acceptance of standby snapshots */ @Config(defaultType = DefaultType.BOOLEAN, defaultBoolean = false) public static final String CLUSTER_ACCEPT_STANDBY_SNAPSHOTS_PROP_NAME = "aeron.cluster.accept.standby.snapshots"; /** * Property name of setting {@link ClusterClock}. Should specify a fully qualified class name. * Defaults to {@link MillisecondClusterClock}. * * @since 1.44.0 */ @Config(defaultType = DefaultType.STRING, defaultString = "io.aeron.cluster.MillisecondClusterClock") public static final String CLUSTER_CLOCK_PROP_NAME = "aeron.cluster.clock"; /** * Property name of setting {@link ConsensusModuleExtension}. Should specify a fully qualified class name. * * @since 1.45.0 */ public static final String CONSENSUS_MODULE_EXTENSION_CLASS_NAME_PROP_NAME = "aeron.cluster.consensus.module.extension"; /** * The value {@link #CLUSTER_INGRESS_FRAGMENT_LIMIT_DEFAULT} or system property * {@link #CLUSTER_INGRESS_FRAGMENT_LIMIT_PROP_NAME} if set. * * @return {@link #CLUSTER_INGRESS_FRAGMENT_LIMIT_DEFAULT} or system property * {@link #CLUSTER_INGRESS_FRAGMENT_LIMIT_PROP_NAME} if set. */ public static int ingressFragmentLimit() { return Integer.getInteger(CLUSTER_INGRESS_FRAGMENT_LIMIT_PROP_NAME, CLUSTER_INGRESS_FRAGMENT_LIMIT_DEFAULT); } /** * The value {@link #CLUSTER_INGRESS_IPC_ALLOWED_DEFAULT} or system property * {@link #CLUSTER_INGRESS_IPC_ALLOWED_PROP_NAME} if set. * * @return {@link #CLUSTER_INGRESS_IPC_ALLOWED_DEFAULT} or system property * {@link #CLUSTER_INGRESS_IPC_ALLOWED_PROP_NAME} if set. */ public static boolean isIpcIngressAllowed() { return "true".equalsIgnoreCase(System.getProperty( CLUSTER_INGRESS_IPC_ALLOWED_PROP_NAME, CLUSTER_INGRESS_IPC_ALLOWED_DEFAULT)); } /** * The value {@link #CLUSTER_MEMBER_ID_DEFAULT} or system property * {@link #CLUSTER_MEMBER_ID_PROP_NAME} if set. * * @return {@link #CLUSTER_MEMBER_ID_DEFAULT} or system property * {@link #CLUSTER_MEMBER_ID_PROP_NAME} if set. */ public static int clusterMemberId() { return Integer.getInteger(CLUSTER_MEMBER_ID_PROP_NAME, CLUSTER_MEMBER_ID_DEFAULT); } /** * The value {@link #APPOINTED_LEADER_ID_DEFAULT} or system property * {@link #APPOINTED_LEADER_ID_PROP_NAME} if set. *

* This feature is for testing and not recommended for production usage. * * @return {@link #APPOINTED_LEADER_ID_DEFAULT} or system property * {@link #APPOINTED_LEADER_ID_PROP_NAME} if set. */ public static int appointedLeaderId() { return Integer.getInteger(APPOINTED_LEADER_ID_PROP_NAME, APPOINTED_LEADER_ID_DEFAULT); } /** * The value of system property {@link #CLUSTER_MEMBERS_PROP_NAME} if set, null otherwise. * * @return of system property {@link #CLUSTER_MEMBERS_PROP_NAME} if set. */ public static String clusterMembers() { return System.getProperty(CLUSTER_MEMBERS_PROP_NAME); } /** * The value {@link #CLUSTER_CONSENSUS_ENDPOINTS_DEFAULT} or system property * {@link #CLUSTER_CONSENSUS_ENDPOINTS_PROP_NAME} if set. * * @return {@link #CLUSTER_CONSENSUS_ENDPOINTS_DEFAULT} or system property * {@link #CLUSTER_CONSENSUS_ENDPOINTS_PROP_NAME} it set. */ public static String clusterConsensusEndpoints() { return System.getProperty(CLUSTER_CONSENSUS_ENDPOINTS_PROP_NAME, CLUSTER_CONSENSUS_ENDPOINTS_DEFAULT); } /** * The value {@link #CLUSTER_MEMBERS_IGNORE_SNAPSHOT_DEFAULT} or system property * {@link #CLUSTER_MEMBERS_IGNORE_SNAPSHOT_PROP_NAME} if set. * * @return {@link #CLUSTER_MEMBERS_IGNORE_SNAPSHOT_DEFAULT} or system property * {@link #CLUSTER_MEMBERS_IGNORE_SNAPSHOT_PROP_NAME} it set. */ public static boolean clusterMembersIgnoreSnapshot() { return "true".equalsIgnoreCase(System.getProperty( CLUSTER_MEMBERS_IGNORE_SNAPSHOT_PROP_NAME, CLUSTER_MEMBERS_IGNORE_SNAPSHOT_DEFAULT)); } /** * The value {@link #LOG_CHANNEL_DEFAULT} or system property {@link #LOG_CHANNEL_PROP_NAME} if set. * * @return {@link #LOG_CHANNEL_DEFAULT} or system property {@link #LOG_CHANNEL_PROP_NAME} if set. */ public static String logChannel() { return System.getProperty(LOG_CHANNEL_PROP_NAME, LOG_CHANNEL_DEFAULT); } /** * The value {@link #LOG_STREAM_ID_DEFAULT} or system property {@link #LOG_STREAM_ID_PROP_NAME} if set. * * @return {@link #LOG_STREAM_ID_DEFAULT} or system property {@link #LOG_STREAM_ID_PROP_NAME} if set. */ public static int logStreamId() { return Integer.getInteger(LOG_STREAM_ID_PROP_NAME, LOG_STREAM_ID_DEFAULT); } /** * The value {@link #MEMBER_ENDPOINTS_DEFAULT} or system property {@link #MEMBER_ENDPOINTS_PROP_NAME} if set. * * @return {@link #MEMBER_ENDPOINTS_DEFAULT} or system property {@link #MEMBER_ENDPOINTS_PROP_NAME} if set. */ public static String memberEndpoints() { return System.getProperty(MEMBER_ENDPOINTS_PROP_NAME, MEMBER_ENDPOINTS_DEFAULT); } /** * The value {@link #SNAPSHOT_CHANNEL_DEFAULT} or system property * {@link io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SNAPSHOT_CHANNEL_PROP_NAME} if set. * * @return {@link #SNAPSHOT_CHANNEL_DEFAULT} or system property * {@link io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SNAPSHOT_CHANNEL_PROP_NAME} if set. */ public static String snapshotChannel() { return System.getProperty( ClusteredServiceContainer.Configuration.SNAPSHOT_CHANNEL_PROP_NAME, SNAPSHOT_CHANNEL_DEFAULT); } /** * The value {@link #SNAPSHOT_STREAM_ID_DEFAULT} or system property * {@link io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SNAPSHOT_STREAM_ID_PROP_NAME} if set. * * @return {@link #SNAPSHOT_STREAM_ID_DEFAULT} or system property * {@link io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SNAPSHOT_STREAM_ID_PROP_NAME} if set. */ public static int snapshotStreamId() { return Integer.getInteger( ClusteredServiceContainer.Configuration.SNAPSHOT_STREAM_ID_PROP_NAME, SNAPSHOT_STREAM_ID_DEFAULT); } /** * The value {@link #SERVICE_COUNT_DEFAULT} or system property * {@link #SERVICE_COUNT_PROP_NAME} if set. * * @return {@link #SERVICE_COUNT_DEFAULT} or system property * {@link #SERVICE_COUNT_PROP_NAME} if set. */ public static int serviceCount() { return Integer.getInteger(SERVICE_COUNT_PROP_NAME, SERVICE_COUNT_DEFAULT); } /** * The value {@link #MAX_CONCURRENT_SESSIONS_DEFAULT} or system property * {@link #MAX_CONCURRENT_SESSIONS_PROP_NAME} if set. * * @return {@link #MAX_CONCURRENT_SESSIONS_DEFAULT} or system property * {@link #MAX_CONCURRENT_SESSIONS_PROP_NAME} if set. */ public static int maxConcurrentSessions() { return Integer.getInteger(MAX_CONCURRENT_SESSIONS_PROP_NAME, MAX_CONCURRENT_SESSIONS_DEFAULT); } /** * Timeout for a session if no activity is observed. * * @return timeout in nanoseconds to wait for activity * @see #SESSION_TIMEOUT_PROP_NAME */ public static long sessionTimeoutNs() { return getDurationInNanos(SESSION_TIMEOUT_PROP_NAME, SESSION_TIMEOUT_DEFAULT_NS); } /** * Timeout for a leader if no heartbeat is received by another member. * * @return timeout in nanoseconds to wait for heartbeat from a leader. * @see #LEADER_HEARTBEAT_TIMEOUT_PROP_NAME */ public static long leaderHeartbeatTimeoutNs() { return getDurationInNanos(LEADER_HEARTBEAT_TIMEOUT_PROP_NAME, LEADER_HEARTBEAT_TIMEOUT_DEFAULT_NS); } /** * Interval at which a leader will send a heartbeat if the log is not progressing. * * @return timeout in nanoseconds to for leader heartbeats when no log being appended. * @see #LEADER_HEARTBEAT_INTERVAL_PROP_NAME */ public static long leaderHeartbeatIntervalNs() { return getDurationInNanos(LEADER_HEARTBEAT_INTERVAL_PROP_NAME, LEADER_HEARTBEAT_INTERVAL_DEFAULT_NS); } /** * Timeout waiting to canvass the status of cluster members before voting if a majority have been heard from. * * @return timeout in nanoseconds to wait for the status of other cluster members before voting. * @see #STARTUP_CANVASS_TIMEOUT_PROP_NAME */ public static long startupCanvassTimeoutNs() { return getDurationInNanos(STARTUP_CANVASS_TIMEOUT_PROP_NAME, STARTUP_CANVASS_TIMEOUT_DEFAULT_NS); } /** * Timeout waiting for votes to become leader in an election. * * @return timeout in nanoseconds to wait for votes to become leader in an election. * @see #ELECTION_TIMEOUT_PROP_NAME */ public static long electionTimeoutNs() { return getDurationInNanos(ELECTION_TIMEOUT_PROP_NAME, ELECTION_TIMEOUT_DEFAULT_NS); } /** * Interval at which a member will send out status messages during the election phases. * * @return interval at which a member will send out status messages during the election phases. * @see #ELECTION_STATUS_INTERVAL_PROP_NAME */ public static long electionStatusIntervalNs() { return getDurationInNanos(ELECTION_STATUS_INTERVAL_PROP_NAME, ELECTION_STATUS_INTERVAL_DEFAULT_NS); } /** * Timeout waiting for follower termination by leader. * * @return timeout in nanoseconds to wait followers to terminate. * @see #TERMINATION_TIMEOUT_PROP_NAME */ public static long terminationTimeoutNs() { return getDurationInNanos(TERMINATION_TIMEOUT_PROP_NAME, TERMINATION_TIMEOUT_DEFAULT_NS); } /** * Get threshold value for the consensus module agent work cycle threshold to track for being exceeded. * * @return threshold value in nanoseconds. */ public static long cycleThresholdNs() { return getDurationInNanos(CYCLE_THRESHOLD_PROP_NAME, CYCLE_THRESHOLD_DEFAULT_NS); } /** * Get threshold value, which is used for monitoring total snapshot duration breaches of its predefined * threshold. * * @return threshold value in nanoseconds. */ public static long totalSnapshotDurationThresholdNs() { return getDurationInNanos( TOTAL_SNAPSHOT_DURATION_THRESHOLD_PROP_NAME, TOTAL_SNAPSHOT_DURATION_THRESHOLD_DEFAULT_NS); } /** * Size in bytes of the error buffer in the mark file. * * @return length of error buffer in bytes. * @see #ERROR_BUFFER_LENGTH_PROP_NAME */ public static int errorBufferLength() { return getSizeAsInt(ERROR_BUFFER_LENGTH_PROP_NAME, ERROR_BUFFER_LENGTH_DEFAULT); } /** * The value {@link DefaultAuthenticatorSupplier#INSTANCE} or system property * {@link #AUTHENTICATOR_SUPPLIER_PROP_NAME} if set. * * @return {@link DefaultAuthenticatorSupplier#INSTANCE} or system property * {@link #AUTHENTICATOR_SUPPLIER_PROP_NAME} if set. */ public static AuthenticatorSupplier authenticatorSupplier() { final String supplierClassName = System.getProperty(AUTHENTICATOR_SUPPLIER_PROP_NAME); if (Strings.isEmpty(supplierClassName)) { return DefaultAuthenticatorSupplier.INSTANCE; } AuthenticatorSupplier supplier = null; try { supplier = (AuthenticatorSupplier)Class.forName(supplierClassName).getConstructor().newInstance(); } catch (final Exception ex) { LangUtil.rethrowUnchecked(ex); } return supplier; } /** * The {@link AuthorisationServiceSupplier} specified in the * {@link #AUTHORISATION_SERVICE_SUPPLIER_PROP_NAME} system property or the * {@link #DEFAULT_AUTHORISATION_SERVICE_SUPPLIER}. * * @return system property {@link #AUTHORISATION_SERVICE_SUPPLIER_PROP_NAME} if set or * {@link #DEFAULT_AUTHORISATION_SERVICE_SUPPLIER} otherwise. */ public static AuthorisationServiceSupplier authorisationServiceSupplier() { final String supplierClassName = System.getProperty(AUTHORISATION_SERVICE_SUPPLIER_PROP_NAME); if (Strings.isEmpty(supplierClassName)) { return DEFAULT_AUTHORISATION_SERVICE_SUPPLIER; } try { return (AuthorisationServiceSupplier)Class.forName(supplierClassName).getConstructor().newInstance(); } catch (final Exception ex) { LangUtil.rethrowUnchecked(ex); return null; } } /** * The value {@link #CONSENSUS_CHANNEL_DEFAULT} or system property * {@link #CONSENSUS_CHANNEL_PROP_NAME} if set. * * @return {@link #CONSENSUS_CHANNEL_DEFAULT} or system property * {@link #CONSENSUS_CHANNEL_PROP_NAME} if set. */ public static String consensusChannel() { return System.getProperty(CONSENSUS_CHANNEL_PROP_NAME, CONSENSUS_CHANNEL_DEFAULT); } /** * The value {@link #CONSENSUS_STREAM_ID_DEFAULT} or system property * {@link #CONSENSUS_STREAM_ID_PROP_NAME} if set. * * @return {@link #CONSENSUS_STREAM_ID_DEFAULT} or system property * {@link #CONSENSUS_STREAM_ID_PROP_NAME} if set. */ public static int consensusStreamId() { return Integer.getInteger(CONSENSUS_STREAM_ID_PROP_NAME, CONSENSUS_STREAM_ID_DEFAULT); } /** * The system property for {@link #REPLICATION_CHANNEL_PROP_NAME} if set or null. * * @return system property {@link #REPLICATION_CHANNEL_PROP_NAME} if set or null. */ public static String replicationChannel() { return System.getProperty(REPLICATION_CHANNEL_PROP_NAME); } /** * The value {@link #FOLLOWER_CATCHUP_CHANNEL_DEFAULT} or system property * {@link #FOLLOWER_CATCHUP_CHANNEL_PROP_NAME} if set. * * @return {@link #FOLLOWER_CATCHUP_CHANNEL_DEFAULT} or system property * {@link #FOLLOWER_CATCHUP_CHANNEL_PROP_NAME} if set. */ public static String followerCatchupChannel() { return System.getProperty(FOLLOWER_CATCHUP_CHANNEL_PROP_NAME, FOLLOWER_CATCHUP_CHANNEL_DEFAULT); } /** * The value {@link #LEADER_ARCHIVE_CONTROL_CHANNEL_DEFAULT} or system property * {@link #LEADER_ARCHIVE_CONTROL_CHANNEL_PROP_NAME} if set. * * @return {@link #LEADER_ARCHIVE_CONTROL_CHANNEL_DEFAULT} or system property * {@link #LEADER_ARCHIVE_CONTROL_CHANNEL_PROP_NAME} if set. */ public static String leaderArchiveControlChannel() { return System.getProperty(LEADER_ARCHIVE_CONTROL_CHANNEL_PROP_NAME, LEADER_ARCHIVE_CONTROL_CHANNEL_DEFAULT); } /** * The value {@link #WHEEL_TICK_RESOLUTION_DEFAULT_NS} or system property * {@link #WHEEL_TICK_RESOLUTION_PROP_NAME} if set. * * @return {@link #WHEEL_TICK_RESOLUTION_DEFAULT_NS} or system property * {@link #WHEEL_TICK_RESOLUTION_PROP_NAME} if set. */ public static long wheelTickResolutionNs() { return getDurationInNanos(WHEEL_TICK_RESOLUTION_PROP_NAME, WHEEL_TICK_RESOLUTION_DEFAULT_NS); } /** * The value {@link #TICKS_PER_WHEEL_DEFAULT} or system property * {@link #CLUSTER_MEMBER_ID_PROP_NAME} if set. * * @return {@link #TICKS_PER_WHEEL_DEFAULT} or system property * {@link #TICKS_PER_WHEEL_PROP_NAME} if set. */ public static int ticksPerWheel() { return Integer.getInteger(TICKS_PER_WHEEL_PROP_NAME, TICKS_PER_WHEEL_DEFAULT); } /** * The level at which files should be sync'ed to disk. *

    *
  • 0 - normal writes.
  • *
  • 1 - sync file data.
  • *
  • 2 - sync file data + metadata.
  • *
* * @return level at which files should be sync'ed to disk. */ public static int fileSyncLevel() { return Integer.getInteger(FILE_SYNC_LEVEL_PROP_NAME, FILE_SYNC_LEVEL_DEFAULT); } /** * The name of the {@link TimerServiceSupplier} to use for supplying the {@link TimerService}. * * @return {@link #TIMER_SERVICE_SUPPLIER_DEFAULT} or system property. * {@link #TIMER_SERVICE_SUPPLIER_PROP_NAME} if set. */ public static String timerServiceSupplier() { return System.getProperty(TIMER_SERVICE_SUPPLIER_PROP_NAME, TIMER_SERVICE_SUPPLIER_DEFAULT); } /** * The name to be used for the {@link Agent#roleName()} for the consensus module agent. * * @return name to be used for the {@link Agent#roleName()} for the consensus module agent. * @see #CLUSTER_CONSENSUS_MODULE_AGENT_ROLE_NAME_PROP_NAME */ public static String agentRoleName() { return System.getProperty(CLUSTER_CONSENSUS_MODULE_AGENT_ROLE_NAME_PROP_NAME); } /** * The amount of time to wait to time out an archive replication when progress has stalled. * * @return system property {@link #CLUSTER_REPLICATION_PROGRESS_TIMEOUT_PROP_NAME} or * {@link #CLUSTER_REPLICATION_PROGRESS_TIMEOUT_DEFAULT_NS}. * @since 1.41.0 */ public static long replicationProgressTimeoutNs() { return SystemUtil.getDurationInNanos( CLUSTER_REPLICATION_PROGRESS_TIMEOUT_PROP_NAME, CLUSTER_REPLICATION_PROGRESS_TIMEOUT_DEFAULT_NS); } /** * Interval between checks for progress on an archive replication. * * @return system property {@link #CLUSTER_REPLICATION_PROGRESS_INTERVAL_PROP_NAME} or {@link Aeron#NULL_VALUE} * if not set. * @since 1.41.0 */ public static long replicationProgressIntervalNs() { return SystemUtil.getDurationInNanos(CLUSTER_REPLICATION_PROGRESS_INTERVAL_PROP_NAME, Aeron.NULL_VALUE); } /** * If this node should accept snapshots from standby nodes. * * @return value from property {@link #CLUSTER_ACCEPT_STANDBY_SNAPSHOTS_PROP_NAME} or false if not set. */ public static boolean acceptStandbySnapshots() { return Boolean.getBoolean(CLUSTER_ACCEPT_STANDBY_SNAPSHOTS_PROP_NAME); } /** * Create a new {@link ConsensusModuleExtension} based on the configured * {@link #CONSENSUS_MODULE_EXTENSION_CLASS_NAME_PROP_NAME}. * * @return a new {@link ConsensusModuleExtension} based on the configured * {@link #CONSENSUS_MODULE_EXTENSION_CLASS_NAME_PROP_NAME}. */ public static ConsensusModuleExtension newConsensusModuleExtension() { final String className = System.getProperty(Configuration.CONSENSUS_MODULE_EXTENSION_CLASS_NAME_PROP_NAME); if (null != className) { try { return (ConsensusModuleExtension)Class.forName(className).getConstructor().newInstance(); } catch (final Exception ex) { LangUtil.rethrowUnchecked(ex); } } return null; } } /** * Programmable overrides for configuring the {@link ConsensusModule} in a cluster. *

* The context will be owned by {@link ConsensusModuleAgent} after a successful * {@link ConsensusModule#launch(Context)} and closed via {@link ConsensusModule#close()}. */ public static final class Context implements Cloneable { /** * Using an integer because there is no support for boolean. 1 is concluded, 0 is not concluded. */ private static final AtomicIntegerFieldUpdater IS_CONCLUDED_UPDATER = newUpdater( Context.class, "isConcluded"); private volatile int isConcluded; private boolean ownsAeronClient = false; private String aeronDirectoryName = CommonContext.getAeronDirectoryName(); private Aeron aeron; private boolean deleteDirOnStart = false; private String clusterDirectoryName = ClusteredServiceContainer.Configuration.clusterDirName(); private String clusterServicesDirectoryName = ClusteredServiceContainer.Configuration.clusterServicesDirName(); private File clusterDir; private File markFileDir; private RecordingLog recordingLog; private ClusterMarkFile markFile; private NodeStateFile nodeStateFile; private int fileSyncLevel = Archive.Configuration.fileSyncLevel(); private int appVersion = SemanticVersion.compose(0, 0, 1); private int clusterId = ClusteredServiceContainer.Configuration.clusterId(); private int clusterMemberId = Configuration.clusterMemberId(); private int appointedLeaderId = Configuration.appointedLeaderId(); private String clusterMembers = Configuration.clusterMembers(); private String clusterConsensusEndpoints = Configuration.clusterConsensusEndpoints(); private boolean clusterMembersIgnoreSnapshot = Configuration.clusterMembersIgnoreSnapshot(); private String ingressChannel = AeronCluster.Configuration.ingressChannel(); private int ingressStreamId = AeronCluster.Configuration.ingressStreamId(); private boolean isIpcIngressAllowed = Configuration.isIpcIngressAllowed(); private int ingressFragmentLimit = Configuration.ingressFragmentLimit(); private String egressChannel = AeronCluster.Configuration.egressChannel(); private String logChannel = Configuration.logChannel(); private int logStreamId = Configuration.logStreamId(); private String memberEndpoints = Configuration.memberEndpoints(); private String replayChannel = ClusteredServiceContainer.Configuration.replayChannel(); private int replayStreamId = ClusteredServiceContainer.Configuration.replayStreamId(); private String controlChannel = ClusteredServiceContainer.Configuration.controlChannel(); private int consensusModuleStreamId = ClusteredServiceContainer.Configuration.consensusModuleStreamId(); private int serviceStreamId = ClusteredServiceContainer.Configuration.serviceStreamId(); private String snapshotChannel = Configuration.snapshotChannel(); private int snapshotStreamId = Configuration.snapshotStreamId(); private String consensusChannel = Configuration.consensusChannel(); private int consensusStreamId = Configuration.consensusStreamId(); private String replicationChannel = Configuration.replicationChannel(); private String followerCatchupChannel = Configuration.followerCatchupChannel(); private String leaderArchiveControlChannel = Configuration.leaderArchiveControlChannel(); private int logFragmentLimit = ClusteredServiceContainer.Configuration.logFragmentLimit(); private int serviceCount = Configuration.serviceCount(); private int errorBufferLength = Configuration.errorBufferLength(); private int maxConcurrentSessions = Configuration.maxConcurrentSessions(); private int ticksPerWheel = Configuration.ticksPerWheel(); private long wheelTickResolutionNs = Configuration.wheelTickResolutionNs(); private long sessionTimeoutNs = Configuration.sessionTimeoutNs(); private long leaderHeartbeatTimeoutNs = Configuration.leaderHeartbeatTimeoutNs(); private long leaderHeartbeatIntervalNs = Configuration.leaderHeartbeatIntervalNs(); private long startupCanvassTimeoutNs = Configuration.startupCanvassTimeoutNs(); private long electionTimeoutNs = Configuration.electionTimeoutNs(); private long electionStatusIntervalNs = Configuration.electionStatusIntervalNs(); private long terminationTimeoutNs = Configuration.terminationTimeoutNs(); private long cycleThresholdNs = Configuration.cycleThresholdNs(); private long totalSnapshotDurationThresholdNs = Configuration.totalSnapshotDurationThresholdNs(); private String agentRoleName = Configuration.agentRoleName(); private ThreadFactory threadFactory; private Supplier idleStrategySupplier; private ClusterClock clusterClock; private EpochClock epochClock; private Random random; private TimerServiceSupplier timerServiceSupplier; private Function clusterTimeConsumerSupplier; private ConsensusModuleExtension consensusModuleExtension; private DistinctErrorLog errorLog; private ErrorHandler errorHandler; private AtomicCounter errorCounter; private CountedErrorHandler countedErrorHandler; private Counter moduleStateCounter; private Counter electionStateCounter; private Counter clusterNodeRoleCounter; private Counter commitPosition; private Counter clusterControlToggle; private Counter nodeControlToggle; private Counter snapshotCounter; private Counter timedOutClientCounter; private Counter standbySnapshotCounter; private Counter electionCounter; private Counter leadershipTermId; private ShutdownSignalBarrier shutdownSignalBarrier; private Runnable terminationHook; private AeronArchive.Context archiveContext; private AuthenticatorSupplier authenticatorSupplier; private AuthorisationServiceSupplier authorisationServiceSupplier; private LogPublisher logPublisher; private EgressPublisher egressPublisher; private DutyCycleTracker dutyCycleTracker; private SnapshotDurationTracker totalSnapshotDurationTracker; private AppVersionValidator appVersionValidator; private boolean isLogMdc; private boolean useAgentInvoker = false; private ConsensusModuleStateExport boostrapState = null; private boolean acceptStandbySnapshots = Configuration.acceptStandbySnapshots(); /** * Perform a shallow copy of the object. * * @return a shallow copy of the object. */ public Context clone() { try { return (Context)super.clone(); } catch (final CloneNotSupportedException ex) { throw new RuntimeException(ex); } } /** * Conclude configuration by setting up defaults when specifics are not provided. */ @SuppressWarnings("MethodLength") public void conclude() { if (0 != IS_CONCLUDED_UPDATER.getAndSet(this, 1)) { throw new ConcurrentConcludeException(); } validateLogChannel(); if (null == clusterDir) { clusterDir = new File(clusterDirectoryName); } if (null == markFileDir) { final String dir = ClusteredServiceContainer.Configuration.markFileDir(); markFileDir = Strings.isEmpty(dir) ? clusterDir : new File(dir); } try { clusterDir = clusterDir.getCanonicalFile(); clusterDirectoryName = clusterDir.getAbsolutePath(); markFileDir = markFileDir.getCanonicalFile(); if (Strings.isEmpty(clusterServicesDirectoryName)) { clusterServicesDirectoryName = clusterDirectoryName; } else { clusterServicesDirectoryName = new File(clusterServicesDirectoryName).getCanonicalPath(); } } catch (final IOException ex) { throw new UncheckedIOException(ex); } if (null == clusterMembers) { throw new ClusterException("ConsensusModule.Context.clusterMembers must be set"); } if (deleteDirOnStart) { IoUtil.delete(clusterDir, false); } IoUtil.ensureDirectoryExists(clusterDir, "cluster"); IoUtil.ensureDirectoryExists(markFileDir, "mark file"); if (startupCanvassTimeoutNs / leaderHeartbeatTimeoutNs < 2) { throw new ClusterException("startupCanvassTimeoutNs=" + startupCanvassTimeoutNs + " must be a multiple of leaderHeartbeatTimeoutNs=" + leaderHeartbeatTimeoutNs); } if (null == clusterClock) { final String clockClassName = System.getProperty( CLUSTER_CLOCK_PROP_NAME, MillisecondClusterClock.class.getName()); try { clusterClock = (ClusterClock)Class.forName(clockClassName).getConstructor().newInstance(); } catch (final Exception e) { throw new ClusterException("failed to instantiate ClusterClock " + clockClassName, e); } } if (null == epochClock) { epochClock = SystemEpochClock.INSTANCE; } if (null == appVersionValidator) { appVersionValidator = AppVersionValidator.SEMANTIC_VERSIONING_VALIDATOR; } if (null == clusterTimeConsumerSupplier) { clusterTimeConsumerSupplier = (ctx) -> (timestamp) -> {}; } if (null == markFile) { markFile = new ClusterMarkFile( new File(markFileDir, ClusterMarkFile.FILENAME), ClusterComponentType.CONSENSUS_MODULE, errorBufferLength, epochClock, ClusteredServiceContainer.Configuration.LIVENESS_TIMEOUT_MS); } MarkFile.ensureMarkFileLink( clusterDir, new File(markFile.parentDirectory(), ClusterMarkFile.FILENAME), ClusterMarkFile.LINK_FILENAME); if (null == nodeStateFile) { try { nodeStateFile = new NodeStateFile(clusterDir, true, fileSyncLevel()); } catch (final IOException ex) { throw new ClusterException("unable to create node-state file", ex); } } if (Aeron.NULL_VALUE == nodeStateFile.candidateTerm().candidateTermId() && Aeron.NULL_VALUE != markFile.candidateTermId()) { nodeStateFile.updateCandidateTermId(markFile.candidateTermId(), Aeron.NULL_VALUE, epochClock.time()); } if (null == errorLog) { errorLog = new DistinctErrorLog(markFile.errorBuffer(), epochClock, StandardCharsets.US_ASCII); } errorHandler = CommonContext.setupErrorHandler(errorHandler, errorLog); if (null == recordingLog) { recordingLog = new RecordingLog(clusterDir, true); } if (Strings.isEmpty(agentRoleName)) { agentRoleName = "consensus-module-" + clusterId + "-" + clusterMemberId; } final ExpandableArrayBuffer buffer = new ExpandableArrayBuffer(); if (null == aeron) { ownsAeronClient = true; aeron = Aeron.connect( new Aeron.Context() .aeronDirectoryName(aeronDirectoryName) .errorHandler(errorHandler) .epochClock(epochClock) .useConductorAgentInvoker(true) .subscriberErrorHandler(RethrowingErrorHandler.INSTANCE) .awaitingIdleStrategy(YieldingIdleStrategy.INSTANCE) .clientLock(NoOpLock.INSTANCE) .clientName(agentRoleName)); if (null == errorCounter) { errorCounter = ClusterCounters.allocateVersioned( aeron, buffer, "Cluster Errors", CONSENSUS_MODULE_ERROR_COUNT_TYPE_ID, clusterId, ConsensusModuleVersion.VERSION, ConsensusModuleVersion.GIT_SHA); } } else { if (!aeron.context().useConductorAgentInvoker()) { throw new ClusterException( "Supplied Aeron client instance must set Aeron.Context.useConductorInvoker(true)"); } } if (null == ingressChannel) { throw new ClusterException("ingressChannel must be specified"); } if (!(aeron.context().subscriberErrorHandler() instanceof RethrowingErrorHandler)) { throw new ClusterException("Aeron client must use a RethrowingErrorHandler"); } if (null == aeron.conductorAgentInvoker()) { throw new ClusterException("Aeron client must use conductor agent invoker"); } if (null == errorCounter) { throw new ClusterException("error counter must be supplied if aeron client is"); } if (null == countedErrorHandler) { countedErrorHandler = new CountedErrorHandler(errorHandler, errorCounter); if (ownsAeronClient) { aeron.context().errorHandler(countedErrorHandler); } } if (null == moduleStateCounter) { final CountersReader counters = aeron.countersReader(); if (Aeron.NULL_VALUE != ClusterCounters.find(counters, CONSENSUS_MODULE_STATE_TYPE_ID, clusterId)) { throw new ClusterException("existing consensus module detected for clusterId=" + clusterId); } moduleStateCounter = ClusterCounters.allocate( aeron, buffer, "Consensus Module state", CONSENSUS_MODULE_STATE_TYPE_ID, clusterId); } validateCounterTypeId(aeron, moduleStateCounter, CONSENSUS_MODULE_STATE_TYPE_ID); if (null == electionStateCounter) { electionStateCounter = ClusterCounters.allocate( aeron, buffer, "Cluster election state", ELECTION_STATE_TYPE_ID, clusterId); } validateCounterTypeId(aeron, electionStateCounter, ELECTION_STATE_TYPE_ID); if (null == electionCounter) { electionCounter = ClusterCounters.allocate( aeron, buffer, "Cluster election count", CLUSTER_ELECTION_COUNT_TYPE_ID, clusterId); } validateCounterTypeId(aeron, electionCounter, CLUSTER_ELECTION_COUNT_TYPE_ID); if (null == leadershipTermId) { leadershipTermId = ClusterCounters.allocate( aeron, buffer, "Cluster leadership term id", CLUSTER_LEADERSHIP_TERM_ID_TYPE_ID, clusterId); } validateCounterTypeId(aeron, leadershipTermId, CLUSTER_LEADERSHIP_TERM_ID_TYPE_ID); if (null == clusterNodeRoleCounter) { clusterNodeRoleCounter = ClusterCounters.allocate( aeron, buffer, "Cluster node role", CLUSTER_NODE_ROLE_TYPE_ID, clusterId); } validateCounterTypeId(aeron, clusterNodeRoleCounter, CLUSTER_NODE_ROLE_TYPE_ID); if (null == commitPosition) { commitPosition = ClusterCounters.allocate( aeron, buffer, "Cluster commit-pos:", COMMIT_POSITION_TYPE_ID, clusterId); } validateCounterTypeId(aeron, commitPosition, COMMIT_POSITION_TYPE_ID); if (null == clusterControlToggle) { clusterControlToggle = ClusterCounters.allocate( aeron, buffer, "Cluster control toggle", CONTROL_TOGGLE_TYPE_ID, clusterId); } validateCounterTypeId(aeron, clusterControlToggle, CONTROL_TOGGLE_TYPE_ID); if (null == nodeControlToggle) { nodeControlToggle = ClusterCounters.allocate( aeron, buffer, "Node control toggle", NODE_CONTROL_TOGGLE_TYPE_ID, clusterId); } validateCounterTypeId(aeron, nodeControlToggle, NODE_CONTROL_TOGGLE_TYPE_ID); if (null == snapshotCounter) { snapshotCounter = ClusterCounters.allocate( aeron, buffer, "Cluster snapshot count", SNAPSHOT_COUNTER_TYPE_ID, clusterId); } validateCounterTypeId(aeron, snapshotCounter, SNAPSHOT_COUNTER_TYPE_ID); if (null == timedOutClientCounter) { timedOutClientCounter = ClusterCounters.allocate( aeron, buffer, "Cluster timed out client count", CLUSTER_CLIENT_TIMEOUT_COUNT_TYPE_ID, clusterId); } validateCounterTypeId(aeron, timedOutClientCounter, CLUSTER_CLIENT_TIMEOUT_COUNT_TYPE_ID); // TODO: Disable with configuration... (Mike) if (acceptStandbySnapshots) { if (null == standbySnapshotCounter) { standbySnapshotCounter = ClusterCounters.allocate( aeron, buffer, "Cluster standby snapshots received", CLUSTER_STANDBY_SNAPSHOT_COUNTER_TYPE_ID, clusterId); } validateCounterTypeId(aeron, standbySnapshotCounter, CLUSTER_STANDBY_SNAPSHOT_COUNTER_TYPE_ID); } if (null == dutyCycleTracker) { dutyCycleTracker = new DutyCycleStallTracker( ClusterCounters.allocate( aeron, buffer, "Cluster max cycle time in ns", AeronCounters.CLUSTER_MAX_CYCLE_TIME_TYPE_ID, clusterId), ClusterCounters.allocate( aeron, buffer, "Cluster work cycle time exceeded count: threshold=" + cycleThresholdNs + "ns", AeronCounters.CLUSTER_CYCLE_TIME_THRESHOLD_EXCEEDED_TYPE_ID, clusterId), cycleThresholdNs); } if (null == totalSnapshotDurationTracker) { totalSnapshotDurationTracker = new SnapshotDurationTracker( ClusterCounters.allocate( aeron, buffer, "Total max snapshot duration in ns", AeronCounters.CLUSTER_TOTAL_MAX_SNAPSHOT_DURATION_TYPE_ID, clusterId), ClusterCounters.allocate( aeron, buffer, "Total max snapshot duration exceeded count: threshold=" + totalSnapshotDurationThresholdNs + "ns", AeronCounters.CLUSTER_TOTAL_SNAPSHOT_DURATION_THRESHOLD_EXCEEDED_TYPE_ID, clusterId), totalSnapshotDurationThresholdNs); } if (null == threadFactory) { threadFactory = Thread::new; } if (null == idleStrategySupplier) { idleStrategySupplier = ClusteredServiceContainer.Configuration.idleStrategySupplier(null); } if (null == timerServiceSupplier) { timerServiceSupplier = getTimerServiceSupplierFromSystemProperty(); } if (null == archiveContext) { archiveContext = new AeronArchive.Context() .controlRequestChannel(AeronArchive.Configuration.localControlChannel()) .controlResponseChannel(AeronArchive.Configuration.localControlChannel()) .controlRequestStreamId(AeronArchive.Configuration.localControlStreamId()); } if (!archiveContext.controlRequestChannel().startsWith(CommonContext.IPC_CHANNEL)) { throw new ClusterException("local archive control must be IPC"); } if (!archiveContext.controlResponseChannel().startsWith(CommonContext.IPC_CHANNEL)) { throw new ClusterException("local archive control must be IPC"); } if (null == replicationChannel) { throw new ClusterException("replicationChannel must be set"); } archiveContext .aeron(aeron) .errorHandler(countedErrorHandler) .ownsAeronClient(false) .lock(NoOpLock.INSTANCE); if (null == shutdownSignalBarrier) { shutdownSignalBarrier = new ShutdownSignalBarrier(); } if (null == terminationHook) { terminationHook = () -> shutdownSignalBarrier.signalAll(); } if (null == authenticatorSupplier) { authenticatorSupplier = Configuration.authenticatorSupplier(); } if (null == authorisationServiceSupplier) { authorisationServiceSupplier = Configuration.authorisationServiceSupplier(); } if (null == random) { random = new Random(); } if (null == logPublisher) { logPublisher = new LogPublisher(logChannel()); } if (null == egressPublisher) { egressPublisher = new EgressPublisher(); } final ChannelUri channelUri = ChannelUri.parse(logChannel()); isLogMdc = channelUri.isUdp() && null == channelUri.get(ENDPOINT_PARAM_NAME); if (null == consensusModuleExtension) { consensusModuleExtension = Configuration.newConsensusModuleExtension(); } if (null != consensusModuleExtension && 0 != serviceCount) { throw new ClusterException("Service count must be zero when ConsensusModuleExtension installed"); } concludeMarkFile(); if (io.aeron.driver.Configuration.printConfigurationOnStart()) { System.out.println(this); } } /** * Has the context had the {@link #conclude()} method called. * * @return true of the {@link #conclude()} method has been called. */ public boolean isConcluded() { return 1 == isConcluded; } /** * Should the consensus module attempt to immediately delete {@link #clusterDir()} on startup. * * @param deleteDirOnStart Attempt deletion. * @return this for a fluent API. */ public Context deleteDirOnStart(final boolean deleteDirOnStart) { this.deleteDirOnStart = deleteDirOnStart; return this; } /** * Will the consensus module attempt to immediately delete {@link #clusterDir()} on startup. * * @return true when directory will be deleted, otherwise false. */ public boolean deleteDirOnStart() { return deleteDirOnStart; } /** * Set the directory name to use for the consensus module directory. * * @param clusterDirectoryName to use. * @return this for a fluent API. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CLUSTER_DIR_PROP_NAME */ public Context clusterDirectoryName(final String clusterDirectoryName) { this.clusterDirectoryName = clusterDirectoryName; return this; } /** * The directory name to use for the consensus module directory. * * @return directory name for the consensus module directory. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CLUSTER_DIR_PROP_NAME */ public String clusterDirectoryName() { return clusterDirectoryName; } /** * Set the directory used by clustered service containers. By default, the cluster services will share a * directory with the consensus module. However, sometimes it will be useful for these values to be different. * Setting this value allows that location to be tracked in the ClusterMarkFile to allow for the ClusterTool to * resolve their location when using action like describe or errors. There is an * expectation that all clustered service containers will use the same directory. * * @param clusterServicesDirectoryName to use. * @return this for a fluent API. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CLUSTER_SERVICES_DIR_PROP_NAME */ public Context clusterServicesDirectoryName(final String clusterServicesDirectoryName) { this.clusterServicesDirectoryName = clusterServicesDirectoryName; return this; } /** * The directory used by the clustered service containers. * * @return directory used by the clustered service containers. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CLUSTER_SERVICES_DIR_PROP_NAME * @see #clusterServicesDirectoryName(String) */ @Config(id = "CLUSTER_SERVICES_DIR") public String clusterServicesDirectoryName() { return clusterServicesDirectoryName; } /** * Set the directory to use for the consensus module directory. * * @param clusterDir to use. * @return this for a fluent API. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CLUSTER_DIR_PROP_NAME */ public Context clusterDir(final File clusterDir) { this.clusterDir = clusterDir; return this; } /** * The directory used for the consensus module directory. * * @return directory for the consensus module directory. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CLUSTER_DIR_PROP_NAME */ public File clusterDir() { return clusterDir; } /** * Get the directory in which the ConsensusModule will store mark file (i.e. {@code cluster-mark.dat}). It * defaults to {@link #clusterDir()} if it is not set explicitly via the * {@link io.aeron.cluster.service.ClusteredServiceContainer.Configuration#MARK_FILE_DIR_PROP_NAME}. * * @return the directory in which the ConsensusModule will store mark file (i.e. {@code cluster-mark.dat}). * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#MARK_FILE_DIR_PROP_NAME * @see #clusterDir() */ public File markFileDir() { return markFileDir; } /** * Set the directory in which the ConsensusModule will store mark file (i.e. {@code cluster-mark.dat}). * * @param markFileDir the directory in which the Archive will store mark file (i.e. {@code cluster-mark.dat}). * @return this for a fluent API. */ public ConsensusModule.Context markFileDir(final File markFileDir) { this.markFileDir = markFileDir; return this; } /** * Set the {@link RecordingLog} for the log terms and snapshots. * * @param recordingLog to use. * @return this for a fluent API. */ public Context recordingLog(final RecordingLog recordingLog) { this.recordingLog = recordingLog; return this; } /** * The {@link RecordingLog} for the log terms and snapshots. * * @return {@link RecordingLog} for the log terms and snapshots. */ public RecordingLog recordingLog() { return recordingLog; } /** * User assigned application version which appended to the log as the appVersion in new leadership events. *

* This can be validated using {@link org.agrona.SemanticVersion} to ensure only application nodes of the same * major version communicate with each other. * * @param appVersion for user application. * @return this for a fluent API. */ public Context appVersion(final int appVersion) { this.appVersion = appVersion; return this; } /** * User assigned application version which appended to the log as the appVersion in new leadership events. *

* This can be validated using {@link org.agrona.SemanticVersion} to ensure only application nodes of the same * major version communicate with each other. * * @return appVersion for user application. */ public int appVersion() { return appVersion; } /** * User assigned application version validator implementation used to check version compatibility. *

* The default validator uses {@link org.agrona.SemanticVersion} semantics. * * @param appVersionValidator for user application. * @return this for fluent API. */ public Context appVersionValidator(final AppVersionValidator appVersionValidator) { this.appVersionValidator = appVersionValidator; return this; } /** * User assigned application version validator implementation used to check version compatibility. *

* The default is to use {@link org.agrona.SemanticVersion} major version for checking compatibility. * * @return AppVersionValidator in use. */ public AppVersionValidator appVersionValidator() { return appVersionValidator; } /** * Get level at which files should be sync'ed to disk. *

    *
  • 0 - normal writes.
  • *
  • 1 - sync file data.
  • *
  • 2 - sync file data + metadata.
  • *
* * @return the level to be applied for file write. * @see Configuration#FILE_SYNC_LEVEL_PROP_NAME */ @Config int fileSyncLevel() { return fileSyncLevel; } /** * Set level at which files should be sync'ed to disk. *
    *
  • 0 - normal writes.
  • *
  • 1 - sync file data.
  • *
  • 2 - sync file data + metadata.
  • *
* * @param syncLevel to be applied for file writes. * @return this for a fluent API. * @see Configuration#FILE_SYNC_LEVEL_PROP_NAME */ public Context fileSyncLevel(final int syncLevel) { this.fileSyncLevel = syncLevel; return this; } /** * Set the id for this cluster instance. This must match with the service containers. * * @param clusterId for this clustered instance. * @return this for a fluent API * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CLUSTER_ID_PROP_NAME */ public Context clusterId(final int clusterId) { this.clusterId = clusterId; return this; } /** * Get the id for this cluster instance. This must match with the service containers. * * @return the id for this cluster instance. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CLUSTER_ID_PROP_NAME */ public int clusterId() { return clusterId; } /** * This cluster member identity. * * @param clusterMemberId for this member. * @return this for a fluent API. * @see Configuration#CLUSTER_MEMBER_ID_PROP_NAME */ public Context clusterMemberId(final int clusterMemberId) { this.clusterMemberId = clusterMemberId; return this; } /** * This cluster member identity. * * @return this cluster member identity. * @see Configuration#CLUSTER_MEMBER_ID_PROP_NAME */ @Config public int clusterMemberId() { return clusterMemberId; } /** * The cluster member id of the appointed cluster leader. *

* -1 means no leader has been appointed and an automated leader election should occur. *

* This feature is for testing and not recommended for production usage. * * @param appointedLeaderId for the cluster. * @return this for a fluent API. * @see Configuration#APPOINTED_LEADER_ID_PROP_NAME */ public Context appointedLeaderId(final int appointedLeaderId) { this.appointedLeaderId = appointedLeaderId; return this; } /** * The cluster member id of the appointed cluster leader. *

* -1 means no leader has been appointed and an automated leader election should occur. * * @return cluster member id of the appointed cluster leader. * @see Configuration#APPOINTED_LEADER_ID_PROP_NAME */ @Config public int appointedLeaderId() { return appointedLeaderId; } /** * String representing the cluster members. *

* * 0,ingress:port,consensus:port,log:port,catchup:port,archive:port| \ * 1,ingress:port,consensus:port,log:port,catchup:port,archive:port| ... * *

* The ingress endpoints will be used as the endpoint substituted into the {@link #ingressChannel()} * if the endpoint is not provided unless it is multicast. * * @param clusterMembers which are all candidates to be leader. * @return this for a fluent API. * @see Configuration#CLUSTER_MEMBERS_PROP_NAME */ public Context clusterMembers(final String clusterMembers) { this.clusterMembers = clusterMembers; return this; } /** * The endpoints representing members of the cluster which are all candidates to be leader. *

* The ingress endpoints will be used as the endpoint in {@link #ingressChannel()} if the endpoint is * not provided in that when it is not multicast. * * @return members of the cluster which are all candidates to be leader. * @see Configuration#CLUSTER_MEMBERS_PROP_NAME */ @Config public String clusterMembers() { return clusterMembers; } /** * String representing the cluster members consensus endpoints used to request to join the cluster. *

* {@code "endpoint,endpoint,endpoint"} * * @param endpoints which are to be contacted for joining the cluster. * @return this for a fluent API. * @see Configuration#CLUSTER_CONSENSUS_ENDPOINTS_PROP_NAME * @deprecated the dynamic join feature that uses this configuration in order to join the cluster is now * deprecated and will be removed in a later release. */ @Deprecated public Context clusterConsensusEndpoints(final String endpoints) { this.clusterConsensusEndpoints = endpoints; return this; } /** * The endpoints representing cluster members of the cluster to attempt to contact to join the cluster. * * @return members of the cluster to attempt to request to join from. * @see Configuration#CLUSTER_CONSENSUS_ENDPOINTS_PROP_NAME * @deprecated the dynamic join feature that uses this configuration in order to join the cluster is now * deprecated and will be removed in a later release. */ @Deprecated @Config public String clusterConsensusEndpoints() { return clusterConsensusEndpoints; } /** * Whether the cluster members in the snapshot should be ignored or not. * * @param ignore or not the cluster members in the snapshot. * @return this for a fluent API. * @see Configuration#CLUSTER_MEMBERS_IGNORE_SNAPSHOT_PROP_NAME */ public Context clusterMembersIgnoreSnapshot(final boolean ignore) { this.clusterMembersIgnoreSnapshot = ignore; return this; } /** * Whether the cluster members in the snapshot should be ignored or not. * * @return ignore or not the cluster members in the snapshot. * @see Configuration#CLUSTER_MEMBERS_IGNORE_SNAPSHOT_PROP_NAME */ @Config public boolean clusterMembersIgnoreSnapshot() { return clusterMembersIgnoreSnapshot; } /** * Set the channel parameter for the ingress channel. * * @param channel parameter for the ingress channel. * @return this for a fluent API. * @see io.aeron.cluster.client.AeronCluster.Configuration#INGRESS_CHANNEL_PROP_NAME */ public Context ingressChannel(final String channel) { ingressChannel = channel; return this; } /** * Get the channel parameter for the ingress channel. * * @return the channel parameter for the ingress channel. * @see io.aeron.cluster.client.AeronCluster.Configuration#INGRESS_CHANNEL_PROP_NAME */ public String ingressChannel() { return ingressChannel; } /** * Set the stream id for the ingress channel. * * @param streamId for the ingress channel. * @return this for a fluent API * @see io.aeron.cluster.client.AeronCluster.Configuration#INGRESS_STREAM_ID_PROP_NAME */ public Context ingressStreamId(final int streamId) { ingressStreamId = streamId; return this; } /** * Get the stream id for the ingress channel. * * @return the stream id for the ingress channel. * @see io.aeron.cluster.client.AeronCluster.Configuration#INGRESS_STREAM_ID_PROP_NAME */ public int ingressStreamId() { return ingressStreamId; } /** * Set limit for fragments to be consumed on each poll of ingress. * * @param ingressFragmentLimit for the ingress channel. * @return this for a fluent API * @see Configuration#CLUSTER_INGRESS_FRAGMENT_LIMIT_PROP_NAME */ public Context ingressFragmentLimit(final int ingressFragmentLimit) { this.ingressFragmentLimit = ingressFragmentLimit; return this; } /** * The limit for fragments to be consumed on each poll of ingress. * * @return the limit for fragments to be consumed on each poll of ingress. * @see Configuration#CLUSTER_INGRESS_FRAGMENT_LIMIT_PROP_NAME */ @Config(id = "CLUSTER_INGRESS_FRAGMENT_LIMIT") public int ingressFragmentLimit() { return ingressFragmentLimit; } /** * Set the template channel that is used to refine the response channels for a Cluster, i.e. egress channel, * backup query response channel, heartbeat response channel etc. Defaults to {@code null} in which case the * provided response channel will be used. The main use-case is the ability to define the `interface` parameter * to steer the response traffic via a specific network interface. * *

Note: each URI parameter that is defined on this channel template will * override the corresponding URI parameter in the provided response channel! * * @param channel the channel template for response channels. * @return this for a fluent API. * @see io.aeron.cluster.client.AeronCluster.Configuration#EGRESS_CHANNEL_PROP_NAME */ public Context egressChannel(final String channel) { egressChannel = channel; return this; } /** * Get the template channel that is used to refine the response channels for a Cluster, i.e. egress channel, * backup query response channel, heartbeat response channel etc. Defaults to {@code null} in which case the * provided response channel will be used. The main use-case is the ability to define the `interface` parameter * to steer the response traffic via a specific network interface. * *

Note: each URI parameter that is defined on this channel template will * override the corresponding URI parameter in the provided response channel! * * @return the channel template for response channels. * @see io.aeron.cluster.client.AeronCluster.Configuration#EGRESS_CHANNEL_PROP_NAME */ public String egressChannel() { return egressChannel; } /** * Set whether IPC ingress is allowed or not. * * @param isIpcIngressAllowed or not. * @return this for a fluent API * @see Configuration#CLUSTER_INGRESS_IPC_ALLOWED_PROP_NAME */ public Context isIpcIngressAllowed(final boolean isIpcIngressAllowed) { this.isIpcIngressAllowed = isIpcIngressAllowed; return this; } /** * Get whether IPC ingress is allowed or not. * * @return whether IPC ingress is allowed or not. * @see Configuration#CLUSTER_INGRESS_IPC_ALLOWED_PROP_NAME */ @Config(id = "CLUSTER_INGRESS_IPC_ALLOWED") public boolean isIpcIngressAllowed() { return isIpcIngressAllowed; } /** * Set the channel parameter for the cluster log channel. * * @param channel parameter for the cluster log channel. * @return this for a fluent API. * @see Configuration#LOG_CHANNEL_PROP_NAME */ public Context logChannel(final String channel) { logChannel = channel; return this; } /** * Get the channel parameter for the cluster log channel. * * @return the channel parameter for the cluster channel. * @see Configuration#LOG_CHANNEL_PROP_NAME */ @Config public String logChannel() { return logChannel; } /** * Set the stream id for the cluster log channel. * * @param streamId for the cluster log channel. * @return this for a fluent API * @see Configuration#LOG_STREAM_ID_PROP_NAME */ public Context logStreamId(final int streamId) { logStreamId = streamId; return this; } /** * Get the stream id for the cluster log channel. * * @return the stream id for the cluster log channel. * @see Configuration#LOG_STREAM_ID_PROP_NAME */ public int logStreamId() { return logStreamId; } /** * Set the endpoints for this cluster node. * * @param endpoints for the cluster node. * @return this for a fluent API. * @see Configuration#MEMBER_ENDPOINTS_PROP_NAME */ public Context memberEndpoints(final String endpoints) { memberEndpoints = endpoints; return this; } /** * Get the endpoints for this cluster node. * * @return the endpoints for the cluster node. * @see Configuration#MEMBER_ENDPOINTS_PROP_NAME */ @Config public String memberEndpoints() { return memberEndpoints; } /** * Set the channel parameter for the cluster log and snapshot replay channel. * * @param channel parameter for the cluster log replay channel. * @return this for a fluent API. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#REPLAY_CHANNEL_PROP_NAME */ public Context replayChannel(final String channel) { replayChannel = channel; return this; } /** * Get the channel parameter for the cluster log and snapshot replay channel. * * @return the channel parameter for the cluster replay channel. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#REPLAY_CHANNEL_PROP_NAME */ public String replayChannel() { return replayChannel; } /** * Set the stream id for the cluster log and snapshot replay channel. * * @param streamId for the cluster log replay channel. * @return this for a fluent API * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#REPLAY_STREAM_ID_PROP_NAME */ public Context replayStreamId(final int streamId) { replayStreamId = streamId; return this; } /** * Get the stream id for the cluster log and snapshot replay channel. * * @return the stream id for the cluster log replay channel. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#REPLAY_STREAM_ID_PROP_NAME */ public int replayStreamId() { return replayStreamId; } /** * Set the channel parameter for bidirectional communications between the consensus module and services. * * @param channel parameter for bidirectional communications between the consensus module and services. * @return this for a fluent API. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CONTROL_CHANNEL_PROP_NAME */ public Context controlChannel(final String channel) { controlChannel = channel; return this; } /** * Get the channel parameter for bidirectional communications between the consensus module and services. * * @return the channel parameter for bidirectional communications between the consensus module and services. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CONTROL_CHANNEL_PROP_NAME */ public String controlChannel() { return controlChannel; } /** * Set the stream id for communications from the consensus module and to the services. * * @param streamId for communications from the consensus module and to the services. * @return this for a fluent API * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SERVICE_STREAM_ID_PROP_NAME */ public Context serviceStreamId(final int streamId) { serviceStreamId = streamId; return this; } /** * Get the stream id for communications from the consensus module and to the services. * * @return the stream id for communications from the consensus module and to the services. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SERVICE_STREAM_ID_PROP_NAME */ public int serviceStreamId() { return serviceStreamId; } /** * Set the stream id for communications from the services to the consensus module. * * @param streamId for communications from the services to the consensus module. * @return this for a fluent API * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CONSENSUS_MODULE_STREAM_ID_PROP_NAME */ public Context consensusModuleStreamId(final int streamId) { consensusModuleStreamId = streamId; return this; } /** * Get the stream id for communications from the services to the consensus module. * * @return the stream id for communications from the services to the consensus module. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#CONSENSUS_MODULE_STREAM_ID_PROP_NAME */ public int consensusModuleStreamId() { return consensusModuleStreamId; } /** * Set the channel parameter for snapshot recordings. * * @param channel parameter for snapshot recordings * @return this for a fluent API. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SNAPSHOT_CHANNEL_PROP_NAME */ public Context snapshotChannel(final String channel) { snapshotChannel = channel; return this; } /** * Get the channel parameter for snapshot recordings. * * @return the channel parameter for snapshot recordings. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SNAPSHOT_CHANNEL_PROP_NAME */ public String snapshotChannel() { return snapshotChannel; } /** * Set the stream id for snapshot recordings. * * @param streamId for snapshot recordings. * @return this for a fluent API * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SNAPSHOT_STREAM_ID_PROP_NAME */ public Context snapshotStreamId(final int streamId) { snapshotStreamId = streamId; return this; } /** * Get the stream id for snapshot recordings. * * @return the stream id for snapshot recordings. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SNAPSHOT_STREAM_ID_PROP_NAME */ public int snapshotStreamId() { return snapshotStreamId; } /** * Set the channel parameter for the consensus communication channel. * * @param channel parameter for the consensus communication channel. * @return this for a fluent API. * @see Configuration#CONSENSUS_CHANNEL_PROP_NAME */ public Context consensusChannel(final String channel) { consensusChannel = channel; return this; } /** * Get the channel parameter for the consensus communication channel. * * @return the channel parameter for the consensus communication channel. * @see Configuration#CONSENSUS_CHANNEL_PROP_NAME */ @Config public String consensusChannel() { return consensusChannel; } /** * Set the stream id for the consensus channel. * * @param streamId for the consensus channel. * @return this for a fluent API * @see Configuration#CONSENSUS_STREAM_ID_PROP_NAME */ public Context consensusStreamId(final int streamId) { consensusStreamId = streamId; return this; } /** * Get the stream id for the consensus channel. * * @return the stream id for the consensus channel. * @see Configuration#CONSENSUS_STREAM_ID_PROP_NAME */ @Config public int consensusStreamId() { return consensusStreamId; } /** * Set the channel parameter for the replication communication channel. This is channel that the local * will send to src archives to replay their recordings back to. It should contain an endpoint that other * members in the cluster will be able to reach. Using port 0 for the endpoint is valid for this channel to * simplify port allocation. * * @param channel replication communication channel to be used by the consensus module. * @return this for a fluent API * @see Configuration#REPLICATION_CHANNEL_PROP_NAME */ public Context replicationChannel(final String channel) { replicationChannel = channel; return this; } /** * Get the replication channel for logs and snapshots. * * @return channel to receive replication responses from other node's archives when using log and snapshot * replication to catch up. * @see Configuration#REPLICATION_CHANNEL_PROP_NAME */ @Config public String replicationChannel() { return replicationChannel; } /** * Set a channel template used for replaying logs to a follower using the * {@link ClusterMember#catchupEndpoint()}. * * @param channel to do a catch replay to a follower. * @return this for a fluent API * @see Configuration#FOLLOWER_CATCHUP_CHANNEL_PROP_NAME */ public Context followerCatchupChannel(final String channel) { this.followerCatchupChannel = channel; return this; } /** * Gets the channel template used for replaying logs to a follower using the * {@link ClusterMember#catchupEndpoint()}. * * @return channel used for replaying older data during a catchup phase. * @see Configuration#FOLLOWER_CATCHUP_CHANNEL_PROP_NAME */ @Config public String followerCatchupChannel() { return followerCatchupChannel; } /** * Set a channel template used to build the control request channel for the leader Archive using the * {@link ClusterMember#archiveEndpoint()}. * * @param channel for the Archive control requests. * @return this for a fluent API * @see Configuration#LEADER_ARCHIVE_CONTROL_CHANNEL_PROP_NAME */ public Context leaderArchiveControlChannel(final String channel) { this.leaderArchiveControlChannel = channel; return this; } /** * Gets the channel template used to build the control request channel for the leader Archive using the * {@link ClusterMember#archiveEndpoint()}. * * @return channel used for replaying older data during a catchup phase. * @see Configuration#LEADER_ARCHIVE_CONTROL_CHANNEL_PROP_NAME */ @Config public String leaderArchiveControlChannel() { return leaderArchiveControlChannel; } /** * Set the fragment limit to be used when polling the log {@link Subscription}. * * @param logFragmentLimit for this clustered service. * @return this for a fluent API * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#LOG_FRAGMENT_LIMIT_DEFAULT */ public Context logFragmentLimit(final int logFragmentLimit) { this.logFragmentLimit = logFragmentLimit; return this; } /** * Get the fragment limit to be used when polling the log {@link Subscription}. * * @return the fragment limit to be used when polling the log {@link Subscription}. * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#LOG_FRAGMENT_LIMIT_PROP_NAME */ public int logFragmentLimit() { return logFragmentLimit; } /** * Resolution in nanoseconds for each tick of the timer wheel for scheduling deadlines. * * @param wheelTickResolutionNs the resolution in nanoseconds of each tick on the timer wheel. * @return this for a fluent API * @see Configuration#WHEEL_TICK_RESOLUTION_PROP_NAME */ public Context wheelTickResolutionNs(final long wheelTickResolutionNs) { this.wheelTickResolutionNs = wheelTickResolutionNs; return this; } /** * Resolution in nanoseconds for each tick of the timer wheel for scheduling deadlines. * * @return the resolution in nanoseconds for each tick on the timer wheel. * @see Configuration#WHEEL_TICK_RESOLUTION_PROP_NAME */ @Config public long wheelTickResolutionNs() { return wheelTickResolutionNs; } /** * Number of ticks, or spokes, on the timer wheel. Higher number of ticks reduces potential conflicts * traded off against memory usage. * * @param ticksPerWheel the number of ticks on the timer wheel. * @return this for a fluent API * @see Configuration#TICKS_PER_WHEEL_PROP_NAME */ public Context ticksPerWheel(final int ticksPerWheel) { this.ticksPerWheel = ticksPerWheel; return this; } /** * Number of ticks, or spokes, on the timer wheel. Higher number of ticks reduces potential conflicts * traded off against memory usage. * * @return the number of ticks on the timer wheel. * @see Configuration#TICKS_PER_WHEEL_PROP_NAME */ @Config public int ticksPerWheel() { return ticksPerWheel; } /** * Set the number of clustered services in this cluster instance. * * @param serviceCount the number of clustered services in this cluster instance. * @return this for a fluent API * @see Configuration#SERVICE_COUNT_PROP_NAME * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SERVICE_ID_PROP_NAME */ public Context serviceCount(final int serviceCount) { this.serviceCount = serviceCount; return this; } /** * Get the number of clustered services in this cluster instance. * * @return the number of clustered services in this cluster instance. * @see Configuration#SERVICE_COUNT_PROP_NAME * @see io.aeron.cluster.service.ClusteredServiceContainer.Configuration#SERVICE_ID_PROP_NAME */ @Config public int serviceCount() { return serviceCount; } /** * Set the limit for the maximum number of concurrent cluster sessions. * * @param maxSessions after which new sessions will be rejected. * @return this for a fluent API * @see Configuration#MAX_CONCURRENT_SESSIONS_PROP_NAME */ public Context maxConcurrentSessions(final int maxSessions) { this.maxConcurrentSessions = maxSessions; return this; } /** * Get the limit for the maximum number of concurrent cluster sessions. * * @return the limit for the maximum number of concurrent cluster sessions. * @see Configuration#MAX_CONCURRENT_SESSIONS_PROP_NAME */ @Config public int maxConcurrentSessions() { return maxConcurrentSessions; } /** * Timeout for a session if no activity is observed. * * @param sessionTimeoutNs to wait for activity on a session. * @return this for a fluent API. * @see Configuration#SESSION_TIMEOUT_PROP_NAME */ public Context sessionTimeoutNs(final long sessionTimeoutNs) { this.sessionTimeoutNs = sessionTimeoutNs; return this; } /** * Timeout for a session if no activity is observed. * * @return the timeout for a session if no activity is observed. * @see Configuration#SESSION_TIMEOUT_PROP_NAME */ @Config public long sessionTimeoutNs() { return CommonContext.checkDebugTimeout(sessionTimeoutNs, TimeUnit.NANOSECONDS); } /** * Timeout for a leader if no heartbeat is received by another member. * * @param heartbeatTimeoutNs to wait for heartbeat from a leader. * @return this for a fluent API. * @see Configuration#LEADER_HEARTBEAT_TIMEOUT_PROP_NAME */ public Context leaderHeartbeatTimeoutNs(final long heartbeatTimeoutNs) { this.leaderHeartbeatTimeoutNs = heartbeatTimeoutNs; return this; } /** * Timeout for a leader if no heartbeat is received by another member. * * @return the timeout for a leader if no heartbeat is received by another member. * @see Configuration#LEADER_HEARTBEAT_TIMEOUT_PROP_NAME */ @Config public long leaderHeartbeatTimeoutNs() { return leaderHeartbeatTimeoutNs; } /** * Interval at which a leader will send heartbeats if the log is not progressing. * * @param heartbeatIntervalNs between leader heartbeats. * @return this for a fluent API. * @see Configuration#LEADER_HEARTBEAT_INTERVAL_PROP_NAME */ @Config public Context leaderHeartbeatIntervalNs(final long heartbeatIntervalNs) { this.leaderHeartbeatIntervalNs = heartbeatIntervalNs; return this; } /** * Interval at which a leader will send heartbeats if the log is not progressing. * * @return the interval at which a leader will send heartbeats if the log is not progressing. * @see Configuration#LEADER_HEARTBEAT_INTERVAL_PROP_NAME */ public long leaderHeartbeatIntervalNs() { return leaderHeartbeatIntervalNs; } /** * Timeout to wait for hearing the status of all cluster members on startup after recovery before commencing * an election if a majority of members has been heard from. * * @param timeoutNs to wait on startup after recovery before commencing an election. * @return this for a fluent API. * @see Configuration#STARTUP_CANVASS_TIMEOUT_PROP_NAME */ public Context startupCanvassTimeoutNs(final long timeoutNs) { this.startupCanvassTimeoutNs = timeoutNs; return this; } /** * Timeout to wait for hearing the status of all cluster members on startup after recovery before commencing * an election if a majority of members has been heard from. * * @return the timeout to wait on startup after recovery before commencing an election. * @see Configuration#STARTUP_CANVASS_TIMEOUT_PROP_NAME */ @Config public long startupCanvassTimeoutNs() { return startupCanvassTimeoutNs; } /** * Timeout to wait for votes in an election before declaring the election void and starting over. * * @param timeoutNs to wait for votes in an elections. * @return this for a fluent API. * @see Configuration#ELECTION_TIMEOUT_PROP_NAME */ public Context electionTimeoutNs(final long timeoutNs) { this.electionTimeoutNs = timeoutNs; return this; } /** * Timeout to wait for votes in an election before declaring the election void and starting over. * * @return the timeout to wait for votes in an elections. * @see Configuration#ELECTION_TIMEOUT_PROP_NAME */ @Config public long electionTimeoutNs() { return electionTimeoutNs; } /** * Interval at which a member will send out status messages during the election phases. * * @param electionStatusIntervalNs between status message updates. * @return this for a fluent API. * @see Configuration#ELECTION_STATUS_INTERVAL_PROP_NAME * @see Configuration#ELECTION_STATUS_INTERVAL_DEFAULT_NS */ public Context electionStatusIntervalNs(final long electionStatusIntervalNs) { this.electionStatusIntervalNs = electionStatusIntervalNs; return this; } /** * Interval at which a member will send out status messages during the election phases. * * @return the interval at which a member will send out status messages during the election phases. * @see Configuration#ELECTION_STATUS_INTERVAL_PROP_NAME * @see Configuration#ELECTION_STATUS_INTERVAL_DEFAULT_NS */ @Config public long electionStatusIntervalNs() { return electionStatusIntervalNs; } /** * Timeout to wait for follower termination by leader. * * @param terminationTimeoutNs to wait for follower termination. * @return this for a fluent API. * @see Configuration#TERMINATION_TIMEOUT_PROP_NAME * @see Configuration#TERMINATION_TIMEOUT_DEFAULT_NS */ public Context terminationTimeoutNs(final long terminationTimeoutNs) { this.terminationTimeoutNs = terminationTimeoutNs; return this; } /** * Timeout to wait for follower termination by leader. * * @return timeout to wait for follower termination by leader. * @see Configuration#TERMINATION_TIMEOUT_PROP_NAME * @see Configuration#TERMINATION_TIMEOUT_DEFAULT_NS */ @Config public long terminationTimeoutNs() { return terminationTimeoutNs; } /** * Set a threshold for the consensus module agent work cycle time which when exceed it will increment the * counter. * * @param thresholdNs value in nanoseconds * @return this for fluent API. * @see Configuration#CYCLE_THRESHOLD_PROP_NAME * @see Configuration#CYCLE_THRESHOLD_DEFAULT_NS */ public Context cycleThresholdNs(final long thresholdNs) { this.cycleThresholdNs = thresholdNs; return this; } /** * Threshold for the consensus module agent work cycle time which when exceed it will increment the * counter. * * @return threshold to track for the consensus module agent work cycle time. */ @Config public long cycleThresholdNs() { return cycleThresholdNs; } /** * Set a duty cycle tracker to be used for tracking the duty cycle time of the consensus module agent. * * @param dutyCycleTracker to use for tracking. * @return this for fluent API. */ public Context dutyCycleTracker(final DutyCycleTracker dutyCycleTracker) { this.dutyCycleTracker = dutyCycleTracker; return this; } /** * The duty cycle tracker used to track the consensus module agent duty cycle. * * @return the duty cycle tracker. */ public DutyCycleTracker dutyCycleTracker() { return dutyCycleTracker; } /** * Set a threshold for total snapshot duration which when exceeded will result in a counter increment. * * @param thresholdNs value in nanoseconds * @return this for fluent API. * @see ConsensusModule.Configuration#TOTAL_SNAPSHOT_DURATION_THRESHOLD_PROP_NAME * @see ConsensusModule.Configuration#TOTAL_SNAPSHOT_DURATION_THRESHOLD_DEFAULT_NS */ public Context totalSnapshotDurationThresholdNs(final long thresholdNs) { this.totalSnapshotDurationThresholdNs = thresholdNs; return this; } /** * Threshold for total snapshot duration which when exceeded it will increment the counter. * * @return threshold value in nanoseconds. */ @Config public long totalSnapshotDurationThresholdNs() { return totalSnapshotDurationThresholdNs; } /** * Set snapshot duration tracker used for monitoring total snapshot duration. * * @param snapshotDurationTracker snapshot duration tracker. * @return this for fluent API. */ public Context totalSnapshotDurationTracker(final SnapshotDurationTracker snapshotDurationTracker) { this.totalSnapshotDurationTracker = snapshotDurationTracker; return this; } /** * Get snapshot duration tracker used for monitoring total snapshot duration. * * @return snapshot duration tracker */ public SnapshotDurationTracker totalSnapshotDurationTracker() { return totalSnapshotDurationTracker; } /** * Get the {@link Agent#roleName()} to be used for the consensus module agent. If {@code null} then one will * be generated. * * @return the {@link Agent#roleName()} to be used for the consensus module agent. */ @Config(id = "CLUSTER_CONSENSUS_MODULE_AGENT_ROLE_NAME") public String agentRoleName() { return agentRoleName; } /** * Set the {@link Agent#roleName()} to be used for the consensus module agent. * * @param agentRoleName to be used for the consensus module agent. * @return this for a fluent API. */ public Context agentRoleName(final String agentRoleName) { this.agentRoleName = agentRoleName; return this; } /** * Get the thread factory used for creating threads. * * @return thread factory used for creating threads. */ public ThreadFactory threadFactory() { return threadFactory; } /** * Set the thread factory used for creating threads. * * @param threadFactory used for creating threads * @return this for a fluent API. */ public Context threadFactory(final ThreadFactory threadFactory) { this.threadFactory = threadFactory; return this; } /** * Provides an {@link IdleStrategy} supplier for the idle strategy for the agent duty cycle. * * @param idleStrategySupplier supplier for the idle strategy for the agent duty cycle. * @return this for a fluent API. */ public Context idleStrategySupplier(final Supplier idleStrategySupplier) { this.idleStrategySupplier = idleStrategySupplier; return this; } /** * Get a new {@link IdleStrategy} based on configured supplier. * * @return a new {@link IdleStrategy} based on configured supplier. */ public IdleStrategy idleStrategy() { return idleStrategySupplier.get(); } /** * Set the {@link ClusterClock} to be used for timestamping messages and timers. * * @param clock {@link ClusterClock} to be used for timestamping messages and timers. * @return this for a fluent API. */ public Context clusterClock(final ClusterClock clock) { this.clusterClock = clock; return this; } /** * Get the {@link ClusterClock} to used for timestamping messages and timers. * * @return the {@link ClusterClock} to used for timestamping messages and timers. */ @Config public ClusterClock clusterClock() { return clusterClock; } /** * Set the {@link EpochClock} to be used for tracking wall clock time. * * @param clock {@link EpochClock} to be used for tracking wall clock time. * @return this for a fluent API. */ public Context epochClock(final EpochClock clock) { this.epochClock = clock; return this; } /** * Get the {@link EpochClock} to used for tracking wall clock time. * * @return the {@link EpochClock} to used for tracking wall clock time. */ public EpochClock epochClock() { return epochClock; } /** * Set the supplier of a consumer of timestamps which can be used for testing time progress in a cluster. The * timestamp passed to the consumer is the timestamp of the last completed {@link Agent#doWork()} cycle by the * consensus module {@link Agent}. The supplier will be called after the context is concluded and can be * referenced during the construction of the consumer. * * @param clusterTimeConsumerSupplier to which the latest timestamp will be passed. * @return this for a fluent API */ public Context clusterTimeConsumerSupplier(final Function clusterTimeConsumerSupplier) { this.clusterTimeConsumerSupplier = clusterTimeConsumerSupplier; return this; } /** * Get the supplier of a consumer of timestamps which can be used for testing time progress in a cluster. The * timestamp passed to the consumer is the timestamp of the last completed {@link Agent#doWork()} cycle by the * consensus module {@link Agent}. * * @return the consumer of timestamps for completed work cycles. */ public Function clusterTimeConsumerSupplier() { return clusterTimeConsumerSupplier; } /** * Get the {@link ErrorHandler} to be used by the Consensus Module. * * @return the {@link ErrorHandler} to be used by the Consensus Module. */ public ErrorHandler errorHandler() { return errorHandler; } /** * Set the {@link ErrorHandler} to be used by the Consensus Module. * * @param errorHandler the error handler to be used by the Consensus Module. * @return this for a fluent API */ public Context errorHandler(final ErrorHandler errorHandler) { this.errorHandler = errorHandler; return this; } /** * The {@link #errorHandler()} that will increment {@link #errorCounter()} by default. * * @param countedErrorHandler to override the default. * @return this for a fluent API. */ public Context countedErrorHandler(final CountedErrorHandler countedErrorHandler) { this.countedErrorHandler = countedErrorHandler; return this; } /** * The {@link #errorHandler()} that will increment {@link #errorCounter()} by default. * * @return {@link #errorHandler()} that will increment {@link #errorCounter()} by default. */ public CountedErrorHandler countedErrorHandler() { return countedErrorHandler; } /** * Get the error counter that will record the number of errors observed. * * @return the error counter that will record the number of errors observed. */ public AtomicCounter errorCounter() { return errorCounter; } /** * Set the error counter that will record the number of errors observed. * * @param errorCounter the error counter that will record the number of errors observed. * @return this for a fluent API. */ public Context errorCounter(final AtomicCounter errorCounter) { this.errorCounter = errorCounter; return this; } /** * Get the counter for the current state of the consensus module. * * @return the counter for the current state of the consensus module. * @see ConsensusModule.State */ public Counter moduleStateCounter() { return moduleStateCounter; } /** * Set the counter for the current state of the consensus module. * * @param moduleState the counter for the current state of the consensus module. * @return this for a fluent API. * @see ConsensusModule.State */ public Context moduleStateCounter(final Counter moduleState) { this.moduleStateCounter = moduleState; return this; } /** * Get the counter for the current state of an election. * * @return the counter for the current state of an election. * @see ElectionState */ public Counter electionStateCounter() { return electionStateCounter; } /** * Set the counter for the current state of an election. * * @param electionStateCounter for the current state of an election. * @return this for a fluent API. * @see ElectionState */ public Context electionStateCounter(final Counter electionStateCounter) { this.electionStateCounter = electionStateCounter; return this; } /** * Get the counter for the commit position the cluster has reached for consensus. * * @return the counter for the commit position the cluster has reached for consensus. */ public Counter commitPositionCounter() { return commitPosition; } /** * Set the counter for the commit position the cluster has reached for consensus. * * @param commitPosition counter for the commit position the cluster has reached for consensus. * @return this for a fluent API. */ public Context commitPositionCounter(final Counter commitPosition) { this.commitPosition = commitPosition; return this; } /** * Get the counter for representing the current {@link io.aeron.cluster.service.Cluster.Role} of the * consensus module node. * * @return the counter for representing the current {@link io.aeron.cluster.service.Cluster.Role} of the * cluster node. * @see io.aeron.cluster.service.Cluster.Role */ public Counter clusterNodeRoleCounter() { return clusterNodeRoleCounter; } /** * Set the counter for representing the current {@link io.aeron.cluster.service.Cluster.Role} of the * cluster node. * * @param nodeRole the counter for representing the current {@link io.aeron.cluster.service.Cluster.Role} * of the cluster node. * @return this for a fluent API. * @see io.aeron.cluster.service.Cluster.Role */ public Context clusterNodeRoleCounter(final Counter nodeRole) { this.clusterNodeRoleCounter = nodeRole; return this; } /** * Get the counter for the control toggle for triggering actions for the cluster. * * @return the counter for triggering cluster node actions. * @see ClusterControl */ public Counter controlToggleCounter() { return clusterControlToggle; } /** * Set the counter for the control toggle for triggering actions for the cluster. * * @param controlToggle the counter for triggering cluster node actions. * @return this for a fluent API. * @see ClusterControl */ public Context controlToggleCounter(final Counter controlToggle) { this.clusterControlToggle = controlToggle; return this; } /** * Get the counter for the control toggle for triggering actions on the cluster node. * * @return the counter for triggering cluster node actions. * @see ClusterControl */ public Counter nodeControlToggleCounter() { return nodeControlToggle; } /** * Set the counter for the control toggle for triggering actions on the cluster node. * * @param nodeControlToggle the counter for triggering cluster node actions. * @return this for a fluent API. * @see ClusterControl */ public Context nodeControlToggleCounter(final Counter nodeControlToggle) { this.nodeControlToggle = nodeControlToggle; return this; } /** * Get the counter for the count of snapshots taken. * * @return the counter for the count of snapshots taken. */ public Counter snapshotCounter() { return snapshotCounter; } /** * Set the counter for the count of snapshots taken. * * @param snapshotCounter the count of snapshots taken. * @return this for a fluent API. */ public Context snapshotCounter(final Counter snapshotCounter) { this.snapshotCounter = snapshotCounter; return this; } /** * Get the counter for the count of clients that have been timed out and disconnected. * * @return the counter for the count of clients that have been timed out and disconnected. */ public Counter timedOutClientCounter() { return timedOutClientCounter; } /** * Set the counter for the count of clients that have been timed out and disconnected. * * @param timedOutClientCounter the count of clients that have been timed out and disconnected. * @return this for a fluent API. */ public Context timedOutClientCounter(final Counter timedOutClientCounter) { this.timedOutClientCounter = timedOutClientCounter; return this; } /** * {@link Aeron} client for communicating with the local Media Driver. *

* This client will be closed when the {@link ConsensusModule#close()} or {@link #close()} methods are called * if {@link #ownsAeronClient()} is true. *

* The method is mostly here in order to test with a custom Aeron instance. The recommended approach is to set * the aeronDirectoryName and allow the ConsensusModule to construct the Aeron client. The supplied Aeron * client must be set to {@code Aeron.Context.useConductorInvoker(true)} to work correctly with the * ConsensusModule. * * @param aeron client for communicating with the local Media Driver. * @return this for a fluent API. * @see io.aeron.Aeron#connect() * @see io.aeron.Aeron.Context#useConductorAgentInvoker(boolean) * @see #aeronDirectoryName(String) */ public Context aeron(final Aeron aeron) { this.aeron = aeron; return this; } /** * {@link Aeron} client for communicating with the local Media Driver. *

* If not provided then a default will be established during {@link #conclude()} by calling * {@link Aeron#connect()}. * * @return client for communicating with the local Media Driver. */ public Aeron aeron() { return aeron; } /** * Set the top level Aeron directory used for communication between the Aeron client and Media Driver. * * @param aeronDirectoryName the top level Aeron directory. * @return this for a fluent API. */ public Context aeronDirectoryName(final String aeronDirectoryName) { this.aeronDirectoryName = aeronDirectoryName; return this; } /** * Get the top level Aeron directory used for communication between the Aeron client and Media Driver. * * @return The top level Aeron directory. */ public String aeronDirectoryName() { return aeronDirectoryName; } /** * Does this context own the {@link #aeron()} client and this takes responsibility for closing it? * * @param ownsAeronClient does this context own the {@link #aeron()} client. * @return this for a fluent API. */ public Context ownsAeronClient(final boolean ownsAeronClient) { this.ownsAeronClient = ownsAeronClient; return this; } /** * Does this context own the {@link #aeron()} client and this takes responsibility for closing it? * * @return does this context own the {@link #aeron()} client and this takes responsibility for closing it? */ public boolean ownsAeronClient() { return ownsAeronClient; } /** * Set the {@link io.aeron.archive.client.AeronArchive.Context} that should be used for communicating with the * local Archive. * * @param archiveContext that should be used for communicating with the local Archive. * @return this for a fluent API. */ public Context archiveContext(final AeronArchive.Context archiveContext) { this.archiveContext = archiveContext; return this; } /** * Get the {@link io.aeron.archive.client.AeronArchive.Context} that should be used for communicating with * the local Archive. * * @return the {@link io.aeron.archive.client.AeronArchive.Context} that should be used for communicating * with the local Archive. */ public AeronArchive.Context archiveContext() { return archiveContext; } /** * Get the {@link AuthenticatorSupplier} that should be used for the consensus module. * * @return the {@link AuthenticatorSupplier} to be used for the consensus module. */ @Config public AuthenticatorSupplier authenticatorSupplier() { return authenticatorSupplier; } /** * Set the {@link AuthenticatorSupplier} that will be used for the consensus module. * * @param authenticatorSupplier {@link AuthenticatorSupplier} to use for the consensus module. * @return this for a fluent API. */ public Context authenticatorSupplier(final AuthenticatorSupplier authenticatorSupplier) { this.authenticatorSupplier = authenticatorSupplier; return this; } /** * Get the {@link AuthorisationServiceSupplier} that should be used for the consensus module. * * @return the {@link AuthorisationServiceSupplier} to be used for the consensus module. */ @Config public AuthorisationServiceSupplier authorisationServiceSupplier() { return authorisationServiceSupplier; } /** *

Set the {@link AuthorisationServiceSupplier} that will be used for the consensus module.

* *

When using an authorisation service for the ConsensusModule, then the following values for protocolId, * actionId, and type should be considered.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Parameters for authorisation service queries from the Consensus Module
DescriptionprotocolIdactionIdtype(s)
Admin requests made through the client API{@link MessageHeaderDecoder#SCHEMA_ID}{@link AdminRequestDecoder#TEMPLATE_ID}{@link AdminRequestType#SNAPSHOT}
Backup queries from Cluster Backup & Standby{@link BackupQueryDecoder#TEMPLATE_ID}(null)
Heartbeat requests from Cluster Standby{@link HeartbeatRequestDecoder#TEMPLATE_ID}(null)
Standby snapshot notifications from Cluster Standby{@link StandbySnapshotDecoder#TEMPLATE_ID}(null)
* * @param authorisationServiceSupplier {@link AuthorisationServiceSupplier} to use for the consensus module. * @return this for a fluent API. */ public Context authorisationServiceSupplier(final AuthorisationServiceSupplier authorisationServiceSupplier) { this.authorisationServiceSupplier = authorisationServiceSupplier; return this; } /** * Should an {@link AgentInvoker} be used for running the {@link ConsensusModule} rather than run it on * a thread with a {@link AgentRunner}. * * @param useAgentInvoker use {@link AgentInvoker} be used for running the {@link ConsensusModule}? * @return this for a fluent API. */ public ConsensusModule.Context useAgentInvoker(final boolean useAgentInvoker) { this.useAgentInvoker = useAgentInvoker; return this; } /** * Should an {@link AgentInvoker} be used for running the {@link ConsensusModule} rather than run it on * a thread with a {@link AgentRunner}. * * @return true if the {@link ConsensusModule} will be run with an {@link AgentInvoker} otherwise false. */ public boolean useAgentInvoker() { return useAgentInvoker; } /** * Set the {@link ShutdownSignalBarrier} that can be used to shut down a consensus module. * * @param barrier that can be used to shut down a consensus module. * @return this for a fluent API. */ public Context shutdownSignalBarrier(final ShutdownSignalBarrier barrier) { shutdownSignalBarrier = barrier; return this; } /** * Get the {@link ShutdownSignalBarrier} that can be used to shut down a consensus module. * * @return the {@link ShutdownSignalBarrier} that can be used to shut down a consensus module. */ public ShutdownSignalBarrier shutdownSignalBarrier() { return shutdownSignalBarrier; } /** * Set the {@link Runnable} that is called when the {@link ConsensusModule} processes a termination action. * * @param terminationHook that can be used to terminate a consensus module. * @return this for a fluent API. */ public Context terminationHook(final Runnable terminationHook) { this.terminationHook = terminationHook; return this; } /** * Get the {@link Runnable} that is called when the {@link ConsensusModule} processes a termination action. *

* The default action is to call signal on the {@link #shutdownSignalBarrier()}. * * @return the {@link Runnable} that can be used to terminate a consensus module. */ public Runnable terminationHook() { return terminationHook; } /** * Set the {@link ClusterMarkFile} in use. * * @param markFile to use. * @return this for a fluent API. */ public Context clusterMarkFile(final ClusterMarkFile markFile) { this.markFile = markFile; return this; } /** * The {@link ClusterMarkFile} in use. * * @return {@link ClusterMarkFile} in use. */ public ClusterMarkFile clusterMarkFile() { return markFile; } Context nodeStateFile(final NodeStateFile nodeStateFile) { this.nodeStateFile = nodeStateFile; return this; } /** * The {@link NodeStateFile} that is used by the consensus module. * * @return node state file. */ public NodeStateFile nodeStateFile() { return nodeStateFile; } /** * Set the error buffer length in bytes to use. * * @param errorBufferLength in bytes to use. * @return this for a fluent API. * @see Configuration#ERROR_BUFFER_LENGTH_PROP_NAME */ public Context errorBufferLength(final int errorBufferLength) { this.errorBufferLength = errorBufferLength; return this; } /** * The error buffer length in bytes. * * @return error buffer length in bytes. * @see Configuration#ERROR_BUFFER_LENGTH_PROP_NAME */ @Config public int errorBufferLength() { return errorBufferLength; } /** * Set the {@link DistinctErrorLog} in use. * * @param errorLog to use. * @return this for a fluent API. */ public Context errorLog(final DistinctErrorLog errorLog) { this.errorLog = errorLog; return this; } /** * The {@link DistinctErrorLog} in use. * * @return {@link DistinctErrorLog} in use. */ public DistinctErrorLog errorLog() { return errorLog; } /** * The source of random values for timeouts used in elections. * * @param random source of random values for timeouts used in elections. * @return this for a fluent API. */ public Context random(final Random random) { this.random = random; return this; } /** * The source of random values for timeouts used in elections. * * @return source of random values for timeouts used in elections. */ public Random random() { return random; } /** * Provides an {@link TimerService} supplier for the idle strategy for the agent duty cycle. * * @param timerServiceSupplier supplier for the idle strategy for the agent duty cycle. * @return this for a fluent API. */ public Context timerServiceSupplier(final TimerServiceSupplier timerServiceSupplier) { this.timerServiceSupplier = timerServiceSupplier; return this; } /** * Supplier of the {@link TimerService} instances. * * @return supplier of dynamically created {@link TimerService} instances. */ @Config public TimerServiceSupplier timerServiceSupplier() { return timerServiceSupplier; } /** * Register a ConsensusModuleExtension to extend the behaviour of the * consensus module instead of using ClusteredServices. * * @param consensusModuleExtension supplier for consensus module extension * @return this for a fluent API. */ public Context consensusModuleExtension(final ConsensusModuleExtension consensusModuleExtension) { this.consensusModuleExtension = consensusModuleExtension; return this; } /** * Registered consensus module extension. * * @return Registered consensus module extension or null. */ public ConsensusModuleExtension consensusModuleExtension() { return consensusModuleExtension; } /** * Deprecated for removal. * * @return {@code null}. */ @Deprecated public NameResolver nameResolver() { return null; } /** * Deprecated for removal. * * @param ignore as deprecated. * @return this for fluent API. */ @Deprecated public Context nameResolver(final NameResolver ignore) { return this; } /** * Indicate whether this node should accept snapshots from standby nodes * * @return true if this node should accept snapshots from standby nodes, false * otherwise. * @see Configuration#CLUSTER_ACCEPT_STANDBY_SNAPSHOTS_PROP_NAME * @see Configuration#acceptStandbySnapshots() */ @Config(id = "CLUSTER_ACCEPT_STANDBY_SNAPSHOTS") public boolean acceptStandbySnapshots() { return acceptStandbySnapshots; } /** * Indicate whether this node should accept snapshots from standby nodes * * @param acceptStandbySnapshots true if this node should accept snapshots from standby nodes, * false otherwise. * @return this for a fluent API. * @see Configuration#CLUSTER_ACCEPT_STANDBY_SNAPSHOTS_PROP_NAME * @see Configuration#acceptStandbySnapshots() */ public ConsensusModule.Context acceptStandbySnapshots(final boolean acceptStandbySnapshots) { this.acceptStandbySnapshots = acceptStandbySnapshots; return this; } /** * Get the counter used to track standby snapshots accepted by this node. * * @return the counter for standby snapshots. */ public Counter standbySnapshotCounter() { return standbySnapshotCounter; } /** * Set the counter used to track standby snapshots accepted by this node. * * @param standbySnapshotCounter the counter for standby snapshots. * @return this for a fluentAPI. */ public Context standbySnapshotCounter(final Counter standbySnapshotCounter) { this.standbySnapshotCounter = standbySnapshotCounter; return this; } /** * Get the counter used to track the number of elections on this node. * * @return the counter for elections. * @since 1.44.0 */ public Counter electionCounter() { return electionCounter; } /** * Set the counter used to track the number of elections on this node. * * @param electionCounter the counter for elections. * @return this for a fluentAPI. * @since 1.44.0 */ public Context electionCounter(final Counter electionCounter) { this.electionCounter = electionCounter; return this; } /** * Get the counter used to track the leadership term id. * * @return the counter containing the leadership term id. * @since 1.44.0 */ public Counter leadershipTermIdCounter() { return leadershipTermId; } /** * Set the counter used to track the number of elections on this node. * * @param leadershipTermId the counter for elections. * @return this for a fluentAPI. * @since 1.44.0 */ public Context leadershipTermIdCounter(final Counter leadershipTermId) { this.leadershipTermId = leadershipTermId; return this; } /** * Delete the cluster directory. */ public void deleteDirectory() { if (null != clusterDir) { IoUtil.delete(clusterDir, false); } } /** * Close the context and free applicable resources. *

* If {@link #ownsAeronClient()} is true then the {@link #aeron()} client will be closed. */ public void close() { CloseHelper.close(countedErrorHandler, recordingLog); CloseHelper.close(countedErrorHandler, nodeStateFile); CloseHelper.close(countedErrorHandler, markFile); if (errorHandler instanceof AutoCloseable) { CloseHelper.quietClose((AutoCloseable)errorHandler); // Ignore error to ensure the rest will be closed } if (ownsAeronClient) { CloseHelper.close(aeron); } else if (!aeron.isClosed()) { CloseHelper.closeAll( timedOutClientCounter, clusterControlToggle, snapshotCounter, moduleStateCounter, electionStateCounter, clusterNodeRoleCounter, commitPosition); } } Context logPublisher(final LogPublisher logPublisher) { this.logPublisher = logPublisher; return this; } LogPublisher logPublisher() { return logPublisher; } Context egressPublisher(final EgressPublisher egressPublisher) { this.egressPublisher = egressPublisher; return this; } EgressPublisher egressPublisher() { return egressPublisher; } boolean isLogMdc() { return isLogMdc; } /** * Start up the consensus module using pre-supplied state skipping the recovery process. Internal use only. * * @param bootstrapState to initialize the consensus module. * @return this for a fluent API. */ ConsensusModule.Context bootstrapState(final ConsensusModuleStateExport bootstrapState) { this.boostrapState = bootstrapState; return this; } ConsensusModuleStateExport boostrapState() { return boostrapState; } private void concludeMarkFile() { final String aeronDirectory = aeron.context().aeronDirectoryName(); final String authenticatorClassName = authenticatorSupplier.getClass().getName(); ClusterMarkFile.checkHeaderLength( aeronDirectory, controlChannel(), ingressChannel, null, authenticatorClassName); markFile.encoder() .archiveStreamId(archiveContext.controlRequestStreamId()) .serviceStreamId(serviceStreamId) .consensusModuleStreamId(consensusModuleStreamId) .ingressStreamId(ingressStreamId) .memberId(clusterMemberId) .serviceId(SERVICE_ID) .clusterId(clusterId) .aeronDirectory(aeronDirectory) .controlChannel(controlChannel) .ingressChannel(ingressChannel) .serviceName(null) .authenticator(authenticatorClassName) .servicesClusterDir(clusterServicesDirectoryName); markFile.updateActivityTimestamp(epochClock.time()); markFile.signalReady(); markFile.force(); } private TimerServiceSupplier getTimerServiceSupplierFromSystemProperty() { final String timeServiceClassName = Configuration.timerServiceSupplier(); if (WheelTimerServiceSupplier.class.getName().equals(timeServiceClassName)) { return new WheelTimerServiceSupplier( clusterClock.timeUnit(), 0, findNextPositivePowerOfTwo( clusterClock.timeUnit().convert(wheelTickResolutionNs, TimeUnit.NANOSECONDS)), ticksPerWheel); } else if (PriorityHeapTimerServiceSupplier.class.getName().equals(timeServiceClassName)) { return new PriorityHeapTimerServiceSupplier(); } throw new ClusterException("invalid TimerServiceSupplier: " + timeServiceClassName); } private void validateLogChannel() { final ChannelUri logChannelUri = ChannelUri.parse(logChannel); if (logChannelUri.containsKey(INITIAL_TERM_ID_PARAM_NAME)) { throw new ConfigurationException("logChannel must not contain: " + INITIAL_TERM_ID_PARAM_NAME); } if (logChannelUri.containsKey(TERM_ID_PARAM_NAME)) { throw new ConfigurationException("logChannel must not contain: " + TERM_ID_PARAM_NAME); } if (logChannelUri.containsKey(TERM_OFFSET_PARAM_NAME)) { throw new ConfigurationException("logChannel must not contain: " + TERM_OFFSET_PARAM_NAME); } } /** * {@inheritDoc} */ public String toString() { return "ConsensusModule.Context" + "\n{" + "\n isConcluded=" + isConcluded() + "\n ownsAeronClient=" + ownsAeronClient + "\n aeronDirectoryName='" + aeronDirectoryName + '\'' + "\n aeron=" + aeron + "\n deleteDirOnStart=" + deleteDirOnStart + "\n clusterDirectoryName='" + clusterDirectoryName + '\'' + "\n clusterDir=" + clusterDir + "\n recordingLog=" + recordingLog + "\n markFile=" + markFile + "\n fileSyncLevel=" + fileSyncLevel + "\n appVersion=" + appVersion + "\n clusterId=" + clusterId + "\n clusterMemberId=" + clusterMemberId + "\n appointedLeaderId=" + appointedLeaderId + "\n clusterMembers='" + clusterMembers + '\'' + "\n clusterConsensusEndpoints='" + clusterConsensusEndpoints + '\'' + "\n clusterMembersIgnoreSnapshot=" + clusterMembersIgnoreSnapshot + "\n ingressChannel='" + ingressChannel + '\'' + "\n ingressStreamId=" + ingressStreamId + "\n ingressFragmentLimit=" + ingressFragmentLimit + "\n logChannel='" + logChannel + '\'' + "\n logStreamId=" + logStreamId + "\n memberEndpoints='" + memberEndpoints + '\'' + "\n replayChannel='" + replayChannel + '\'' + "\n replayStreamId=" + replayStreamId + "\n controlChannel='" + controlChannel + '\'' + "\n consensusModuleStreamId=" + consensusModuleStreamId + "\n serviceStreamId=" + serviceStreamId + "\n snapshotChannel='" + snapshotChannel + '\'' + "\n snapshotStreamId=" + snapshotStreamId + "\n consensusChannel='" + consensusChannel + '\'' + "\n consensusStreamId=" + consensusStreamId + "\n replicationChannel='" + replicationChannel + '\'' + "\n logFragmentLimit=" + logFragmentLimit + "\n serviceCount=" + serviceCount + "\n errorBufferLength=" + errorBufferLength + "\n maxConcurrentSessions=" + maxConcurrentSessions + "\n ticksPerWheel=" + ticksPerWheel + "\n wheelTickResolutionNs=" + wheelTickResolutionNs + "\n timerServiceSupplier=" + timerServiceSupplier + "\n sessionTimeoutNs=" + sessionTimeoutNs + "\n leaderHeartbeatTimeoutNs=" + leaderHeartbeatTimeoutNs + "\n leaderHeartbeatIntervalNs=" + leaderHeartbeatIntervalNs + "\n startupCanvassTimeoutNs=" + startupCanvassTimeoutNs + "\n electionTimeoutNs=" + electionTimeoutNs + "\n electionStatusIntervalNs=" + electionStatusIntervalNs + "\n terminationTimeoutNs=" + terminationTimeoutNs + "\n threadFactory=" + threadFactory + "\n idleStrategySupplier=" + idleStrategySupplier + "\n clusterClock=" + clusterClock + "\n epochClock=" + epochClock + "\n random=" + random + "\n errorLog=" + errorLog + "\n errorHandler=" + errorHandler + "\n errorCounter=" + errorCounter + "\n countedErrorHandler=" + countedErrorHandler + "\n moduleStateCounter=" + moduleStateCounter + "\n electionStateCounter=" + electionStateCounter + "\n clusterNodeRoleCounter=" + clusterNodeRoleCounter + "\n commitPosition=" + commitPosition + "\n controlToggle=" + clusterControlToggle + "\n nodeControlToggle=" + nodeControlToggle + "\n snapshotCounter=" + snapshotCounter + "\n timedOutClientCounter=" + timedOutClientCounter + "\n standbySnapshotCounter=" + standbySnapshotCounter + "\n electionCounter=" + electionCounter + "\n leadershipTermId=" + leadershipTermId + "\n shutdownSignalBarrier=" + shutdownSignalBarrier + "\n terminationHook=" + terminationHook + "\n archiveContext=" + archiveContext + "\n authenticatorSupplier=" + authenticatorSupplier + "\n authorisationServiceSupplier=" + authorisationServiceSupplier + "\n logPublisher=" + logPublisher + "\n egressPublisher=" + egressPublisher + "\n isLogMdc=" + isLogMdc + "\n useAgentInvoker=" + useAgentInvoker + "\n cycleThresholdNs=" + cycleThresholdNs + "\n dutyCycleTracker=" + dutyCycleTracker + "\n totalSnapshotDurationThresholdNs=" + totalSnapshotDurationThresholdNs + "\n totalSnapshotDurationTracker=" + totalSnapshotDurationTracker + "\n acceptStandbySnapshots=" + acceptStandbySnapshots + "\n boostrapState=" + boostrapState + "\n}"; } } /** * {@inheritDoc} */ public String toString() { return "ConsensusModule{" + "conductor=" + conductor + '}'; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy