io.zeebe.logstreams.impl.snapshot.fs.FsSnapshotStorage Maven / Gradle / Ivy
/*
* 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