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

io.aeron.archive.Archive 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.archive;

import io.aeron.*;
import io.aeron.archive.checksum.Checksum;
import io.aeron.archive.checksum.Checksums;
import io.aeron.archive.client.AeronArchive;
import io.aeron.archive.client.ArchiveException;
import io.aeron.config.Config;
import io.aeron.config.DefaultType;
import io.aeron.driver.DutyCycleTracker;
import io.aeron.driver.status.DutyCycleStallTracker;
import io.aeron.exceptions.AeronException;
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.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.StatusIndicator;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.channels.FileChannel;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.function.Supplier;

import static io.aeron.Aeron.NULL_VALUE;
import static io.aeron.AeronCounters.*;
import static io.aeron.CommonContext.ENDPOINT_PARAM_NAME;
import static io.aeron.archive.Archive.Configuration.ARCHIVE_CONTROL_SESSIONS_TYPE_ID;
import static io.aeron.archive.Archive.Configuration.ERROR_BUFFER_LENGTH_DEFAULT;
import static io.aeron.archive.ArchiveThreadingMode.DEDICATED;
import static io.aeron.logbuffer.LogBufferDescriptor.*;
import static java.lang.System.getProperty;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater;
import static org.agrona.BitUtil.CACHE_LINE_LENGTH;
import static org.agrona.BitUtil.isPowerOfTwo;
import static org.agrona.BufferUtil.allocateDirectAligned;
import static org.agrona.SystemUtil.*;

/**
 * The Aeron Archive which allows for the recording and replay of local and remote {@link io.aeron.Publication}s .
 */
@Versioned
public final class Archive implements AutoCloseable
{
    private final Context ctx;
    private final AgentRunner conductorRunner;
    private final AgentInvoker conductorInvoker;

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

            final ArchiveConductor conductor = DEDICATED == ctx.threadingMode() ?
                (new DedicatedModeArchiveConductor(ctx)) : (new SharedModeArchiveConductor(ctx));

            if (ArchiveThreadingMode.INVOKER == ctx.threadingMode())
            {
                conductorInvoker = new AgentInvoker(ctx.errorHandler(), ctx.errorCounter(), conductor);
                conductorRunner = null;
            }
            else
            {
                conductorInvoker = null;
                conductorRunner = new AgentRunner(
                    ctx.idleStrategy(), ctx.errorHandler(), ctx.errorCounter(), conductor);
            }
        }
        catch (final ConcurrentConcludeException ex)
        {
            throw ex;
        }
        catch (final Exception ex)
        {
            CloseHelper.quietClose(ctx::close);
            throw ex;
        }
    }

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

        final ShutdownSignalBarrier shutdownSignalBarrier = new ShutdownSignalBarrier();
        final Archive.Context ctx = new Context().errorHandler(
            (throwable) ->
            {
                if (throwable instanceof AgentTerminationException)
                {
                    shutdownSignalBarrier.signal();
                }
                else if (AeronException.isFatal(throwable))
                {
                    shutdownSignalBarrier.signal();
                }
            });

        try (Archive ignore = launch(ctx))
        {
            shutdownSignalBarrier.await();
            System.out.println("Shutdown Archive...");
        }
    }

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

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

    /**
     * Get the {@link AgentInvoker} for the archive if it is running in {@link ArchiveThreadingMode#INVOKER}.
     *
     * @return the {@link AgentInvoker} for the archive if it is running in {@link ArchiveThreadingMode#INVOKER}
     * otherwise null.
     */
    public AgentInvoker invoker()
    {
        return conductorInvoker;
    }

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

    /**
     * Launch an Archive by providing a configuration context.
     *
     * @param ctx for the configuration parameters.
     * @return a new instance of an Archive.
     */
    public static Archive launch(final Context ctx)
    {
        final Archive archive = new Archive(ctx);
        if (ArchiveThreadingMode.INVOKER == ctx.threadingMode())
        {
            archive.conductorInvoker.start();
        }
        else
        {
            AgentRunner.startOnThread(archive.conductorRunner, ctx.threadFactory());
        }

        return archive;
    }

    /**
     * Configuration for system properties and defaults.
     * 

* Details for the individual parameters can be found in the Javadoc for the {@link Context} setters. */ @Config(existsInC = false) public static final class Configuration { /** * Filename for the single instance of a {@link Catalog} contents for an archive. */ static final String CATALOG_FILE_NAME = "archive.catalog"; /** * Recording segment file suffix extension. */ static final String RECORDING_SEGMENT_SUFFIX = ".rec"; /** * Default block length of data in a single IO operation during a recording or replay. */ @Config public static final int FILE_IO_MAX_LENGTH_DEFAULT = 1024 * 1024; /** * Maximum length of a file IO operation for recording or replay. Must be a power of 2. */ @Config public static final String FILE_IO_MAX_LENGTH_PROP_NAME = "aeron.archive.file.io.max.length"; /** * Directory in which the archive stores it files such as the catalog and recordings. */ @Config public static final String ARCHIVE_DIR_PROP_NAME = "aeron.archive.dir"; /** * Default directory for the archive files. * * @see #ARCHIVE_DIR_PROP_NAME */ @Config public static final String ARCHIVE_DIR_DEFAULT = "aeron-archive"; /** * Alternative directory to store mark file (i.e. {@code archive-mark.dat}). */ @Config(defaultType = DefaultType.STRING, defaultString = "") public static final String MARK_FILE_DIR_PROP_NAME = "aeron.archive.mark.file.dir"; /** * Recordings will be segmented on disk in files limited to the segment length which must be a multiple of * the term length for each stream. For lots of small recording this value may be reduced. */ @Config public static final String SEGMENT_FILE_LENGTH_PROP_NAME = "aeron.archive.segment.file.length"; /** * Default segment file length which is multiple of terms. * * @see #SEGMENT_FILE_LENGTH_PROP_NAME */ @Config public static final int SEGMENT_FILE_LENGTH_DEFAULT = 128 * 1024 * 1024; /** * Threshold below which the archive will reject new recording requests. */ @Config public static final String LOW_STORAGE_SPACE_THRESHOLD_PROP_NAME = "aeron.archive.low.storage.space.threshold"; /** * Default threshold below which the archive will reject new recording requests. * * @see #LOW_STORAGE_SPACE_THRESHOLD_PROP_NAME */ @Config public static final int LOW_STORAGE_SPACE_THRESHOLD_DEFAULT = SEGMENT_FILE_LENGTH_DEFAULT; /** * The level at which recording 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.archive.file.sync.level"; /** * Default is to use normal file writes which may mean some data loss in the event of a power failure. * * @see #FILE_SYNC_LEVEL_PROP_NAME */ @Config public static final int FILE_SYNC_LEVEL_DEFAULT = 0; /** * The level at which catalog updates and directory should be sync'ed to disk. *
    *
  • 0 - normal writes.
  • *
  • 1 - sync file data.
  • *
  • 2 - sync file data + metadata.
  • *
*/ @Config public static final String CATALOG_FILE_SYNC_LEVEL_PROP_NAME = "aeron.archive.catalog.file.sync.level"; /** * Default is to use normal file writes which may mean some data loss in the event of a power failure. * * @see #CATALOG_FILE_SYNC_LEVEL_PROP_NAME */ @Config public static final int CATALOG_FILE_SYNC_LEVEL_DEFAULT = FILE_SYNC_LEVEL_DEFAULT; /** * What {@link ArchiveThreadingMode} should be used. */ @Config(defaultType = DefaultType.STRING, defaultString = "DEDICATED") public static final String THREADING_MODE_PROP_NAME = "aeron.archive.threading.mode"; /** * Default {@link IdleStrategy} to be used for the archive {@link Agent}s when not busy. */ @Config public static final String ARCHIVE_IDLE_STRATEGY_PROP_NAME = "aeron.archive.idle.strategy"; /** * The {@link IdleStrategy} to be used for the archive recorder {@link Agent} when not busy. */ @Config(defaultType = DefaultType.STRING, defaultString = "") public static final String ARCHIVE_RECORDER_IDLE_STRATEGY_PROP_NAME = "aeron.archive.recorder.idle.strategy"; /** * The {@link IdleStrategy} to be used for the archive replayer {@link Agent} when not busy. */ @Config(defaultType = DefaultType.STRING, defaultString = "") public static final String ARCHIVE_REPLAYER_IDLE_STRATEGY_PROP_NAME = "aeron.archive.replayer.idle.strategy"; /** * Default {@link IdleStrategy} to be used for the archive {@link Agent}s when not busy. * * @see #ARCHIVE_IDLE_STRATEGY_PROP_NAME */ @Config(id = "ARCHIVE_IDLE_STRATEGY") public static final String DEFAULT_IDLE_STRATEGY = "org.agrona.concurrent.BackoffIdleStrategy"; /** * Maximum number of concurrent recordings which can be active at a time. Going beyond this number will * result in an exception and further recordings will be rejected. Since wildcard subscriptions can have * multiple images, and thus multiple recordings, then the limit may come later. It is best to * use session based subscriptions. */ @Config public static final String MAX_CONCURRENT_RECORDINGS_PROP_NAME = "aeron.archive.max.concurrent.recordings"; /** * Default maximum number of concurrent recordings. Unless on a very fast SSD and having sufficient memory * for the page cache then this number should be kept low, especially when sync'ing writes. * * @see #MAX_CONCURRENT_RECORDINGS_PROP_NAME */ @Config public static final int MAX_CONCURRENT_RECORDINGS_DEFAULT = 20; /** * Maximum number of concurrent replays. Beyond this maximum an exception will be raised and further replays * will be rejected. */ @Config public static final String MAX_CONCURRENT_REPLAYS_PROP_NAME = "aeron.archive.max.concurrent.replays"; /** * Default maximum number of concurrent replays. Unless on a fast SSD and having sufficient memory * for the page cache then this number should be kept low. */ @Config public static final int MAX_CONCURRENT_REPLAYS_DEFAULT = 20; /** * Maximum number of entries for the archive {@link Catalog}. Increasing this limit will require use of the * {@link CatalogTool}. The number of entries can be reduced by extending existing recordings rather than * creating new ones. * * @deprecated Use {@link #CATALOG_CAPACITY_PROP_NAME} instead. */ @Deprecated @Config public static final String MAX_CATALOG_ENTRIES_PROP_NAME = "aeron.archive.max.catalog.entries"; /** * Default limit for the entries in the {@link Catalog} * * @see #MAX_CATALOG_ENTRIES_PROP_NAME */ @Deprecated @Config public static final long MAX_CATALOG_ENTRIES_DEFAULT = 8 * 1024; /** * Default capacity in bytes of the archive {@link Catalog}. {@link Catalog} will resize itself when this * limit is reached. */ @Config public static final String CATALOG_CAPACITY_PROP_NAME = "aeron.archive.catalog.capacity"; /** * Default capacity in bytes for the {@link Catalog}. * * @see #CATALOG_CAPACITY_PROP_NAME */ @Config public static final long CATALOG_CAPACITY_DEFAULT = Catalog.DEFAULT_CAPACITY; /** * Timeout for making a connection back to a client for a control session or replay. */ @Config public static final String CONNECT_TIMEOUT_PROP_NAME = "aeron.archive.connect.timeout"; /** * Default timeout for connecting back to a client for a control session or replay. You may want to * increase this on higher latency networks. * * @see #CONNECT_TIMEOUT_PROP_NAME */ @Config(defaultType = DefaultType.LONG, defaultLong = 5L * 1000 * 1000 * 1000) public static final long CONNECT_TIMEOUT_DEFAULT_NS = TimeUnit.SECONDS.toNanos(5); /** * How long a replay publication should linger after all data is sent. Longer linger can help avoid tail loss. */ @Config public static final String REPLAY_LINGER_TIMEOUT_PROP_NAME = "aeron.archive.replay.linger.timeout"; /** * Default for long to linger a replay connection which defaults to * {@link io.aeron.driver.Configuration#publicationLingerTimeoutNs()}. * * @see #REPLAY_LINGER_TIMEOUT_PROP_NAME */ @Config(defaultType = DefaultType.LONG, defaultLong = 5L * 1000 * 1000 * 1000) public static final long REPLAY_LINGER_TIMEOUT_DEFAULT_NS = io.aeron.driver.Configuration.publicationLingerTimeoutNs(); /** * Property name for threshold value for the conductor work cycle threshold to track for being exceeded. */ @Config public static final String CONDUCTOR_CYCLE_THRESHOLD_PROP_NAME = "aeron.archive.conductor.cycle.threshold"; /** * Default threshold value for the conductor work cycle threshold to track for being exceeded. */ @Config(defaultType = DefaultType.LONG, defaultLong = 1000L * 1000 * 1000) public static final long CONDUCTOR_CYCLE_THRESHOLD_DEFAULT_NS = TimeUnit.MILLISECONDS.toNanos(1000); /** * Property name for threshold value for the recorder work cycle threshold to track for being exceeded. */ @Config public static final String RECORDER_CYCLE_THRESHOLD_PROP_NAME = "aeron.archive.recorder.cycle.threshold"; /** * Default threshold value for the recorder work cycle threshold to track for being exceeded. */ @Config(defaultType = DefaultType.LONG, defaultLong = 1000L * 1000 * 1000) public static final long RECORDER_CYCLE_THRESHOLD_DEFAULT_NS = TimeUnit.MILLISECONDS.toNanos(1000); /** * Property name for threshold value for the replayer work cycle threshold to track for being exceeded. */ @Config public static final String REPLAYER_CYCLE_THRESHOLD_PROP_NAME = "aeron.archive.replayer.cycle.threshold"; /** * Default threshold value for the replayer work cycle threshold to track for being exceeded. */ @Config(defaultType = DefaultType.LONG, defaultLong = 1000L * 1000 * 1000) public static final long REPLAYER_CYCLE_THRESHOLD_DEFAULT_NS = TimeUnit.MILLISECONDS.toNanos(1000); /** * Should the archive delete existing files on start. Default is false and should only be true for testing. */ @Config(defaultType = DefaultType.BOOLEAN, defaultBoolean = false) public static final String ARCHIVE_DIR_DELETE_ON_START_PROP_NAME = "aeron.archive.dir.delete.on.start"; /** * Channel for receiving replication streams replayed from another archive. */ @Config(defaultType = DefaultType.STRING, defaultString = "") public static final String REPLICATION_CHANNEL_PROP_NAME = "aeron.archive.replication.channel"; /** * Name of the system property for specifying a supplier of {@link Authenticator} for the archive. */ @Config public static final String AUTHENTICATOR_SUPPLIER_PROP_NAME = "aeron.archive.authenticator.supplier"; /** * Name of the class to use as a supplier of {@link Authenticator} for the archive. Default is * a non-authenticating option. */ @Config public static final String AUTHENTICATOR_SUPPLIER_DEFAULT = "io.aeron.security.DefaultAuthenticatorSupplier"; /** * Name of the system property for specifying a supplier of {@link AuthorisationService} for the archive. */ @Config(defaultType = DefaultType.STRING, defaultString = "") public static final String AUTHORISATION_SERVICE_SUPPLIER_PROP_NAME = "aeron.archive.authorisation.service.supplier"; /** * Default {@link AuthorisationServiceSupplier} that returns {@link AuthorisationService} that allows any * command to be executed (i.e. {@link AuthorisationService#ALLOW_ALL}). */ public static final AuthorisationServiceSupplier DEFAULT_AUTHORISATION_SERVICE_SUPPLIER = () -> AuthorisationService.ALLOW_ALL; /** * The type id of the {@link Counter} used for keeping track of the number of errors that have occurred. */ public static final int ARCHIVE_ERROR_COUNT_TYPE_ID = AeronCounters.ARCHIVE_ERROR_COUNT_TYPE_ID; /** * The type id of the {@link Counter} used for keeping track of the count of concurrent control sessions. */ public static final int ARCHIVE_CONTROL_SESSIONS_TYPE_ID = AeronCounters.ARCHIVE_CONTROL_SESSIONS_TYPE_ID; /** * Size in bytes of the error buffer for the archive when not externally provided. */ @Config public static final String ERROR_BUFFER_LENGTH_PROP_NAME = "aeron.archive.error.buffer.length"; /** * Size in bytes of the error buffer for the archive when not eternally provided. */ @Config public static final int ERROR_BUFFER_LENGTH_DEFAULT = 1024 * 1024; /** * Property that specifies fully qualified class name of the {@link io.aeron.archive.checksum.Checksum} * to be used for checksum computation during recording. */ @Config(defaultType = DefaultType.STRING, defaultString = "") public static final String RECORD_CHECKSUM_PROP_NAME = "aeron.archive.record.checksum"; /** * Property that specifies fully qualified class name of the {@link io.aeron.archive.checksum.Checksum} * to be used for checksum validation during replay. */ @Config(defaultType = DefaultType.STRING, defaultString = "") public static final String REPLAY_CHECKSUM_PROP_NAME = "aeron.archive.replay.checksum"; /** * Property name for the identity of the Archive instance. If not specified defaults to the * {@link Aeron#clientId()} of the assigned {@link Aeron} instance. */ @Config(defaultType = DefaultType.LONG, defaultLong = -1) public static final String ARCHIVE_ID_PROP_NAME = "aeron.archive.id"; /** * Is network (UDP) control channel enabled. Defaults to {@code true}. *

* If set to anything other than {@code true} then control channel is disabled, i.e. the Archive will run in * IPC-only mode. */ @Config(defaultType = DefaultType.BOOLEAN, defaultBoolean = true) public static final String CONTROL_CHANNEL_ENABLED_PROP_NAME = "aeron.archive.control.channel.enabled"; /** * Update interval in ms for archive mark file. */ static final long MARK_FILE_UPDATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(1); /** * Timeout in milliseconds for detecting if there is an active Archive instance. */ static final long LIVENESS_TIMEOUT_MS = 10 * MARK_FILE_UPDATE_INTERVAL_MS; /** * Get the directory name to be used for storing the archive. * * @return the directory name to be used for storing the archive. */ public static String archiveDirName() { return System.getProperty(ARCHIVE_DIR_PROP_NAME, ARCHIVE_DIR_DEFAULT); } /** * Get the alternative directory to be used for storing the archive mark file. * * @return the directory to be used for storing the archive mark file. */ public static String markFileDir() { return System.getProperty(MARK_FILE_DIR_PROP_NAME); } /** * The maximum length of a file IO operation. * * @return the maximum length of a file IO operation. */ public static int fileIoMaxLength() { return getSizeAsInt(FILE_IO_MAX_LENGTH_PROP_NAME, FILE_IO_MAX_LENGTH_DEFAULT); } /** * The length of file to be used for storing recording segments that must be a power of 2. *

* If the {@link Image#termBufferLength()} is greater than this will take priority. * * @return length of file to be used for storing recording segments. */ public static int segmentFileLength() { return getSizeAsInt(SEGMENT_FILE_LENGTH_PROP_NAME, SEGMENT_FILE_LENGTH_DEFAULT); } /** * The low storage space threshold beyond which the archive will reject new requests to record streams. * * @return threshold beyond which the archive will reject new requests to record streams. */ public static long lowStorageSpaceThreshold() { return getSizeAsLong(LOW_STORAGE_SPACE_THRESHOLD_PROP_NAME, LOW_STORAGE_SPACE_THRESHOLD_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. * @see #FILE_SYNC_LEVEL_PROP_NAME */ public static int fileSyncLevel() { return Integer.getInteger(FILE_SYNC_LEVEL_PROP_NAME, FILE_SYNC_LEVEL_DEFAULT); } /** * The level at which the catalog file and directory 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. * @see #CATALOG_FILE_SYNC_LEVEL_PROP_NAME */ public static int catalogFileSyncLevel() { return Integer.getInteger(CATALOG_FILE_SYNC_LEVEL_PROP_NAME, CATALOG_FILE_SYNC_LEVEL_DEFAULT); } /** * The threading mode to be employed by the archive. * * @return the threading mode to be employed by the archive. */ public static ArchiveThreadingMode threadingMode() { return ArchiveThreadingMode.valueOf(System.getProperty(THREADING_MODE_PROP_NAME, DEDICATED.name())); } /** * Create a supplier of {@link IdleStrategy}s for the {@link #ARCHIVE_IDLE_STRATEGY_PROP_NAME} * system property. * * @param controllableStatus if a {@link org.agrona.concurrent.ControllableIdleStrategy} is required. * @return the new idle strategy {@link Supplier}. */ public static Supplier idleStrategySupplier(final StatusIndicator controllableStatus) { return () -> { final String name = System.getProperty(ARCHIVE_IDLE_STRATEGY_PROP_NAME, DEFAULT_IDLE_STRATEGY); return io.aeron.driver.Configuration.agentIdleStrategy(name, controllableStatus); }; } /** * Create a supplier of {@link IdleStrategy}s for the {@link #ARCHIVE_RECORDER_IDLE_STRATEGY_PROP_NAME} * system property. * * @param controllableStatus if a {@link org.agrona.concurrent.ControllableIdleStrategy} is required. * @return the new idle strategy {@link Supplier}. */ public static Supplier recorderIdleStrategySupplier(final StatusIndicator controllableStatus) { final String name = System.getProperty(ARCHIVE_RECORDER_IDLE_STRATEGY_PROP_NAME); if (null == name) { return null; } return () -> io.aeron.driver.Configuration.agentIdleStrategy(name, controllableStatus); } /** * Create a supplier of {@link IdleStrategy}s for the {@link #ARCHIVE_REPLAYER_IDLE_STRATEGY_PROP_NAME} * system property. * * @param controllableStatus if a {@link org.agrona.concurrent.ControllableIdleStrategy} is required. * @return the new idle strategy {@link Supplier}. */ public static Supplier replayerIdleStrategySupplier(final StatusIndicator controllableStatus) { final String name = System.getProperty(ARCHIVE_REPLAYER_IDLE_STRATEGY_PROP_NAME); if (null == name) { return null; } return () -> io.aeron.driver.Configuration.agentIdleStrategy(name, controllableStatus); } /** * The maximum number of recordings that can operate concurrently after which new requests will be rejected. * * @return the maximum number of recordings that can operate concurrently. */ public static int maxConcurrentRecordings() { return Integer.getInteger(MAX_CONCURRENT_RECORDINGS_PROP_NAME, MAX_CONCURRENT_RECORDINGS_DEFAULT); } /** * The maximum number of replays that can operate concurrently after which new requests will be rejected. * * @return the maximum number of replays that can operate concurrently. */ public static int maxConcurrentReplays() { return Integer.getInteger(MAX_CONCURRENT_REPLAYS_PROP_NAME, MAX_CONCURRENT_REPLAYS_DEFAULT); } /** * Maximum number of catalog entries to allocate for the catalog file. * * @return the maximum number of catalog entries to support for the catalog file. * @see #catalogCapacity() * @deprecated Use {@link #catalogCapacity()} instead. */ @Deprecated public static long maxCatalogEntries() { return SystemUtil.getSizeAsLong(MAX_CATALOG_ENTRIES_PROP_NAME, MAX_CATALOG_ENTRIES_DEFAULT); } /** * Default capacity (size) in bytes for the catalog file. * * @return default size of the catalog file in bytes. */ public static long catalogCapacity() { return SystemUtil.getSizeAsLong(CATALOG_CAPACITY_PROP_NAME, CATALOG_CAPACITY_DEFAULT); } /** * The timeout in nanoseconds to wait for a connection. * * @return timeout in nanoseconds to wait for a connection. * @see #CONNECT_TIMEOUT_PROP_NAME */ public static long connectTimeoutNs() { return getDurationInNanos(CONNECT_TIMEOUT_PROP_NAME, CONNECT_TIMEOUT_DEFAULT_NS); } /** * The timeout in nanoseconds to for a replay network publication to linger after draining. * * @return timeout in nanoseconds for a replay network publication to wait in linger. * @see #REPLAY_LINGER_TIMEOUT_PROP_NAME * @see io.aeron.driver.Configuration#PUBLICATION_LINGER_PROP_NAME */ public static long replayLingerTimeoutNs() { return getDurationInNanos(REPLAY_LINGER_TIMEOUT_PROP_NAME, REPLAY_LINGER_TIMEOUT_DEFAULT_NS); } /** * Get threshold value for the conductor work cycle threshold to track for being exceeded. * * @return threshold value in nanoseconds. */ public static long conductorCycleThresholdNs() { return getDurationInNanos(CONDUCTOR_CYCLE_THRESHOLD_PROP_NAME, CONDUCTOR_CYCLE_THRESHOLD_DEFAULT_NS); } /** * Get threshold value for the recorder work cycle threshold to track for being exceeded. * * @return threshold value in nanoseconds. */ public static long recorderCycleThresholdNs() { return getDurationInNanos(RECORDER_CYCLE_THRESHOLD_PROP_NAME, RECORDER_CYCLE_THRESHOLD_DEFAULT_NS); } /** * Get threshold value for the replayer work cycle threshold to track for being exceeded. * * @return threshold value in nanoseconds. */ public static long replayerCycleThresholdNs() { return getDurationInNanos(REPLAYER_CYCLE_THRESHOLD_PROP_NAME, REPLAYER_CYCLE_THRESHOLD_DEFAULT_NS); } /** * Whether to delete directory on start or not. * * @return whether to delete directory on start or not. * @see #ARCHIVE_DIR_DELETE_ON_START_PROP_NAME */ public static boolean deleteArchiveOnStart() { return "true".equals(getProperty(ARCHIVE_DIR_DELETE_ON_START_PROP_NAME, "false")); } /** * The system property {@link #REPLICATION_CHANNEL_PROP_NAME} if set, null otherwise. * * @return system property {@link #REPLICATION_CHANNEL_PROP_NAME} if set. */ public static String replicationChannel() { return System.getProperty(REPLICATION_CHANNEL_PROP_NAME); } /** * 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 #AUTHENTICATOR_SUPPLIER_DEFAULT} or system property * {@link #AUTHENTICATOR_SUPPLIER_PROP_NAME} if set. * * @return {@link #AUTHENTICATOR_SUPPLIER_DEFAULT} or system property * {@link #AUTHENTICATOR_SUPPLIER_PROP_NAME} if set. */ public static AuthenticatorSupplier authenticatorSupplier() { final String supplierClassName = System.getProperty( AUTHENTICATOR_SUPPLIER_PROP_NAME, AUTHENTICATOR_SUPPLIER_DEFAULT); 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; } } /** * Fully qualified class name of the {@link io.aeron.archive.checksum.Checksum} implementation to use during * recording to compute checksums. Non-empty value means that checksum is enabled for recording. * * @return class that implements {@link io.aeron.archive.checksum.Checksum} interface * @see Configuration#RECORD_CHECKSUM_PROP_NAME */ public static String recordChecksum() { return getProperty(RECORD_CHECKSUM_PROP_NAME); } /** * Fully qualified class name of the {@link io.aeron.archive.checksum.Checksum} implementation to use during * replay for the checksum. Non-empty value means that checksum is enabled for replay. * * @return class that implements {@link io.aeron.archive.checksum.Checksum} interface * @see Configuration#REPLAY_CHECKSUM_PROP_NAME */ public static String replayChecksum() { return getProperty(REPLAY_CHECKSUM_PROP_NAME); } /** * Should the network (UDP) control channel be enabled. * * @return {@code true} if the network control channel to be enabled. * @see #CONTROL_CHANNEL_ENABLED_PROP_NAME */ public static boolean controlChannelEnabled() { return "true".equals(System.getProperty(CONTROL_CHANNEL_ENABLED_PROP_NAME, "true")); } /** * Return configured id for the Archive. * * @return id for the Archive or {@link Aeron#NULL_VALUE}. * @see #ARCHIVE_ID_PROP_NAME */ public static long archiveId() { final String prop = getProperty(Configuration.ARCHIVE_ID_PROP_NAME); if (!Strings.isEmpty(prop)) { return AsciiEncoding.parseLongAscii(prop, 0, prop.length()); } return NULL_VALUE; } } /** * Overrides for the defaults and system properties. *

* The context will be owned by {@link ArchiveConductor} after a successful * {@link Archive#launch(Context)} and closed via {@link Archive#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 deleteArchiveOnStart = Configuration.deleteArchiveOnStart(); private boolean ownsAeronClient = false; private String aeronDirectoryName = CommonContext.getAeronDirectoryName(); private Aeron aeron; private File archiveDir; private File markFileDir; private String archiveDirectoryName = Configuration.archiveDirName(); private FileChannel archiveDirChannel; private FileStore archiveFileStore; private Catalog catalog; private ArchiveMarkFile markFile; private AeronArchive.Context archiveClientContext; private AgentInvoker mediaDriverAgentInvoker; private boolean controlChannelEnabled = Configuration.controlChannelEnabled(); private String controlChannel = AeronArchive.Configuration.controlChannel(); private int controlStreamId = AeronArchive.Configuration.controlStreamId(); private String localControlChannel = AeronArchive.Configuration.localControlChannel(); private int localControlStreamId = AeronArchive.Configuration.localControlStreamId(); private boolean controlTermBufferSparse = AeronArchive.Configuration.controlTermBufferSparse(); private int controlTermBufferLength = AeronArchive.Configuration.controlTermBufferLength(); private int controlMtuLength = AeronArchive.Configuration.controlMtuLength(); private String recordingEventsChannel = AeronArchive.Configuration.recordingEventsChannel(); private int recordingEventsStreamId = AeronArchive.Configuration.recordingEventsStreamId(); private boolean recordingEventsEnabled = AeronArchive.Configuration.recordingEventsEnabled(); private String replicationChannel = Configuration.replicationChannel(); private long connectTimeoutNs = Configuration.connectTimeoutNs(); private long replayLingerTimeoutNs = Configuration.replayLingerTimeoutNs(); private long conductorCycleThresholdNs = Configuration.conductorCycleThresholdNs(); private long recorderCycleThresholdNs = Configuration.recorderCycleThresholdNs(); private long replayerCycleThresholdNs = Configuration.replayerCycleThresholdNs(); private long catalogCapacity = Configuration.catalogCapacity(); private long lowStorageSpaceThreshold = Configuration.lowStorageSpaceThreshold(); private int segmentFileLength = Configuration.segmentFileLength(); private int fileSyncLevel = Configuration.fileSyncLevel(); private int catalogFileSyncLevel = Configuration.catalogFileSyncLevel(); private int maxConcurrentRecordings = Configuration.maxConcurrentRecordings(); private int maxConcurrentReplays = Configuration.maxConcurrentReplays(); private int fileIoMaxLength = Configuration.fileIoMaxLength(); private long archiveId = Configuration.archiveId(); private ArchiveThreadingMode threadingMode = Configuration.threadingMode(); private ThreadFactory threadFactory; private ThreadFactory recorderThreadFactory; private ThreadFactory replayerThreadFactory; private CountDownLatch abortLatch; private Supplier idleStrategySupplier; private Supplier replayerIdleStrategySupplier; private Supplier recorderIdleStrategySupplier; private EpochClock epochClock; private NanoClock nanoClock; private AuthenticatorSupplier authenticatorSupplier; private AuthorisationServiceSupplier authorisationServiceSupplier; private Counter controlSessionsCounter; private Counter recordingSessionCounter; private Counter replaySessionCounter; private int errorBufferLength = Configuration.errorBufferLength(); private ErrorHandler errorHandler; private AtomicCounter errorCounter; private CountedErrorHandler countedErrorHandler; private Checksum recordChecksum; private Checksum replayChecksum; private UnsafeBuffer dataBuffer; private UnsafeBuffer replayBuffer; private UnsafeBuffer recordChecksumBuffer; private DutyCycleTracker conductorDutyCycleTracker; private DutyCycleTracker recorderDutyCycleTracker; private DutyCycleTracker replayerDutyCycleTracker; private Counter totalWriteBytesCounter; private Counter totalWriteTimeCounter; private Counter maxWriteTimeCounter; private Counter totalReadBytesCounter; private Counter totalReadTimeCounter; private Counter maxReadTimeCounter; /** * 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 the configuration parameters by resolving dependencies and null values to use defaults. */ @SuppressWarnings("MethodLength") public void conclude() { if (0 != IS_CONCLUDED_UPDATER.getAndSet(this, 1)) { throw new ConcurrentConcludeException(); } if (catalogFileSyncLevel < fileSyncLevel) { throw new ConfigurationException( "catalogFileSyncLevel " + catalogFileSyncLevel + " < fileSyncLevel " + fileSyncLevel); } if (fileIoMaxLength < TERM_MIN_LENGTH || !BitUtil.isPowerOfTwo(fileIoMaxLength)) { throw new ConfigurationException("invalid fileIoMaxLength=" + fileIoMaxLength); } io.aeron.driver.Configuration.validateMtuLength(controlMtuLength); checkTermLength(controlTermBufferLength); if (controlChannelEnabled) { if (null == controlChannel) { throw new ConfigurationException("Archive.Context.controlChannel must be set"); } if (!controlChannel.startsWith(CommonContext.UDP_CHANNEL)) { throw new ConfigurationException( "Archive.Context.controlChannel must be UDP media: uri=" + controlChannel); } } if (!localControlChannel.startsWith(CommonContext.IPC_CHANNEL)) { throw new ConfigurationException("local control channel must be IPC media: uri=" + localControlChannel); } if (null == replicationChannel) { throw new ConfigurationException("Archive.Context.replicationChannel must be set"); } if (recordingEventsEnabled() && null == recordingEventsChannel()) { throw new ConfigurationException( "Archive.Context.recordingEventsChannel must be set if " + "Archive.Context.recordingEventsEnabled is true"); } if (null == archiveDir) { archiveDir = new File(archiveDirectoryName); } if (null == markFileDir) { final String markFileDirPath = Configuration.markFileDir(); markFileDir = !Strings.isEmpty(markFileDirPath) ? new File(markFileDirPath) : archiveDir; } try { archiveDir = archiveDir.getCanonicalFile(); archiveDirectoryName = archiveDir.getAbsolutePath(); markFileDir = markFileDir.getCanonicalFile(); } catch (final IOException e) { throw new UncheckedIOException(e); } if (deleteArchiveOnStart) { IoUtil.delete(archiveDir, false); } IoUtil.ensureDirectoryExists(archiveDir, "archive"); IoUtil.ensureDirectoryExists(markFileDir, "mark file"); archiveDirChannel = channelForDirectorySync(archiveDir, catalogFileSyncLevel); if (null == archiveFileStore) { try { archiveFileStore = Files.getFileStore(archiveDir.toPath()); } catch (final IOException ex) { throw new UncheckedIOException(ex); } } if (null == epochClock) { epochClock = SystemEpochClock.INSTANCE; } if (null == nanoClock) { nanoClock = SystemNanoClock.INSTANCE; } if (null != aeron) { aeronDirectoryName = aeron.context().aeronDirectoryName(); } if (null == markFile) { if (errorBufferLength < ERROR_BUFFER_LENGTH_DEFAULT || errorBufferLength > Integer.MAX_VALUE - ArchiveMarkFile.HEADER_LENGTH) { throw new ConfigurationException("invalid errorBufferLength=" + errorBufferLength); } markFile = new ArchiveMarkFile(this); } try { MarkFile.ensureMarkFileLink( archiveDir, new File(markFile.parentDirectory(), ArchiveMarkFile.FILENAME), ArchiveMarkFile.LINK_FILENAME); errorHandler = CommonContext.setupErrorHandler( errorHandler, new DistinctErrorLog(markFile.errorBuffer(), epochClock, US_ASCII)); final ExpandableArrayBuffer tempBuffer = new ExpandableArrayBuffer(); if (null == aeron) { ownsAeronClient = true; aeron = Aeron.connect( new Aeron.Context() .aeronDirectoryName(aeronDirectoryName) .epochClock(epochClock) .nanoClock(nanoClock) .errorHandler(RethrowingErrorHandler.INSTANCE) .driverAgentInvoker(mediaDriverAgentInvoker) .useConductorAgentInvoker(true) .subscriberErrorHandler(RethrowingErrorHandler.INSTANCE) .awaitingIdleStrategy(YieldingIdleStrategy.INSTANCE) .clientLock(NoOpLock.INSTANCE) .clientName(NULL_VALUE != archiveId ? "archive-" + archiveId : "archive")); if (null == errorCounter) { concludeArchiveId(); if (NULL_VALUE != ArchiveCounters.find(aeron.countersReader(), ARCHIVE_ERROR_COUNT_TYPE_ID, archiveId)) { throw new ArchiveException("found existing archive for archiveId=" + archiveId); } errorCounter = ArchiveCounters.allocateErrorCounter(aeron, tempBuffer, archiveId); } } else if (!aeron.context().useConductorAgentInvoker()) { throw new ArchiveException( "Aeron client instance must set Aeron.Context.useConductorInvoker(true)"); } concludeArchiveId(); if (!(aeron.context().subscriberErrorHandler() instanceof RethrowingErrorHandler)) { throw new ArchiveException("Aeron client must use a RethrowingErrorHandler"); } Objects.requireNonNull(errorCounter, "Error counter must be supplied if aeron client is"); if (null == countedErrorHandler) { countedErrorHandler = new CountedErrorHandler(errorHandler, errorCounter); } if (null == threadFactory) { threadFactory = Thread::new; } if (null == recorderThreadFactory) { recorderThreadFactory = threadFactory; } if (null == replayerThreadFactory) { replayerThreadFactory = threadFactory; } if (null == idleStrategySupplier) { idleStrategySupplier = Configuration.idleStrategySupplier(null); } if (null == conductorDutyCycleTracker) { conductorDutyCycleTracker = new DutyCycleStallTracker( ArchiveCounters.allocate( aeron, tempBuffer, AeronCounters.ARCHIVE_MAX_CYCLE_TIME_TYPE_ID, "archive-conductor max cycle time in ns: " + threadingMode.name(), archiveId), ArchiveCounters.allocate( aeron, tempBuffer, AeronCounters.ARCHIVE_CYCLE_TIME_THRESHOLD_EXCEEDED_TYPE_ID, "archive-conductor work cycle time exceeded count: threshold=" + conductorCycleThresholdNs + "ns " + threadingMode.name(), archiveId), conductorCycleThresholdNs); } if (DEDICATED == threadingMode) { if (null == recorderIdleStrategySupplier) { recorderIdleStrategySupplier = Configuration.recorderIdleStrategySupplier(null); if (null == recorderIdleStrategySupplier) { recorderIdleStrategySupplier = idleStrategySupplier; } } if (null == replayerIdleStrategySupplier) { replayerIdleStrategySupplier = Configuration.replayerIdleStrategySupplier(null); if (null == replayerIdleStrategySupplier) { replayerIdleStrategySupplier = idleStrategySupplier; } } if (null == recorderDutyCycleTracker) { recorderDutyCycleTracker = new DutyCycleStallTracker( ArchiveCounters.allocate( aeron, tempBuffer, AeronCounters.ARCHIVE_MAX_CYCLE_TIME_TYPE_ID, "archive-recorder max cycle time in ns", archiveId), ArchiveCounters.allocate( aeron, tempBuffer, AeronCounters.ARCHIVE_CYCLE_TIME_THRESHOLD_EXCEEDED_TYPE_ID, "archive-recorder work cycle time exceeded count: threshold=" + recorderCycleThresholdNs + "ns", archiveId), recorderCycleThresholdNs); } if (null == replayerDutyCycleTracker) { replayerDutyCycleTracker = new DutyCycleStallTracker( ArchiveCounters.allocate( aeron, tempBuffer, AeronCounters.ARCHIVE_MAX_CYCLE_TIME_TYPE_ID, "archive-replayer max cycle time in ns", archiveId), ArchiveCounters.allocate( aeron, tempBuffer, AeronCounters.ARCHIVE_CYCLE_TIME_THRESHOLD_EXCEEDED_TYPE_ID, "archive-replayer work cycle time exceeded count: threshold=" + replayerCycleThresholdNs + "ns", archiveId), replayerCycleThresholdNs); } } if (!isPowerOfTwo(segmentFileLength)) { throw new ArchiveException("segment file length not a power of 2: " + segmentFileLength); } else if (segmentFileLength < TERM_MIN_LENGTH || segmentFileLength > TERM_MAX_LENGTH) { throw new ArchiveException("segment file length not in valid range: " + segmentFileLength); } if (null == authenticatorSupplier) { authenticatorSupplier = Configuration.authenticatorSupplier(); } if (null == authorisationServiceSupplier) { authorisationServiceSupplier = Configuration.authorisationServiceSupplier(); } concludeRecordChecksum(); concludeReplayChecksum(); if (null == catalog) { catalog = new Catalog( archiveDir, archiveDirChannel, catalogFileSyncLevel, catalogCapacity, epochClock, recordChecksum, null != recordChecksum ? recordChecksumBuffer() : dataBuffer()); } if (null == archiveClientContext) { archiveClientContext = new AeronArchive.Context(); } if (null == archiveClientContext.controlResponseChannel()) { if (controlChannelEnabled) { final ChannelUri controlChannelUri = ChannelUri.parse(controlChannel); final String endpoint = controlChannelUri.get(ENDPOINT_PARAM_NAME); final int separatorIndex; if (null == endpoint || -1 == (separatorIndex = endpoint.lastIndexOf(':'))) { throw new ConfigurationException( "Unable to derive Archive.Context.archiveClientContext.controlResponseChannel as " + "Archive.Context.controlChannel.endpoint=" + endpoint + " and is not in the : format"); } final String responseEndpoint = endpoint.substring(0, separatorIndex) + ":0"; final String responseChannel = new ChannelUriStringBuilder() .media("udp") .endpoint(responseEndpoint) .build(); archiveClientContext.controlResponseChannel(responseChannel); } else { throw new ConfigurationException( "Archive.Context.archiveClientContext.controlResponseChannel must be set if " + "Archive.Context.controlChannelEnabled is false" ); } } archiveClientContext.aeron(aeron).lock(NoOpLock.INSTANCE).errorHandler(errorHandler); if (null == controlSessionsCounter) { controlSessionsCounter = ArchiveCounters.allocate( aeron, tempBuffer, ARCHIVE_CONTROL_SESSIONS_TYPE_ID, "Archive Control Sessions", archiveId); } validateCounterTypeId(aeron, controlSessionsCounter, ARCHIVE_CONTROL_SESSIONS_TYPE_ID); if (null == recordingSessionCounter) { recordingSessionCounter = ArchiveCounters.allocate( aeron, tempBuffer, ARCHIVE_RECORDING_SESSION_COUNT_TYPE_ID, "Archive Recording Sessions", archiveId); } validateCounterTypeId(aeron, recordingSessionCounter, ARCHIVE_RECORDING_SESSION_COUNT_TYPE_ID); if (null == replaySessionCounter) { replaySessionCounter = ArchiveCounters.allocate( aeron, tempBuffer, ARCHIVE_REPLAY_SESSION_COUNT_TYPE_ID, "Archive Replay Sessions", archiveId); } validateCounterTypeId(aeron, replaySessionCounter, ARCHIVE_REPLAY_SESSION_COUNT_TYPE_ID); if (null == maxWriteTimeCounter) { final int counterId = ArchiveCounters.find( aeron.countersReader(), ARCHIVE_RECORDER_MAX_WRITE_TIME_TYPE_ID, archiveId); if (NULL_VALUE != counterId) { throw new ConfigurationException( "existing max write time counter detected for archiveId=" + archiveId); } maxWriteTimeCounter = ArchiveCounters.allocate( aeron, tempBuffer, ARCHIVE_RECORDER_MAX_WRITE_TIME_TYPE_ID, "archive-recorder max write time in ns", archiveId); } validateCounterTypeId(aeron, maxWriteTimeCounter, ARCHIVE_RECORDER_MAX_WRITE_TIME_TYPE_ID); if (null == totalWriteBytesCounter) { totalWriteBytesCounter = ArchiveCounters.allocate( aeron, tempBuffer, ARCHIVE_RECORDER_TOTAL_WRITE_BYTES_TYPE_ID, "archive-recorder total write bytes", archiveId); } validateCounterTypeId(aeron, totalWriteBytesCounter, ARCHIVE_RECORDER_TOTAL_WRITE_BYTES_TYPE_ID); if (null == totalWriteTimeCounter) { totalWriteTimeCounter = ArchiveCounters.allocate( aeron, tempBuffer, ARCHIVE_RECORDER_TOTAL_WRITE_TIME_TYPE_ID, "archive-recorder total write time in ns", archiveId); } validateCounterTypeId(aeron, totalWriteTimeCounter, ARCHIVE_RECORDER_TOTAL_WRITE_TIME_TYPE_ID); if (null == maxReadTimeCounter) { maxReadTimeCounter = ArchiveCounters.allocate( aeron, tempBuffer, ARCHIVE_REPLAYER_MAX_READ_TIME_TYPE_ID, "archive-replayer max read time in ns", archiveId); } validateCounterTypeId(aeron, maxReadTimeCounter, ARCHIVE_REPLAYER_MAX_READ_TIME_TYPE_ID); if (null == totalReadBytesCounter) { totalReadBytesCounter = ArchiveCounters.allocate( aeron, tempBuffer, ARCHIVE_REPLAYER_TOTAL_READ_BYTES_TYPE_ID, "archive-replayer total read bytes", archiveId); } validateCounterTypeId(aeron, totalReadBytesCounter, ARCHIVE_REPLAYER_TOTAL_READ_BYTES_TYPE_ID); if (null == totalReadTimeCounter) { totalReadTimeCounter = ArchiveCounters.allocate( aeron, tempBuffer, ARCHIVE_REPLAYER_TOTAL_READ_TIME_TYPE_ID, "archive-replayer total read time in ns", archiveId); } validateCounterTypeId(aeron, totalReadTimeCounter, ARCHIVE_REPLAYER_TOTAL_READ_TIME_TYPE_ID); int expectedCount = DEDICATED == threadingMode ? 2 : 0; expectedCount += aeron.conductorAgentInvoker() == null ? 1 : 0; abortLatch = new CountDownLatch(expectedCount); markFile.updateActivityTimestamp(epochClock.time()); } finally { markFile.signalReady(); markFile.force(); } 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 an existing archive be deleted on start. Useful only for testing. * * @param deleteArchiveOnStart true if an existing archive should be deleted on startup. * @return this for a fluent API. */ public Context deleteArchiveOnStart(final boolean deleteArchiveOnStart) { this.deleteArchiveOnStart = deleteArchiveOnStart; return this; } /** * Should an existing archive be deleted on start. Useful only for testing. * * @return {@code true} if an existing archive should be deleted on start up. */ @Config(id = "ARCHIVE_DIR_DELETE_ON_START") public boolean deleteArchiveOnStart() { return deleteArchiveOnStart; } /** * Set the directory name to be used for the archive to store recordings and the {@link Catalog}. * This name is used if {@link #archiveDir(File)} is not set. * * @param archiveDirectoryName to store recordings and the {@link Catalog}. * @return this for a fluent API. * @see Configuration#ARCHIVE_DIR_PROP_NAME */ public Context archiveDirectoryName(final String archiveDirectoryName) { this.archiveDirectoryName = archiveDirectoryName; return this; } /** * Get the directory name to be used to store recordings and the {@link Catalog}. * * @return the directory name to be used for the archive to store recordings and the {@link Catalog}. */ @Config(id = "ARCHIVE_DIR") public String archiveDirectoryName() { return archiveDirectoryName; } /** * Get the directory in which the Archive will store recordings and the {@link Catalog}. * * @return the directory in which the Archive will store recordings and the {@link Catalog}. */ public File archiveDir() { return archiveDir; } /** * Set the directory in which the Archive will store recordings and the {@link Catalog}. * * @param archiveDir the directory in which the Archive will store recordings and the {@link Catalog}. * @return this for a fluent API. */ public Context archiveDir(final File archiveDir) { this.archiveDir = archiveDir; return this; } /** * Get the directory in which the Archive will store mark file (i.e. {@code archive-mark.dat}). It defaults to * {@link #archiveDir()} if it is not set explicitly via the * {@link Configuration#MARK_FILE_DIR_PROP_NAME}. * * @return the directory in which the Archive will store mark file (i.e. {@code archive-mark.dat}). * @see Configuration#MARK_FILE_DIR_PROP_NAME * @see #archiveDir() */ @Config public File markFileDir() { return markFileDir; } /** * Set the directory in which the Archive will store mark file (i.e. {@code archive-mark.dat}). * * @param markFileDir the directory in which the Archive will store mark file (i.e. {@code archive-mark.dat}). * @return this for a fluent API. */ public Context markFileDir(final File markFileDir) { this.markFileDir = markFileDir; return this; } /** * Get the {@link FileStore} where the archive will record streams. * * @return the {@link FileStore} where the archive will record streams. */ public FileStore archiveFileStore() { return archiveFileStore; } /** * Set the {@link FileStore} where the archive will record streams. This should only be used for testing. * * @param fileStore where the archive will record streams. * @return this for a fluent API. */ public Context archiveFileStore(final FileStore fileStore) { this.archiveFileStore = fileStore; return this; } /** * Get the {@link FileChannel} for the directory in which the Archive will store recordings and the * {@link Catalog}. This can be used for sync'ing the directory. * * @return the directory in which the Archive will store recordings and the {@link Catalog}. */ public FileChannel archiveDirChannel() { return archiveDirChannel; } Context archiveDirChannel(final FileChannel archiveDirChannel) { this.archiveDirChannel = archiveDirChannel; return this; } /** * Set the {@link io.aeron.archive.client.AeronArchive.Context} that should be used for communicating * with a remote archive for replication. * * @param archiveContext that should be used for communicating with a remote Archive. * @return this for a fluent API. */ public Context archiveClientContext(final AeronArchive.Context archiveContext) { this.archiveClientContext = archiveContext; return this; } /** * Get the {@link io.aeron.archive.client.AeronArchive.Context} that should be used for communicating * with a remote archive for replication. * * @return the {@link io.aeron.archive.client.AeronArchive.Context} that should be used for communicating * with a remote archive for replication. */ public AeronArchive.Context archiveClientContext() { return archiveClientContext; } /** * Should the UDP control channel be enabled. * * @return {@code true} if the UDP control channel should be enabled. * @see Configuration#CONTROL_CHANNEL_ENABLED_PROP_NAME */ @Config public boolean controlChannelEnabled() { return controlChannelEnabled; } /** * Set if the UDP control channel should be enabled. * * @param controlChannelEnabled indication of if the network control channel should be enabled. * @return this for a fluent API. * @see Configuration#CONTROL_CHANNEL_ENABLED_PROP_NAME */ public Context controlChannelEnabled(final boolean controlChannelEnabled) { this.controlChannelEnabled = controlChannelEnabled; return this; } /** * Get the channel URI on which the control request subscription will listen. * * @return the channel URI on which the control request subscription will listen. * @see io.aeron.archive.client.AeronArchive.Configuration#CONTROL_CHANNEL_PROP_NAME */ public String controlChannel() { return controlChannel; } /** * Set the channel URI on which the control request subscription will listen. * * @param controlChannel channel URI on which the control request subscription will listen. * @return this for a fluent API. * @see io.aeron.archive.client.AeronArchive.Configuration#CONTROL_CHANNEL_PROP_NAME */ public Context controlChannel(final String controlChannel) { this.controlChannel = controlChannel; return this; } /** * Get the stream id on which the control request subscription will listen. * * @return the stream id on which the control request subscription will listen. * @see io.aeron.archive.client.AeronArchive.Configuration#CONTROL_STREAM_ID_PROP_NAME */ public int controlStreamId() { return controlStreamId; } /** * Set the stream id on which the control request subscription will listen. * * @param controlStreamId stream id on which the control request subscription will listen. * @return this for a fluent API. * @see io.aeron.archive.client.AeronArchive.Configuration#CONTROL_STREAM_ID_PROP_NAME */ public Context controlStreamId(final int controlStreamId) { this.controlStreamId = controlStreamId; return this; } /** * Get the driver local channel URI on which the control request subscription will listen. * * @return the channel URI on which the control request subscription will listen. * @see io.aeron.archive.client.AeronArchive.Configuration#LOCAL_CONTROL_CHANNEL_PROP_NAME */ public String localControlChannel() { return localControlChannel; } /** * Set the driver local channel URI on which the control request subscription will listen. * * @param controlChannel channel URI on which the control request subscription will listen. * @return this for a fluent API. * @see io.aeron.archive.client.AeronArchive.Configuration#LOCAL_CONTROL_CHANNEL_PROP_NAME */ public Context localControlChannel(final String controlChannel) { this.localControlChannel = controlChannel; return this; } /** * Get the local stream id on which the control request subscription will listen. * * @return the stream id on which the control request subscription will listen. * @see io.aeron.archive.client.AeronArchive.Configuration#LOCAL_CONTROL_STREAM_ID_PROP_NAME */ public int localControlStreamId() { return localControlStreamId; } /** * Should the control streams use sparse file term buffers. * * @param controlTermBufferSparse for the control stream. * @return this for a fluent API. * @see io.aeron.archive.client.AeronArchive.Configuration#CONTROL_TERM_BUFFER_SPARSE_PROP_NAME */ public Context controlTermBufferSparse(final boolean controlTermBufferSparse) { this.controlTermBufferSparse = controlTermBufferSparse; return this; } /** * Should the control streams use sparse file term buffers. * * @return {@code true} if the control stream should use sparse file term buffers. * @see io.aeron.archive.client.AeronArchive.Configuration#CONTROL_TERM_BUFFER_SPARSE_PROP_NAME */ public boolean controlTermBufferSparse() { return controlTermBufferSparse; } /** * Set the term buffer length for the control streams. * * @param controlTermBufferLength for the control streams. * @return this for a fluent API. * @see io.aeron.archive.client.AeronArchive.Configuration#CONTROL_TERM_BUFFER_LENGTH_PROP_NAME */ public Context controlTermBufferLength(final int controlTermBufferLength) { this.controlTermBufferLength = controlTermBufferLength; return this; } /** * Get the term buffer length for the control streams. * * @return the term buffer length for the control streams. * @see io.aeron.archive.client.AeronArchive.Configuration#CONTROL_TERM_BUFFER_LENGTH_PROP_NAME */ public int controlTermBufferLength() { return controlTermBufferLength; } /** * Set the MTU length for the control streams. * * @param controlMtuLength for the control streams. * @return this for a fluent API. * @see io.aeron.archive.client.AeronArchive.Configuration#CONTROL_MTU_LENGTH_PROP_NAME */ public Context controlMtuLength(final int controlMtuLength) { this.controlMtuLength = controlMtuLength; return this; } /** * Get the MTU length for the control streams. * * @return the MTU length for the control streams. * @see io.aeron.archive.client.AeronArchive.Configuration#CONTROL_MTU_LENGTH_PROP_NAME */ public int controlMtuLength() { return controlMtuLength; } /** * Set the local stream id on which the control request subscription will listen. * * @param controlStreamId stream id on which the control request subscription will listen. * @return this for a fluent API. * @see io.aeron.archive.client.AeronArchive.Configuration#LOCAL_CONTROL_STREAM_ID_PROP_NAME */ public Context localControlStreamId(final int controlStreamId) { this.localControlStreamId = controlStreamId; return this; } /** * Get the channel URI on which the recording events publication will publish. Will be null if not configured. * * @return the channel URI on which the recording events publication will publish. * @see io.aeron.archive.client.AeronArchive.Configuration#RECORDING_EVENTS_CHANNEL_PROP_NAME */ public String recordingEventsChannel() { return recordingEventsChannel; } /** * Set the channel URI on which the recording events publication will publish. *

* To support dynamic subscribers then this can be set to multicast or MDC (Multi-Destination-Cast) if * multicast cannot be supported for on the available the network infrastructure. * * @param recordingEventsChannel channel URI on which the recording events publication will publish. * @return this for a fluent API. * @see io.aeron.archive.client.AeronArchive.Configuration#RECORDING_EVENTS_CHANNEL_PROP_NAME * @see io.aeron.CommonContext#MDC_CONTROL_PARAM_NAME */ public Context recordingEventsChannel(final String recordingEventsChannel) { this.recordingEventsChannel = recordingEventsChannel; return this; } /** * Get the stream id on which the recording events publication will publish. * * @return the stream id on which the recording events publication will publish. * @see io.aeron.archive.client.AeronArchive.Configuration#RECORDING_EVENTS_STREAM_ID_PROP_NAME */ public int recordingEventsStreamId() { return recordingEventsStreamId; } /** * Set the stream id on which the recording events publication will publish. * * @param recordingEventsStreamId stream id on which the recording events publication will publish. * @return this for a fluent API. * @see io.aeron.archive.client.AeronArchive.Configuration#RECORDING_EVENTS_STREAM_ID_PROP_NAME */ public Context recordingEventsStreamId(final int recordingEventsStreamId) { this.recordingEventsStreamId = recordingEventsStreamId; return this; } /** * Should the recording events channel be enabled. * * @return {@code true} if the recording events channel should be enabled. * @see io.aeron.archive.client.AeronArchive.Configuration#RECORDING_EVENTS_ENABLED_PROP_NAME */ @Config public boolean recordingEventsEnabled() { return recordingEventsEnabled; } /** * Set if the recording events channel should be enabled. * * @param recordingEventsEnabled indication of if the recording events channel should be enabled. * @return this for a fluent API. * @see io.aeron.archive.client.AeronArchive.Configuration#RECORDING_EVENTS_ENABLED_PROP_NAME */ public Context recordingEventsEnabled(final boolean recordingEventsEnabled) { this.recordingEventsEnabled = recordingEventsEnabled; return this; } /** * Get the channel URI for replicating stream from another archive as replays. * * @return the channel URI for replicating stream from another archive as replays. * @see Archive.Configuration#REPLICATION_CHANNEL_PROP_NAME */ @Config public String replicationChannel() { return replicationChannel; } /** * The channel URI for replicating stream from another archive as replays. * * @param replicationChannel channel URI for replicating stream from another archive as replays. * @return this for a fluent API. * @see Archive.Configuration#REPLICATION_CHANNEL_PROP_NAME */ public Context replicationChannel(final String replicationChannel) { this.replicationChannel = replicationChannel; return this; } /** * The timeout in nanoseconds to wait for connection to be established. * * @param connectTimeoutNs to wait for a connection to be established. * @return this for a fluent API. * @see Configuration#CONNECT_TIMEOUT_PROP_NAME */ public Context connectTimeoutNs(final long connectTimeoutNs) { this.connectTimeoutNs = connectTimeoutNs; return this; } /** * The timeout in nanoseconds to wait for connection to be established. * * @return the message timeout in nanoseconds to wait for a connection to be established. * @see Configuration#CONNECT_TIMEOUT_PROP_NAME */ @Config public long connectTimeoutNs() { return connectTimeoutNs; } /** * The timeout in nanoseconds for or a replay publication to linger after draining. * * @param replayLingerTimeoutNs in nanoseconds for a replay publication to linger after draining. * @return this for a fluent API. * @see Configuration#REPLAY_LINGER_TIMEOUT_PROP_NAME * @see io.aeron.driver.Configuration#PUBLICATION_LINGER_PROP_NAME */ public Context replayLingerTimeoutNs(final long replayLingerTimeoutNs) { this.replayLingerTimeoutNs = replayLingerTimeoutNs; return this; } /** * The timeout in nanoseconds for a replay publication to linger after draining. * * @return the timeout in nanoseconds for a replay publication to linger after draining. * @see Configuration#REPLAY_LINGER_TIMEOUT_PROP_NAME * @see io.aeron.driver.Configuration#PUBLICATION_LINGER_PROP_NAME */ @Config public long replayLingerTimeoutNs() { return replayLingerTimeoutNs; } /** * Set a threshold for the conductor work cycle time which when exceed it will increment the * conductor cycle time exceeded count. * * @param thresholdNs value in nanoseconds * @return this for fluent API. * @see io.aeron.archive.Archive.Configuration#CONDUCTOR_CYCLE_THRESHOLD_PROP_NAME * @see io.aeron.archive.Archive.Configuration#CONDUCTOR_CYCLE_THRESHOLD_DEFAULT_NS */ public Context conductorCycleThresholdNs(final long thresholdNs) { this.conductorCycleThresholdNs = thresholdNs; return this; } /** * Threshold for the conductor work cycle time which when exceed it will increment the * conductor cycle time exceeded count. * * @return threshold to track for the conductor work cycle time. */ @Config public long conductorCycleThresholdNs() { return conductorCycleThresholdNs; } /** * Set a threshold for the recorder work cycle time which when exceed it will increment the * recorder cycle time exceeded count. * * @param thresholdNs value in nanoseconds * @return this for fluent API. * @see io.aeron.archive.Archive.Configuration#RECORDER_CYCLE_THRESHOLD_PROP_NAME * @see io.aeron.archive.Archive.Configuration#RECORDER_CYCLE_THRESHOLD_DEFAULT_NS */ public Context recorderCycleThresholdNs(final long thresholdNs) { this.recorderCycleThresholdNs = thresholdNs; return this; } /** * Threshold for the recorder work cycle time which when exceed it will increment the * recorder cycle time exceeded count. * * @return threshold to track for the recorder work cycle time. */ @Config public long recorderCycleThresholdNs() { return recorderCycleThresholdNs; } /** * Set a threshold for the replayer work cycle time which when exceed it will increment the * replayer cycle time exceeded count. * * @param thresholdNs value in nanoseconds * @return this for fluent API. * @see io.aeron.archive.Archive.Configuration#REPLAYER_CYCLE_THRESHOLD_PROP_NAME * @see io.aeron.archive.Archive.Configuration#REPLAYER_CYCLE_THRESHOLD_DEFAULT_NS */ public Context replayerCycleThresholdNs(final long thresholdNs) { this.replayerCycleThresholdNs = thresholdNs; return this; } /** * Threshold for the replayer work cycle time which when exceed it will increment the * replayer cycle time exceeded count. * * @return threshold to track for the replayer work cycle time. */ @Config public long replayerCycleThresholdNs() { return replayerCycleThresholdNs; } /** * Set the duty cycle tracker for the conductor. * * @param dutyCycleTracker for the conductor. * @return this for a fluent API. */ public Context conductorDutyCycleTracker(final DutyCycleTracker dutyCycleTracker) { this.conductorDutyCycleTracker = dutyCycleTracker; return this; } /** * The duty cycle tracker for the conductor. * * @return duty cycle tracker for the conductor. */ public DutyCycleTracker conductorDutyCycleTracker() { return conductorDutyCycleTracker; } /** * Set the duty cycle tracker for the recorder. * NOTE: Only used in DEDICATED threading mode. * * @param dutyCycleTracker for the recorder. * @return this for a fluent API. */ public Context recorderDutyCycleTracker(final DutyCycleTracker dutyCycleTracker) { this.recorderDutyCycleTracker = dutyCycleTracker; return this; } /** * The duty cycle tracker for the recorder. * NOTE: Only used in DEDICATED threading mode. * * @return duty cycle tracker for the recorder. */ public DutyCycleTracker recorderDutyCycleTracker() { return recorderDutyCycleTracker; } /** * Set the duty cycle tracker for the replayer. * NOTE: Only used in DEDICATED threading mode. * * @param dutyCycleTracker for the replayer. * @return this for a fluent API. */ public Context replayerDutyCycleTracker(final DutyCycleTracker dutyCycleTracker) { this.replayerDutyCycleTracker = dutyCycleTracker; return this; } /** * The duty cycle tracker for the replayer. * NOTE: Only used in DEDICATED threading mode. * * @return duty cycle tracker for the replayer. */ public DutyCycleTracker replayerDutyCycleTracker() { return replayerDutyCycleTracker; } /** * Provides an explicit {@link Checksum} for checksum computation during recording. * * @param recordChecksum to be used for recordings. * @return this for a fluent API. * @see Configuration#RECORD_CHECKSUM_PROP_NAME */ public Context recordChecksum(final Checksum recordChecksum) { this.recordChecksum = recordChecksum; return this; } /** * Get the {@link Checksum} for checksum computation during recording. * * @return the {@link Checksum} instance for checksum computation during recording or * {@code null} if no {@link Checksum} was configured. * @see Configuration#RECORD_CHECKSUM_PROP_NAME */ @Config public Checksum recordChecksum() { return recordChecksum; } /** * The {@link Checksum} for checksum computation during replay. * * @param replayChecksum to be used for replays. * @return this for a fluent API. * @see Configuration#REPLAY_CHECKSUM_PROP_NAME */ public Context replayChecksum(final Checksum replayChecksum) { this.replayChecksum = replayChecksum; return this; } /** * Get the {@link Checksum} for checksum computation during replay. * * @return the {@link Checksum} instance for checksum computation during replay or * {@code null} if no replay {@link Checksum} was configured. * @see Configuration#REPLAY_CHECKSUM_PROP_NAME */ @Config public Checksum replayChecksum() { return replayChecksum; } /** * Provides an {@link IdleStrategy} supplier for idling the conductor or composite {@link Agent}. Which is also * the default for recorder and replayer {@link Agent}s. * * @param idleStrategySupplier supplier for idling the conductor. * @return this for a fluent API. */ public Context idleStrategySupplier(final Supplier idleStrategySupplier) { this.idleStrategySupplier = idleStrategySupplier; return this; } /** * Get a new {@link IdleStrategy} for idling the conductor or composite {@link Agent}. Which is also * the default for recorder and replayer {@link Agent}s. * * @return a new {@link IdleStrategy} for idling the conductor or composite {@link Agent}. */ @Config(id = "ARCHIVE_IDLE_STRATEGY") public IdleStrategy idleStrategy() { return idleStrategySupplier.get(); } /** * Provides an {@link IdleStrategy} supplier for idling the recorder {@link Agent}. * * @param idleStrategySupplier supplier for idling the conductor. * @return this for a fluent API. */ public Context recorderIdleStrategySupplier(final Supplier idleStrategySupplier) { this.recorderIdleStrategySupplier = idleStrategySupplier; return this; } /** * Get a new {@link IdleStrategy} for idling the recorder {@link Agent}. * * @return a new {@link IdleStrategy} for idling the recorder {@link Agent}. */ @Config(id = "ARCHIVE_RECORDER_IDLE_STRATEGY") public IdleStrategy recorderIdleStrategy() { return recorderIdleStrategySupplier.get(); } /** * Provides an {@link IdleStrategy} supplier for idling the replayer {@link Agent}. * * @param idleStrategySupplier supplier for idling the replayer. * @return this for a fluent API. */ public Context replayerIdleStrategySupplier(final Supplier idleStrategySupplier) { this.replayerIdleStrategySupplier = idleStrategySupplier; return this; } /** * Get a new {@link IdleStrategy} for idling the replayer {@link Agent}. * * @return a new {@link IdleStrategy} for idling the replayer {@link Agent}. */ @Config(id = "ARCHIVE_REPLAYER_IDLE_STRATEGY") public IdleStrategy replayerIdleStrategy() { return replayerIdleStrategySupplier.get(); } /** * 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 {@link NanoClock} to be used for tracking wall clock time. * * @param clock {@link NanoClock} to be used for tracking wall clock time. * @return this for a fluent API. */ public Context nanoClock(final NanoClock clock) { this.nanoClock = clock; return this; } /** * Get the {@link NanoClock} to used for tracking wall clock time. * * @return the {@link NanoClock} to used for tracking wall clock time. */ public NanoClock nanoClock() { return nanoClock; } /** * Get the file length used for recording data segment files. * * @return the file length used for recording data segment files * @see Configuration#SEGMENT_FILE_LENGTH_PROP_NAME */ @Config int segmentFileLength() { return segmentFileLength; } /** * Set the file length to be used for recording data segment files. If the {@link Image#termBufferLength()} is * larger than the segment file length then the term length will be used. * * @param segmentFileLength the file length to be used for recording data segment files. * @return this for a fluent API. * @see Configuration#SEGMENT_FILE_LENGTH_PROP_NAME */ public Context segmentFileLength(final int segmentFileLength) { this.segmentFileLength = segmentFileLength; return this; } /** * 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 #catalogFileSyncLevel() * @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 #catalogFileSyncLevel() * @see Configuration#FILE_SYNC_LEVEL_PROP_NAME */ public Context fileSyncLevel(final int syncLevel) { this.fileSyncLevel = syncLevel; return this; } /** * Get level at which the catalog file 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 #fileSyncLevel() * @see Configuration#CATALOG_FILE_SYNC_LEVEL_PROP_NAME */ @Config int catalogFileSyncLevel() { return catalogFileSyncLevel; } /** * Set level at which the catalog file 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 #fileSyncLevel() * @see Configuration#CATALOG_FILE_SYNC_LEVEL_PROP_NAME */ public Context catalogFileSyncLevel(final int syncLevel) { this.catalogFileSyncLevel = syncLevel; return this; } /** * Get the {@link AgentInvoker} that should be used for the Media Driver if running in a lightweight mode. * * @return the {@link AgentInvoker} that should be used for the Media Driver if running in a lightweight mode. */ AgentInvoker mediaDriverAgentInvoker() { return mediaDriverAgentInvoker; } /** * Set the {@link AgentInvoker} that should be used for the Media Driver if running in a lightweight mode. * * @param mediaDriverAgentInvoker that should be used for the Media Driver if running in a lightweight mode. * @return this for a fluent API. */ public Context mediaDriverAgentInvoker(final AgentInvoker mediaDriverAgentInvoker) { this.mediaDriverAgentInvoker = mediaDriverAgentInvoker; return this; } /** * Get the {@link ErrorHandler} to be used by the Archive. * * @return the {@link ErrorHandler} to be used by the Archive. */ public ErrorHandler errorHandler() { return errorHandler; } /** * Set the {@link ErrorHandler} to be used by the Archive. * * @param errorHandler the error handler to be used by the Archive. * @return this for a fluent API */ public Context errorHandler(final ErrorHandler errorHandler) { this.errorHandler = errorHandler; return this; } /** * Non-default for context. * * @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 archive threading mode. * * @return the archive threading mode. * @see Configuration#THREADING_MODE_PROP_NAME */ @Config public ArchiveThreadingMode threadingMode() { return threadingMode; } /** * Set the archive threading mode. * * @param threadingMode archive threading mode. * @return this for a fluent API. * @see Configuration#THREADING_MODE_PROP_NAME */ public Context threadingMode(final ArchiveThreadingMode threadingMode) { this.threadingMode = threadingMode; return this; } /** * Get the thread factory used for creating threads in {@link ArchiveThreadingMode#SHARED} and * {@link ArchiveThreadingMode#DEDICATED} threading modes. * * @return thread factory used for creating threads in SHARED and DEDICATED threading modes. */ public ThreadFactory threadFactory() { return threadFactory; } /** * Set the thread factory used for creating threads in {@link ArchiveThreadingMode#SHARED} and * {@link ArchiveThreadingMode#DEDICATED} threading modes. The thread factories can be overridden for the * recorder and replayer by using {@link #recorderThreadFactory(ThreadFactory)} and * {@link #replayerThreadFactory(ThreadFactory)} respectively. * * @param threadFactory used for creating threads in SHARED and DEDICATED threading modes. * @return this for a fluent API. */ public Context threadFactory(final ThreadFactory threadFactory) { this.threadFactory = threadFactory; return this; } /** * Get the thread factory used for creating the recorder thread when running in DEDICATED threading mode. * * @return the thread factory used for creating the recorder thread when running in DEDICATED threading mode. */ public ThreadFactory recorderThreadFactory() { return recorderThreadFactory; } /** * Set the thread factory used for creating the recorder thread when running in DEDICATED threading mode. This * will be used to override {@link #threadFactory}. * * @param threadFactory used for creating the recorder thread when running in DEDICATED threading mode. * @return this for a fluent API. */ public Context recorderThreadFactory(final ThreadFactory threadFactory) { this.recorderThreadFactory = threadFactory; return this; } /** * Get the thread factory used for creating the replayer thread when running in DEDICATED threading mode. * * @return the thread factory used for creating the replayer thread when running in DEDICATED threading mode. */ public ThreadFactory replayerThreadFactory() { return replayerThreadFactory; } /** * Set the thread factory used for creating the replayer thread when running in DEDICATED threading mode. This * will be used to override {@link #threadFactory}. * * @param threadFactory used for creating the replayer thread when running in DEDICATED threading mode. * @return this for a fluent API. */ public Context replayerThreadFactory(final ThreadFactory threadFactory) { this.replayerThreadFactory = threadFactory; return this; } /** * 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 id for this Archive instance. * * @param archiveId for this Archive instance. * @return this for a fluent API * @see io.aeron.archive.Archive.Configuration#ARCHIVE_ID_PROP_NAME */ public Context archiveId(final long archiveId) { this.archiveId = archiveId; return this; } /** * Get the id of this Archive instance. * * @return the id of this Archive instance. * @see io.aeron.archive.Archive.Configuration#ARCHIVE_ID_PROP_NAME */ @Config public long archiveId() { return archiveId; } /** * 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 used to track the number of active control sessions. * * @return the counter used to track the number of active control sessions. */ public Counter controlSessionsCounter() { return controlSessionsCounter; } /** * Set the counter used to track the number of active control sessions. * * @param controlSessionsCounter the counter used to track the number of active control sessions. * @return this for a fluent API. */ public Context controlSessionsCounter(final Counter controlSessionsCounter) { this.controlSessionsCounter = controlSessionsCounter; return this; } /** * Get the counter used to track the count of concurrent recording sessions. * * @return the counter used to track the count of concurrent recording sessions. */ public Counter recordingSessionCounter() { return recordingSessionCounter; } /** * Set the counter used to track the count of concurrent recording sessions. * * @param counter used to track the count of concurrent recording sessions. * @return this for a fluent API. */ public Context recordingSessionCounter(final Counter counter) { this.recordingSessionCounter = counter; return this; } /** * Get the counter used to track the count of concurrent replay sessions. * * @return the counter used to track the count of concurrent replay sessions. */ public Counter replaySessionCounter() { return replaySessionCounter; } /** * Set the counter used to track the count of concurrent replay sessions. * * @param counter used to track the count of concurrent replay sessions. * @return this for a fluent API. */ public Context replaySessionCounter(final Counter counter) { this.replaySessionCounter = counter; return this; } /** * Get the counter used to track the total number of bytes written by the recorder. * * @return the counter used to track the total number of bytes written by the recorder. */ public Counter totalWriteBytesCounter() { return totalWriteBytesCounter; } /** * Set the counter used to track the total number of bytes written by the recorder. * * @param counter used to track the total number of bytes written by the recorder. * @return this for a fluent API. */ public Context totalWriteBytesCounter(final Counter counter) { this.totalWriteBytesCounter = counter; return this; } /** * Get the counter used to track the total time used by the recorder to write data. * * @return the counter used to track the total time used by the recorder to write data. */ public Counter totalWriteTimeCounter() { return totalWriteTimeCounter; } /** * Set the counter used to track the total time used by the recorder to write data. * * @param counter used to track the total time used by the recorder to write data. * @return this for a fluent API. */ public Context totalWriteTimeCounter(final Counter counter) { this.totalWriteTimeCounter = counter; return this; } /** * Get the counter used to track the max time used by the recorder to write a block of data. * * @return the counter used to track the max time used by the recorder to write a block of data. */ public Counter maxWriteTimeCounter() { return maxWriteTimeCounter; } /** * Set the counter used to track the max time used by the recorder to write a block of data. * * @param counter used to track the max time used by the recorder to write a block of data. * @return this for a fluent API. */ public Context maxWriteTimeCounter(final Counter counter) { maxWriteTimeCounter = counter; return this; } /** * Get the counter used to track the total number of bytes read by the replayer. * * @return the counter used to track the total number of bytes read by the replayer. */ public Counter totalReadBytesCounter() { return totalReadBytesCounter; } /** * Set the counter used to track the total number of bytes read by the replayer. * * @param counter used to track the total number of bytes read by the replayer. * @return this for a fluent API. */ public Context totalReadBytesCounter(final Counter counter) { this.totalReadBytesCounter = counter; return this; } /** * Get the counter used to track the total time used by the replayer to read data. * * @return the counter used to track the total time used by the replayer to read data. */ public Counter totalReadTimeCounter() { return totalReadTimeCounter; } /** * Set the counter used to track the total time used by the replayer to read data. * * @param counter used to track the total time used by the replayer to read data. * @return this for a fluent API. */ public Context totalReadTimeCounter(final Counter counter) { this.totalReadTimeCounter = counter; return this; } /** * Get the counter used to track the max time used by the replayer to read a block of data. * * @return the counter used to track the max time used by the replayer to read a block of data. */ public Counter maxReadTimeCounter() { return maxReadTimeCounter; } /** * Set the counter used to track the max time used by the replayer to read a block of data. * * @param counter used to track the max time used by the replayer to read a block of data. * @return this for a fluent API. */ public Context maxReadTimeCounter(final Counter counter) { this.maxReadTimeCounter = counter; return this; } /** * Get the max number of concurrent recordings. * * @return the max number of concurrent recordings. * @see Configuration#MAX_CONCURRENT_RECORDINGS_PROP_NAME */ @Config public int maxConcurrentRecordings() { return maxConcurrentRecordings; } /** * Set the max number of concurrent recordings. * * @param maxConcurrentRecordings the max number of concurrent recordings. * @return this for a fluent API. * @see Configuration#MAX_CONCURRENT_RECORDINGS_PROP_NAME */ public Context maxConcurrentRecordings(final int maxConcurrentRecordings) { this.maxConcurrentRecordings = maxConcurrentRecordings; return this; } /** * Get the max number of concurrent replays. * * @return the max number of concurrent replays. * @see Configuration#MAX_CONCURRENT_REPLAYS_PROP_NAME */ @Config public int maxConcurrentReplays() { return maxConcurrentReplays; } /** * Set the max number of concurrent replays. * * @param maxConcurrentReplays the max number of concurrent replays. * @return this for a fluent API. * @see Configuration#MAX_CONCURRENT_REPLAYS_PROP_NAME */ public Context maxConcurrentReplays(final int maxConcurrentReplays) { this.maxConcurrentReplays = maxConcurrentReplays; return this; } /** * Get the max length of a file IO operation. * * @return the max length of a file IO operation. * @see Configuration#FILE_IO_MAX_LENGTH_PROP_NAME */ @Config public int fileIoMaxLength() { return fileIoMaxLength; } /** * Set the max length of a file IO operation. * * @param fileIoMaxLength the max length of a file IO operation. * @return this for a fluent API. * @see Configuration#FILE_IO_MAX_LENGTH_PROP_NAME */ public Context fileIoMaxLength(final int fileIoMaxLength) { this.fileIoMaxLength = fileIoMaxLength; return this; } /** * Threshold below which the archive will reject new recording requests. * * @param lowStorageSpaceThreshold in bytes. * @return this for a fluent API. * @see Configuration#LOW_STORAGE_SPACE_THRESHOLD_PROP_NAME */ public Context lowStorageSpaceThreshold(final long lowStorageSpaceThreshold) { this.lowStorageSpaceThreshold = lowStorageSpaceThreshold; return this; } /** * Threshold below which the archive will reject new recording requests. * * @return threshold below which the archive will reject new recording requests in bytes. * @see Configuration#LOW_STORAGE_SPACE_THRESHOLD_PROP_NAME */ @Config public long lowStorageSpaceThreshold() { return lowStorageSpaceThreshold; } /** * Delete the archive directory if the {@link #archiveDir()} value is not null. *

* Use {@link #deleteDirectory()} instead. */ @Deprecated public void deleteArchiveDirectory() { deleteDirectory(); } /** * Delete the archive directory if the {@link #archiveDir()} value is not null. */ public void deleteDirectory() { if (null != archiveDir) { IoUtil.delete(archiveDir, false); } } /** * 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; } /** * {@link Aeron} client for communicating with the local Media Driver. *

* This client will be closed when the {@link Archive#close()} or {@link #close()} methods are called if * {@link #ownsAeronClient()} is true. * * @param aeron client for communicating with the local Media Driver. * @return this for a fluent API. * @see Aeron#connect() */ 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; } /** * 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; } /** * The {@link Catalog} describing the contents of the Archive. * * @param catalog {@link Catalog} describing the contents of the Archive. * @return this for a fluent API. */ Context catalog(final Catalog catalog) { this.catalog = catalog; return this; } /** * The {@link Catalog} describing the contents of the Archive. * * @return the {@link Catalog} describing the contents of the Archive. */ Catalog catalog() { return catalog; } /** * The {@link ArchiveMarkFile} for the Archive. * * @param archiveMarkFile {@link ArchiveMarkFile} for the Archive. * @return this for a fluent API. */ public Context archiveMarkFile(final ArchiveMarkFile archiveMarkFile) { this.markFile = archiveMarkFile; return this; } /** * The {@link ArchiveMarkFile} for the Archive. * * @return {@link ArchiveMarkFile} for the Archive. */ public ArchiveMarkFile archiveMarkFile() { return markFile; } /** * Maximum number of catalog entries for the Archive. *

* Note: This method has no effect. * * @param maxCatalogEntries for the archive. * @return this for a fluent API. * @see #catalogCapacity(long) * @deprecated This method was deprecated in favor of {@link #catalogCapacity(long)} which works with bytes * rather than number of entries. */ @Deprecated public Context maxCatalogEntries(final long maxCatalogEntries) { return this; } /** * Maximum number of catalog entries for the Archive. *

* Note: This method is not used. * * @return maximum number of catalog entries for the Archive. * @see #catalogCapacity() * @deprecated This method was deprecated in favor of {@link #catalogCapacity()} which returns capacity of * the {@link Catalog} in bytes rather than in number of entries. */ @Deprecated @Config public long maxCatalogEntries() { return -1; } /** * Capacity in bytes of the {@link Catalog}. * * @param catalogCapacity in bytes. * @return this for a fluent API. * @see Configuration#CATALOG_CAPACITY_PROP_NAME */ public Context catalogCapacity(final long catalogCapacity) { this.catalogCapacity = catalogCapacity; return this; } /** * Capacity in bytes of the {@link Catalog}. * * @return capacity in bytes of the {@link Catalog}. * @see Configuration#CATALOG_CAPACITY_PROP_NAME */ @Config public long catalogCapacity() { return catalogCapacity; } /** * Get the {@link AuthenticatorSupplier} that should be used for the Archive. * * @return the {@link AuthenticatorSupplier} to be used for the Archive. */ @Config public AuthenticatorSupplier authenticatorSupplier() { return authenticatorSupplier; } /** * Set the {@link AuthenticatorSupplier} that will be used for the Archive. * * @param authenticatorSupplier {@link AuthenticatorSupplier} to use for the Archive. * @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 Archive. * * @return the {@link AuthorisationServiceSupplier} to be used for the Archive. */ @Config public AuthorisationServiceSupplier authorisationServiceSupplier() { return authorisationServiceSupplier; } /** *

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

*

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 Archive
DescriptionprotocolIdactionIdtype(s)
Start the recording of a stream{@link io.aeron.archive.codecs.MessageHeaderDecoder#SCHEMA_ID}{@link io.aeron.archive.codecs.StartRecordingRequestDecoder#TEMPLATE_ID}(null)
Start the recording of a stream (version 2){@link io.aeron.archive.codecs.StartRecordingRequest2Decoder#TEMPLATE_ID}(null)
Stop the recording of a stream{@link io.aeron.archive.codecs.StopRecordingRequestDecoder#TEMPLATE_ID}(null)
Replay a stream from the archive{@link io.aeron.archive.codecs.ReplayRequestDecoder#TEMPLATE_ID}(null)
Stop a replay from the archive{@link io.aeron.archive.codecs.StopReplayRequestDecoder#TEMPLATE_ID}(null)
List the recordings from the archive{@link io.aeron.archive.codecs.ListRecordingsRequestDecoder#TEMPLATE_ID}(null)
List the recordings from the archive for a specific URI{@link io.aeron.archive.codecs.ListRecordingsForUriRequestDecoder#TEMPLATE_ID}(null)
List a specific recording from the archive{@link io.aeron.archive.codecs.ListRecordingRequestDecoder#TEMPLATE_ID}(null)
Extend a recording{@link io.aeron.archive.codecs.ExtendRecordingRequestDecoder#TEMPLATE_ID}(null)
Extend a recording (version 2){@link io.aeron.archive.codecs.ExtendRecordingRequest2Decoder#TEMPLATE_ID}(null)
Gets the position of a recording{@link io.aeron.archive.codecs.RecordingPositionRequestDecoder#TEMPLATE_ID}(null)
Truncate a recording{@link io.aeron.archive.codecs.TruncateRecordingRequestDecoder#TEMPLATE_ID}(null)
Stop a recording by subscription{@link io.aeron.archive.codecs.StopRecordingSubscriptionRequestDecoder#TEMPLATE_ID}(null)
Extend a recording{@link io.aeron.archive.codecs.ExtendRecordingRequestDecoder#TEMPLATE_ID}(null)
Get the stop position for a recording{@link io.aeron.archive.codecs.StopPositionRequestDecoder#TEMPLATE_ID}(null)
Find the last recording for a given stream, session and channel fragment{@link io.aeron.archive.codecs.FindLastMatchingRecordingRequestDecoder#TEMPLATE_ID}(null)
List subscriptions being used for recordings{@link io.aeron.archive.codecs.ListRecordingSubscriptionsRequestDecoder#TEMPLATE_ID}(null)
Stop all replays{@link io.aeron.archive.codecs.StopAllReplaysRequestDecoder#TEMPLATE_ID}(null)
Start replicating a recording{@link io.aeron.archive.codecs.ReplicateRequestDecoder#TEMPLATE_ID}(null)
Start replicating a recording (version 2){@link io.aeron.archive.codecs.ReplicateRequest2Decoder#TEMPLATE_ID}(null)
Stop replication a recording{@link io.aeron.archive.codecs.StopReplicationRequestDecoder#TEMPLATE_ID}(null)
Get the start position of a recording{@link io.aeron.archive.codecs.StartRecordingRequestDecoder#TEMPLATE_ID}(null)
Detach segment files from a recording{@link io.aeron.archive.codecs.DetachSegmentsRequestDecoder#TEMPLATE_ID}(null)
Delete detached segments{@link io.aeron.archive.codecs.DeleteDetachedSegmentsRequestDecoder#TEMPLATE_ID}(null)
Attach new segments for a recording{@link io.aeron.archive.codecs.AttachSegmentsRequestDecoder#TEMPLATE_ID}(null)
Migrate segments from one recording to another{@link io.aeron.archive.codecs.MigrateSegmentsRequestDecoder#TEMPLATE_ID}(null)
Keep alive the archive connection{@link io.aeron.archive.codecs.KeepAliveRequestDecoder#TEMPLATE_ID}(null)
Replicate a recording with a tag{@link io.aeron.archive.codecs.TaggedReplicateRequestDecoder#TEMPLATE_ID}(null)
Stop recording by recording id{@link io.aeron.archive.codecs.StopRecordingByIdentityRequestDecoder#TEMPLATE_ID}(null)
Purge a recording by recording id{@link io.aeron.archive.codecs.PurgeRecordingRequestDecoder#TEMPLATE_ID}(null)
* * @param authorisationServiceSupplier {@link AuthorisationServiceSupplier} to use for the Archive. * @return this for a fluent API. */ public Context authorisationServiceSupplier(final AuthorisationServiceSupplier authorisationServiceSupplier) { this.authorisationServiceSupplier = authorisationServiceSupplier; return this; } CountDownLatch abortLatch() { return abortLatch; } void concludeRecordChecksum() { if (null == recordChecksum) { final String checksumClass = Configuration.recordChecksum(); if (!Strings.isEmpty(checksumClass)) { recordChecksum = Checksums.newInstance(checksumClass); } } } void concludeReplayChecksum() { if (null == replayChecksum) { final String checksumClass = Configuration.replayChecksum(); if (!Strings.isEmpty(checksumClass)) { replayChecksum = Checksums.newInstance(checksumClass); } } } Context dataBuffer(final UnsafeBuffer dataBuffer) { this.dataBuffer = dataBuffer; return this; } UnsafeBuffer dataBuffer() { if (null == dataBuffer) { dataBuffer = new UnsafeBuffer(allocateDirectAligned(fileIoMaxLength, CACHE_LINE_LENGTH)); } return dataBuffer; } Context replayBuffer(final UnsafeBuffer replayBuffer) { this.replayBuffer = replayBuffer; return this; } UnsafeBuffer replayBuffer() { if (DEDICATED != threadingMode) { return dataBuffer(); } if (null == replayBuffer) { replayBuffer = new UnsafeBuffer(allocateDirectAligned(fileIoMaxLength, CACHE_LINE_LENGTH)); } return replayBuffer; } Context recordChecksumBuffer(final UnsafeBuffer recordChecksumBuffer) { this.recordChecksumBuffer = recordChecksumBuffer; return this; } UnsafeBuffer recordChecksumBuffer() { if (null == recordChecksum) { return null; } if (DEDICATED != threadingMode) { return dataBuffer(); } if (null == recordChecksumBuffer) { recordChecksumBuffer = new UnsafeBuffer(allocateDirectAligned(fileIoMaxLength, CACHE_LINE_LENGTH)); } return recordChecksumBuffer; } /** * 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, archiveDirChannel); CloseHelper.close(countedErrorHandler, catalog); if (ownsAeronClient) { CloseHelper.close(aeron); } else { CloseHelper.close(countedErrorHandler, controlSessionsCounter); CloseHelper.close(countedErrorHandler, recordingSessionCounter); CloseHelper.close(countedErrorHandler, replaySessionCounter); CloseHelper.close(countedErrorHandler, totalWriteBytesCounter); CloseHelper.close(countedErrorHandler, totalWriteTimeCounter); CloseHelper.close(countedErrorHandler, maxWriteTimeCounter); CloseHelper.close(countedErrorHandler, totalReadBytesCounter); CloseHelper.close(countedErrorHandler, totalReadTimeCounter); CloseHelper.close(countedErrorHandler, maxReadTimeCounter); closeDutyCycleCounters(conductorDutyCycleTracker); closeDutyCycleCounters(recorderDutyCycleTracker); closeDutyCycleCounters(replayerDutyCycleTracker); CloseHelper.close(errorCounter); } if (errorHandler instanceof AutoCloseable) { CloseHelper.close((AutoCloseable)errorHandler); } CloseHelper.close(markFile); } private void closeDutyCycleCounters(final DutyCycleTracker dutyCycleTracker) { if (dutyCycleTracker instanceof DutyCycleStallTracker) { final DutyCycleStallTracker dutyCycleStallTracker = (DutyCycleStallTracker)dutyCycleTracker; CloseHelper.close(countedErrorHandler, dutyCycleStallTracker.maxCycleTime()); CloseHelper.close(countedErrorHandler, dutyCycleStallTracker.cycleTimeThresholdExceededCount()); } } private void concludeArchiveId() { if (NULL_VALUE == archiveId) { archiveId = aeron.clientId(); } } /** * {@inheritDoc} */ public String toString() { return "Archive.Context" + "\n{" + "\n isConcluded=" + isConcluded() + "\n deleteArchiveOnStart=" + deleteArchiveOnStart + "\n ownsAeronClient=" + ownsAeronClient + "\n aeronDirectoryName='" + aeronDirectoryName + '\'' + "\n aeron=" + aeron + "\n archiveDir=" + archiveDir + "\n archiveDirectoryName='" + archiveDirectoryName + '\'' + "\n archiveDirChannel=" + archiveDirChannel + "\n archiveFileStore=" + archiveFileStore + "\n archiveId=" + archiveId + "\n catalog=" + catalog + "\n markFile=" + markFile + "\n archiveClientContext=" + archiveClientContext + "\n mediaDriverAgentInvoker=" + mediaDriverAgentInvoker + "\n controlChannel='" + controlChannel + '\'' + "\n controlStreamId=" + controlStreamId + "\n localControlChannel='" + localControlChannel + '\'' + "\n localControlStreamId=" + localControlStreamId + "\n controlTermBufferSparse=" + controlTermBufferSparse + "\n controlTermBufferLength=" + controlTermBufferLength + "\n controlMtuLength=" + controlMtuLength + "\n recordingEventsChannel='" + recordingEventsChannel + '\'' + "\n recordingEventsStreamId=" + recordingEventsStreamId + "\n recordingEventsEnabled=" + recordingEventsEnabled + "\n replicationChannel='" + replicationChannel + '\'' + "\n connectTimeoutNs=" + connectTimeoutNs + "\n replayLingerTimeoutNs=" + replayLingerTimeoutNs + "\n maxCatalogEntries=" + -1 + "\n catalogCapacity=" + catalogCapacity + "\n lowStorageSpaceThreshold=" + lowStorageSpaceThreshold + "\n segmentFileLength=" + segmentFileLength + "\n fileSyncLevel=" + fileSyncLevel + "\n catalogFileSyncLevel=" + catalogFileSyncLevel + "\n maxConcurrentRecordings=" + maxConcurrentRecordings + "\n maxConcurrentReplays=" + maxConcurrentReplays + "\n fileIoMaxLength=" + fileIoMaxLength + "\n threadingMode=" + threadingMode + "\n threadFactory=" + threadFactory + "\n abortLatch=" + abortLatch + "\n idleStrategySupplier=" + idleStrategySupplier + "\n replayerIdleStrategySupplier=" + replayerIdleStrategySupplier + "\n recorderIdleStrategySupplier=" + recorderIdleStrategySupplier + "\n epochClock=" + epochClock + "\n authenticatorSupplier=" + authenticatorSupplier + "\n controlSessionsCounter=" + controlSessionsCounter + "\n recordingSessionCounter=" + recordingSessionCounter + "\n replaySessionCounter=" + replaySessionCounter + "\n errorBufferLength=" + errorBufferLength + "\n errorHandler=" + errorHandler + "\n errorCounter=" + errorCounter + "\n countedErrorHandler=" + countedErrorHandler + "\n recordChecksum=" + recordChecksum + "\n replayChecksum=" + replayChecksum + "\n dataBuffer=" + dataBuffer + "\n replayBuffer=" + replayBuffer + "\n recordChecksumBuffer=" + recordChecksumBuffer + "\n conductorCycleThresholdNs=" + conductorCycleThresholdNs + "\n recorderCycleThresholdNs=" + recorderCycleThresholdNs + "\n replayerCycleThresholdNs=" + replayerCycleThresholdNs + "\n conductorDutyCycleTracker=" + conductorDutyCycleTracker + "\n recorderDutyCycleTracker=" + recorderDutyCycleTracker + "\n replayerDutyCycleTracker=" + replayerDutyCycleTracker + "\n totalWriteBytesCounter=" + totalWriteBytesCounter + "\n totalWriteTimeCounter=" + totalWriteTimeCounter + "\n maxWriteTimeCounter=" + maxWriteTimeCounter + "\n totalReadBytesCounter=" + totalReadBytesCounter + "\n totalReadTimeCounter=" + totalReadTimeCounter + "\n maxReadTimeCounter=" + maxReadTimeCounter + "\n}"; } } /** * The filename to be used for a segment file based on recording id and position the segment begins. * * @param recordingId to identify the recorded stream. * @param segmentBasePosition at which the segment file begins. * @return the filename to be used for a segment file based on recording id and position the segment begins. */ static String segmentFileName(final long recordingId, final long segmentBasePosition) { return recordingId + "-" + segmentBasePosition + Configuration.RECORDING_SEGMENT_SUFFIX; } /** * Get the {@link FileChannel} for the parent directory for the recordings and catalog, so it can be sync'ed * to storage when new files are created. * * @param directory which will store the files created by the archive. * @param fileSyncLevel to be applied for file updates, {@link Archive.Configuration#FILE_SYNC_LEVEL_PROP_NAME}. * @return the {@link FileChannel} for the parent directory for the recordings and catalog if fileSyncLevel * greater than zero otherwise null. */ static FileChannel channelForDirectorySync(final File directory, final int fileSyncLevel) { if (fileSyncLevel > 0) { try { return FileChannel.open(directory.toPath()); } catch (final IOException ignore) { } } return null; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy