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

io.aeron.archive.ArchiveMigration_2_3 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 org.agrona.*;
import org.agrona.collections.MutableInteger;
import org.agrona.concurrent.UnsafeBuffer;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;

import static io.aeron.archive.Archive.Configuration.CATALOG_FILE_NAME;
import static io.aeron.archive.Catalog.DESCRIPTOR_HEADER_LENGTH;
import static io.aeron.archive.Catalog.MAX_CATALOG_LENGTH;
import static io.aeron.archive.MigrationUtils.fullVersionString;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static java.nio.channels.FileChannel.MapMode.READ_WRITE;
import static java.nio.file.StandardOpenOption.*;
import static org.agrona.BitUtil.*;

class ArchiveMigration_2_3 implements ArchiveMigrationStep
{
    private static final int MINIMUM_VERSION = SemanticVersion.compose(3, 0, 0);

    /**
     * {@inheritDoc}
     */
    public int minimumVersion()
    {
        return MINIMUM_VERSION;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("try")
    public void migrate(
        final PrintStream stream,
        final ArchiveMarkFile markFile,
        final Catalog catalog,
        final File archiveDir)
    {
        final int version = minimumVersion();
        try (FileChannel ignore = MigrationUtils.createMigrationTimestampFile(
            archiveDir, markFile.decoder().version(), version))
        {
            final File newFile = new File(archiveDir, CATALOG_FILE_NAME + ".updated");
            IoUtil.deleteIfExists(newFile);
            final Path updatedCatalogFile = newFile.toPath();

            try (FileChannel channel = FileChannel.open(updatedCatalogFile, READ, WRITE, CREATE_NEW))
            {
                final MappedByteBuffer mappedByteBuffer = channel.map(READ_WRITE, 0, MAX_CATALOG_LENGTH);
                mappedByteBuffer.order(LITTLE_ENDIAN);
                final int offset;
                try
                {
                    offset = migrateCatalogFile(catalog, version, mappedByteBuffer);
                }
                finally
                {
                    BufferUtil.free(mappedByteBuffer);
                }

                channel.truncate(offset); // trim file to actual length used
            }

            catalog.close();

            final Path catalogFilePath = updatedCatalogFile.resolveSibling(CATALOG_FILE_NAME);
            Files.delete(catalogFilePath);
            Files.move(updatedCatalogFile, catalogFilePath);

            markFile.encoder().version(version);
        }
        catch (final IOException ex)
        {
            LangUtil.rethrowUnchecked(ex);
        }
    }

    /**
     * {@inheritDoc}
     */
    public String toString()
    {
        return "to " + fullVersionString(minimumVersion());
    }

    private int migrateCatalogFile(final Catalog catalog, final int version, final MappedByteBuffer mappedByteBuffer)
    {
        final UnsafeBuffer buffer = new UnsafeBuffer(mappedByteBuffer);

        final int alignment = CACHE_LINE_LENGTH;

        // Create CatalogHeader for version 3.0.0
        final int catalogHeaderLength =
            writeCatalogHeader(buffer, version, catalog.nextRecordingId(), alignment);

        final MutableInteger offset = new MutableInteger(catalogHeaderLength);

        catalog.forEach(
            (recordingDescriptorOffset, headerEncoder, headerDecoder, descriptorEncoder, descriptorDecoder) ->
            {
                final int strippedChannelLength = descriptorDecoder.strippedChannelLength();
                descriptorDecoder.skipStrippedChannel();

                final int originalChannelLength = descriptorDecoder.originalChannelLength();
                descriptorDecoder.skipOriginalChannel();

                final int sourceIdentityLength = descriptorDecoder.sourceIdentityLength();

                final int frameLength =
                    align(DESCRIPTOR_HEADER_LENGTH + 7 * SIZE_OF_LONG + 6 * SIZE_OF_INT +
                    SIZE_OF_INT + strippedChannelLength +
                    SIZE_OF_INT + originalChannelLength +
                    SIZE_OF_INT + sourceIdentityLength,
                    alignment);

                final DirectBuffer srcBuffer = headerDecoder.buffer();
                final int headerLength = headerDecoder.encodedLength();

                // Copy recording header
                int index = offset.get();
                buffer.putBytes(
                    index,
                    srcBuffer,
                    0,
                    headerLength);
                index += headerLength;

                // Correct length
                buffer.putInt(offset.get(), frameLength - headerLength, LITTLE_ENDIAN);

                // Copy recording descriptor
                buffer.putBytes(
                    index,
                    srcBuffer,
                    headerLength,
                    frameLength - headerLength);

                offset.addAndGet(frameLength);
            });

        return offset.get();
    }

    private int writeCatalogHeader(
        final UnsafeBuffer buffer, final int version, final long nextRecordingId, final int alignment)
    {
        final int catalogHeaderLength = 32;

        int index = 0;
        buffer.putInt(index, version, LITTLE_ENDIAN); // version
        index += SIZE_OF_INT;

        buffer.putInt(index, catalogHeaderLength, LITTLE_ENDIAN); // length
        index += SIZE_OF_INT;

        buffer.putLong(index, nextRecordingId, LITTLE_ENDIAN); // nextRecordingId
        index += SIZE_OF_LONG;

        buffer.putInt(index, alignment, LITTLE_ENDIAN); // alignment

        return catalogHeaderLength;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy