blob: 97771dcc0bb4f8da1c03794001f83730213cb335 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.iotdb.db.engine.snapshot;
import org.apache.iotdb.commons.conf.IoTDBConstant;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
import org.apache.iotdb.db.conf.directories.DirectoryManager;
import org.apache.iotdb.db.engine.StorageEngineV2;
import org.apache.iotdb.db.engine.storagegroup.DataRegion;
import org.apache.iotdb.db.exception.DiskSpaceInsufficientException;
import org.apache.iotdb.tsfile.utils.Pair;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
public class SnapshotLoader {
private Logger LOGGER = LoggerFactory.getLogger(SnapshotLoader.class);
private String storageGroupName;
private String snapshotPath;
private String dataRegionId;
private SnapshotLogAnalyzer logAnalyzer;
public SnapshotLoader(String snapshotPath, String storageGroupName, String dataRegionId) {
this.snapshotPath = snapshotPath;
this.storageGroupName = storageGroupName;
this.dataRegionId = dataRegionId;
}
private DataRegion loadSnapshot() {
try {
return new DataRegion(
IoTDBDescriptor.getInstance().getConfig().getSystemDir()
+ File.separator
+ "storage_groups"
+ File.separator
+ storageGroupName,
dataRegionId,
StorageEngineV2.getInstance().getFileFlushPolicy(),
storageGroupName);
} catch (Exception e) {
LOGGER.error("Exception occurs while load snapshot from {}", snapshotPath, e);
return null;
}
}
private File getSnapshotLogFile() {
File sourceDataDir = new File(snapshotPath);
if (sourceDataDir.exists()) {
File[] files =
sourceDataDir.listFiles((dir, name) -> name.equals(SnapshotLogger.SNAPSHOT_LOG_NAME));
if (files == null || files.length == 0) {
LOGGER.warn("Failed to find snapshot log file, cannot recover it");
} else if (files.length > 1) {
LOGGER.warn(
"Found more than one snapshot log file, cannot recover it. {}", Arrays.toString(files));
} else {
LOGGER.info("Reading snapshot log file {}", files[0]);
return files[0];
}
}
return null;
}
/**
* 1. Clear origin data 2. Move snapshot data to data dir 3. Load data region
*
* @return
*/
public DataRegion loadSnapshotForStateMachine() {
LOGGER.info(
"Loading snapshot for {}-{}, source directory is {}",
storageGroupName,
dataRegionId,
snapshotPath);
File snapshotLogFile = getSnapshotLogFile();
if (snapshotLogFile == null) {
return loadSnapshotWithoutLog();
} else {
return loadSnapshotWithLog(snapshotLogFile);
}
}
private DataRegion loadSnapshotWithoutLog() {
try {
try {
deleteAllFilesInDataDirs();
LOGGER.info("Remove all data files in original data dir");
} catch (IOException e) {
LOGGER.error("Failed to remove origin data files", e);
return null;
}
LOGGER.info("Moving snapshot file to data dirs");
try {
deleteAllFilesInDataDirs();
LOGGER.info("Remove all data files in original data dir");
} catch (IOException e) {
return null;
}
createLinksFromSnapshotDirToDataDirWithoutLog(new File(snapshotPath));
return loadSnapshot();
} catch (IOException | DiskSpaceInsufficientException e) {
LOGGER.error(
"Exception occurs when loading snapshot for {}-{}", storageGroupName, dataRegionId, e);
return null;
}
}
private DataRegion loadSnapshotWithLog(File logFile) {
try {
logAnalyzer = new SnapshotLogAnalyzer(logFile);
} catch (Exception e) {
LOGGER.error("Exception occurs when reading snapshot file", e);
return null;
}
try {
try {
deleteAllFilesInDataDirs();
LOGGER.info("Remove all data files in original data dir");
} catch (IOException e) {
LOGGER.error("Failed to remove origin data files", e);
return null;
}
createLinksFromSnapshotDirToDataDirWithLog();
return loadSnapshot();
} finally {
logAnalyzer.close();
}
}
private void deleteAllFilesInDataDirs() throws IOException {
String[] dataDirPaths = IoTDBDescriptor.getInstance().getConfig().getDataDirs();
// delete
List<File> timePartitions = new ArrayList<>();
for (String dataDirPath : dataDirPaths) {
File seqDataDirForThisRegion =
new File(
dataDirPath
+ File.separator
+ IoTDBConstant.SEQUENCE_FLODER_NAME
+ File.separator
+ storageGroupName
+ File.separator
+ dataRegionId);
if (seqDataDirForThisRegion.exists()) {
File[] files = seqDataDirForThisRegion.listFiles();
if (files != null) {
timePartitions.addAll(Arrays.asList(files));
}
}
File unseqDataDirForThisRegion =
new File(
dataDirPath
+ File.separator
+ IoTDBConstant.UNSEQUENCE_FLODER_NAME
+ File.separator
+ storageGroupName
+ File.separator
+ dataRegionId);
if (unseqDataDirForThisRegion.exists()) {
File[] files = unseqDataDirForThisRegion.listFiles();
if (files != null) {
timePartitions.addAll(Arrays.asList(files));
}
}
}
try {
for (File timePartition : timePartitions) {
FileUtils.forceDelete(timePartition);
}
} catch (IOException e) {
LOGGER.error(
"Exception occurs when deleting time partition directory for {}-{}",
storageGroupName,
dataRegionId,
e);
throw e;
}
}
private void createLinksFromSnapshotDirToDataDirWithoutLog(File sourceDir)
throws IOException, DiskSpaceInsufficientException {
File seqFileDir = new File(sourceDir, "sequence" + File.separator + storageGroupName);
File unseqFileDir = new File(sourceDir, "unsequence" + File.separator + storageGroupName);
if (!seqFileDir.exists() && !unseqFileDir.exists()) {
throw new IOException(
String.format(
"Cannot find %s or %s",
seqFileDir.getAbsolutePath(), unseqFileDir.getAbsolutePath()));
}
File[] seqRegionDirs = seqFileDir.listFiles();
if (seqRegionDirs != null && seqRegionDirs.length > 0) {
for (File seqRegionDir : seqRegionDirs) {
if (!seqRegionDir.isDirectory()) {
LOGGER.info("Skip {}, because it is not a directory", seqRegionDir);
continue;
}
File[] seqPartitionDirs = seqRegionDir.listFiles();
if (seqPartitionDirs != null && seqPartitionDirs.length > 0) {
for (File seqPartitionDir : seqPartitionDirs) {
String[] splitPath =
seqPartitionDir
.getAbsolutePath()
.split(File.separator.equals("\\") ? "\\\\" : File.separator);
long timePartition = Long.parseLong(splitPath[splitPath.length - 1]);
File[] files = seqPartitionDir.listFiles();
if (files != null && files.length > 0) {
Arrays.sort(files, Comparator.comparing(File::getName));
String currDir = DirectoryManager.getInstance().getNextFolderForSequenceFile();
for (File file : files) {
if (file.getName().endsWith(".tsfile")) {
currDir = DirectoryManager.getInstance().getNextFolderForSequenceFile();
}
File targetFile =
new File(
currDir,
storageGroupName
+ File.separator
+ dataRegionId
+ File.separator
+ timePartition
+ File.separator
+ file.getName());
if (!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs()) {
throw new IOException(
String.format("Failed to create dir %s", targetFile.getParent()));
}
try {
Files.createLink(targetFile.toPath(), file.toPath());
} catch (FileSystemException e) {
// cannot create link between two directories in different fs
// copy it
Files.copy(file.toPath(), targetFile.toPath());
}
}
}
}
}
}
}
File[] unseqRegionDirs = unseqFileDir.listFiles();
if (unseqRegionDirs != null && unseqRegionDirs.length > 0) {
for (File unseqRegionDir : unseqRegionDirs) {
if (!unseqRegionDir.isDirectory()) {
LOGGER.info("Skip {}, because it is not a directory", unseqRegionDir);
continue;
}
File[] unseqPartitionDirs = unseqRegionDir.listFiles();
if (unseqPartitionDirs != null && unseqPartitionDirs.length > 0) {
for (File unseqPartitionDir : unseqPartitionDirs) {
String[] splitPath =
unseqPartitionDir
.getAbsolutePath()
.split(File.separator.equals("\\") ? "\\\\" : File.separator);
long timePartition = Long.parseLong(splitPath[splitPath.length - 1]);
File[] files = unseqPartitionDir.listFiles();
if (files != null && files.length > 0) {
Arrays.sort(files, Comparator.comparing(File::getName));
String currDir = DirectoryManager.getInstance().getNextFolderForUnSequenceFile();
for (File file : files) {
if (file.getName().endsWith(".tsfile")) {
currDir = DirectoryManager.getInstance().getNextFolderForUnSequenceFile();
}
File targetFile =
new File(
currDir,
storageGroupName
+ File.separator
+ dataRegionId
+ File.separator
+ timePartition
+ File.separator
+ file.getName());
if (!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs()) {
throw new IOException(
String.format("Failed to create dir %s", targetFile.getParent()));
}
try {
Files.createLink(targetFile.toPath(), file.toPath());
} catch (FileSystemException e) {
Files.copy(file.toPath(), targetFile.toPath());
}
}
}
}
}
}
}
}
private void createLinksFromSnapshotDirToDataDirWithLog() {
while (logAnalyzer.hasNext()) {
Pair<String, String> filesPath = logAnalyzer.getNextPairs();
File sourceFile = new File(filesPath.left);
File linkedFile = new File(filesPath.right);
if (!linkedFile.exists()) {
LOGGER.warn("Snapshot file {} does not exist, skip it", linkedFile);
continue;
}
if (!sourceFile.getParentFile().exists() && !sourceFile.getParentFile().mkdirs()) {
LOGGER.error("Failed to create folder {}", sourceFile.getParentFile());
continue;
}
try {
Files.createLink(sourceFile.toPath(), linkedFile.toPath());
} catch (IOException e) {
LOGGER.error("Failed to create link from {} to {}", linkedFile, sourceFile, e);
}
}
}
public List<File> getSnapshotFileInfo() throws IOException {
File snapshotLogFile = getSnapshotLogFile();
if (snapshotLogFile == null) {
return getSnapshotFileWithoutLog();
} else {
return getSnapshotFileWithLog(snapshotLogFile);
}
}
private List<File> getSnapshotFileWithLog(File logFile) throws IOException {
SnapshotLogAnalyzer analyzer = new SnapshotLogAnalyzer(logFile);
try {
List<File> fileList = new LinkedList<>();
while (analyzer.hasNext()) {
fileList.add(new File(analyzer.getNextPairs().right));
}
return fileList;
} finally {
analyzer.close();
}
}
private List<File> getSnapshotFileWithoutLog() {
return new LinkedList<>(
Arrays.asList(
Objects.requireNonNull(
new File(snapshotPath)
.listFiles((dir, name) -> SnapshotFileSet.isDataFile(new File(dir, name))))));
}
}