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

io.zeebe.logstreams.impl.snapshot.fs.FsSnapshotStorage Maven / Gradle / Ivy

There is a newer version: 0.16.4
Show newest version
/*
 * Copyright © 2017 camunda services GmbH ([email protected])
 *
 * 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
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.zeebe.logstreams.impl.snapshot.fs;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import io.zeebe.logstreams.impl.Loggers;
import io.zeebe.logstreams.spi.ReadableSnapshot;
import io.zeebe.logstreams.spi.SnapshotStorage;
import org.agrona.LangUtil;
import org.slf4j.Logger;

public class FsSnapshotStorage implements SnapshotStorage
{
    public static final Logger LOG = Loggers.LOGSTREAMS_LOGGER;

    protected final FsSnapshotStorageConfiguration cfg;

    public FsSnapshotStorage(FsSnapshotStorageConfiguration cfg)
    {
        this.cfg = cfg;
    }

    @Override
    public FsReadableSnapshot getLastSnapshot(String name)
    {
        final File rootFile = new File(cfg.getRootPath());
        final List snapshotFiles = Arrays.asList(rootFile.listFiles(file -> cfg.matchesSnapshotFileNamePattern(file, name)));

        FsReadableSnapshot snapshot = null;

        if (snapshotFiles.size() > 0)
        {
            snapshotFiles.sort((f1, f2) -> Long.compare(position(f1, name), position(f2, name)));

            final File snapshotFile = snapshotFiles.get(0);
            final long logPosition = position(snapshotFile, name);

            final String checksumFileName = cfg.checksumFileName(name, logPosition);
            final File checksumFile = new File(checksumFileName);

            if (checksumFile.exists())
            {
                snapshot = new FsReadableSnapshot(cfg, snapshotFile, checksumFile, logPosition);
            }
            else
            {
                // delete snapshot file since checksum does not exist anymore
                LOG.error("Delete snapshot {}, no checksum file exists.", snapshotFile.getAbsolutePath());

                snapshotFile.delete();
            }
        }

        return snapshot;
    }

    @Override
    public boolean purgeSnapshot(String name)
    {
        final File rootFile = new File(cfg.getRootPath());
        final List snapshotFiles = Arrays.asList(rootFile.listFiles(file -> cfg.matchesSnapshotFileNamePattern(file, name)));

        boolean deletionSuccessful = false;
        if (snapshotFiles.size() > 0)
        {
            for (File snapshotFile : snapshotFiles)
            {
                final long logPosition = position(snapshotFile, name);
                final String checksumFileName = cfg.checksumFileName(name, logPosition);
                final File checksumFile = new File(checksumFileName);

                checksumFile.delete();
                snapshotFile.delete();
            }
            deletionSuccessful = true;
        }
        return deletionSuccessful;
    }

    protected long position(File file, String snapshotName)
    {
        return cfg.getPositionOfSnapshotFile(file, snapshotName);
    }

    @Override
    public FsSnapshotWriter createSnapshot(String name, long logPosition) throws Exception
    {
        final FsReadableSnapshot lastSnapshot = getLastSnapshot(name);

        final String snapshotFileName = cfg.snapshotFileName(name, logPosition);
        final String checksumFileName = cfg.checksumFileName(name, logPosition);

        final File snapshotFile = new File(snapshotFileName);
        final File checksumFile = new File(checksumFileName);

        if (snapshotFile.exists())
        {
            throw new RuntimeException(String.format("Cannot write snapshot %s, file already exists.", snapshotFile.getAbsolutePath()));
        }

        if (checksumFile.exists())
        {
            throw new RuntimeException(String.format("Cannot write snapshot checksum %s, file already exists.", checksumFile.getAbsolutePath()));
        }

        try
        {
            checksumFile.createNewFile();
            snapshotFile.createNewFile();
        }
        catch (IOException e)
        {
            checksumFile.delete();
            snapshotFile.delete();
            LangUtil.rethrowUnchecked(e);
        }

        return new FsSnapshotWriter(cfg, snapshotFile, checksumFile, lastSnapshot);
    }

    @Override
    public List listSnapshots()
    {
        final File rootFile = new File(cfg.getRootPath());
        final File[] snapshotFiles = rootFile.listFiles(file -> cfg.matchesSnapshotFileNamePattern(file, ".+"));
        final ArrayList snapshots = new ArrayList<>();

        if (snapshotFiles != null)
        {
            snapshots.ensureCapacity(snapshotFiles.length);
            for (final File snapshotFile : snapshotFiles)
            {
                final String snapshotName = cfg.getSnapshotNameFromFileName(snapshotFile.getName());
                snapshots.add(getLastSnapshot(snapshotName));
            }
        }

        return snapshots;
    }

    @Override
    public FsTemporarySnapshotWriter createTemporarySnapshot(final String prefix, final String name, final long logPosition) throws Exception
    {
        final File destinationFile = new File(cfg.snapshotFileName(name, logPosition));
        final File checksumFile = new File(cfg.checksumFileName(name, logPosition));
        final File temporaryFile = File.createTempFile(prefix, null, new File(cfg.getRootPath()));

        temporaryFile.deleteOnExit();

        try
        {
            Files.createFile(destinationFile.toPath());

            try
            {
                Files.createFile(checksumFile.toPath());
            }
            catch (final IOException ex)
            {
                //noinspection ResultOfMethodCallIgnored
                destinationFile.delete();
                throw ex;
            }
        }
        catch (final Exception ex)
        {
            //noinspection ResultOfMethodCallIgnored
            temporaryFile.delete();
            throw ex;
        }

        return new FsTemporarySnapshotWriter(cfg, temporaryFile, checksumFile, destinationFile);
    }

    // TODO: is it enough to check only for the snapshot file? or also the sha1 file?
    @Override
    public boolean snapshotExists(String name, long logPosition)
    {
        return Files.exists(Paths.get(cfg.snapshotFileName(name, logPosition)));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy