blob: ef0f56a251827861b57405d3bc8b8382167e565f [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.storageengine.dataregion.compaction.execute.recover;
import org.apache.iotdb.commons.conf.IoTDBConstant;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
import org.apache.iotdb.db.storageengine.dataregion.compaction.execute.utils.CompactionUtils;
import org.apache.iotdb.db.storageengine.dataregion.compaction.execute.utils.log.CompactionLogAnalyzer;
import org.apache.iotdb.db.storageengine.dataregion.compaction.execute.utils.log.TsFileIdentifier;
import org.apache.iotdb.db.storageengine.dataregion.modification.ModificationFile;
import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileManager;
import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
import org.apache.commons.io.FileUtils;
import org.apache.tsfile.common.constant.TsFileConstant;
import org.apache.tsfile.utils.TsFileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** CompactionRecoverTask executes the recover process for all compaction tasks. */
public class CompactionRecoverTask {
private static final Logger LOGGER =
LoggerFactory.getLogger(IoTDBConstant.COMPACTION_LOGGER_NAME);
private final File compactionLogFile;
private final boolean isInnerSpace;
private final String fullStorageGroupName;
private final TsFileManager tsFileManager;
public CompactionRecoverTask(
String logicalStorageGroupName,
String virtualStorageGroupName,
TsFileManager tsFileManager,
File logFile,
boolean isInnerSpace) {
this.compactionLogFile = logFile;
this.isInnerSpace = isInnerSpace;
this.fullStorageGroupName = logicalStorageGroupName + "-" + virtualStorageGroupName;
this.tsFileManager = tsFileManager;
}
@SuppressWarnings({"squid:S3776", "squid:S6541"})
public void doCompaction() {
boolean recoverSuccess = true;
LOGGER.info(
"{} [Compaction][Recover] compaction log is {}", fullStorageGroupName, compactionLogFile);
try {
if (compactionLogFile.exists()) {
LOGGER.info(
"{} [Compaction][Recover] compaction log file {} exists, start to recover it",
fullStorageGroupName,
compactionLogFile);
CompactionLogAnalyzer logAnalyzer = new CompactionLogAnalyzer(compactionLogFile);
logAnalyzer.analyze();
List<TsFileIdentifier> sourceFileIdentifiers = logAnalyzer.getSourceFileInfos();
List<TsFileIdentifier> targetFileIdentifiers = logAnalyzer.getTargetFileInfos();
List<TsFileIdentifier> deletedTargetFileIdentifiers =
logAnalyzer.getDeletedTargetFileInfos();
// compaction log file is incomplete
if (targetFileIdentifiers.isEmpty() || sourceFileIdentifiers.isEmpty()) {
LOGGER.info(
"{} [Compaction][Recover] incomplete log file, abort recover", fullStorageGroupName);
return;
}
// check is all source files existed
boolean isAllSourcesFileExisted = true;
for (TsFileIdentifier sourceFileIdentifier : sourceFileIdentifiers) {
File sourceFile = sourceFileIdentifier.getFileFromDataDirs();
if (sourceFile == null) {
isAllSourcesFileExisted = false;
break;
}
}
if (isAllSourcesFileExisted) {
recoverSuccess =
handleWithAllSourceFilesExist(targetFileIdentifiers, sourceFileIdentifiers);
} else {
recoverSuccess =
handleWithSomeSourceFilesLost(
targetFileIdentifiers, deletedTargetFileIdentifiers, sourceFileIdentifiers);
}
}
} catch (IOException e) {
LOGGER.error("Recover compaction error", e);
} finally {
if (!recoverSuccess) {
LOGGER.error("{} [Compaction][Recover] Failed to recover compaction", fullStorageGroupName);
} else {
if (compactionLogFile.exists()) {
try {
LOGGER.info(
"{} [Compaction][Recover] Recover compaction successfully, delete log file {}",
fullStorageGroupName,
compactionLogFile);
FileUtils.delete(compactionLogFile);
} catch (IOException e) {
LOGGER.error(
"{} [Compaction][Recover] Exception occurs while deleting log file {}",
fullStorageGroupName,
compactionLogFile,
e);
}
}
}
}
}
/**
* All source files exist: (1) delete all the target files and tmp target files (2) delete
* compaction mods files.
*/
private boolean handleWithAllSourceFilesExist(
List<TsFileIdentifier> targetFileIdentifiers, List<TsFileIdentifier> sourceFileIdentifiers) {
LOGGER.info(
"{} [Compaction][Recover] all source files exists, delete all target files.",
fullStorageGroupName);
// remove tmp target files and target files
for (TsFileIdentifier targetFileIdentifier : targetFileIdentifiers) {
// xxx.inner or xxx.cross
File tmpTargetFile = targetFileIdentifier.getFileFromDataDirs();
// xxx.tsfile
File targetFile =
getFileFromDataDirs(
targetFileIdentifier
.getFilePath()
.replace(
isInnerSpace
? IoTDBConstant.INNER_COMPACTION_TMP_FILE_SUFFIX
: IoTDBConstant.CROSS_COMPACTION_TMP_FILE_SUFFIX,
TsFileConstant.TSFILE_SUFFIX));
TsFileResource targetResource = null;
if (tmpTargetFile != null) {
targetResource = new TsFileResource(tmpTargetFile);
} else if (targetFile != null) {
targetResource = new TsFileResource(targetFile);
}
if (targetResource != null && !targetResource.remove()) {
// failed to remove tmp target tsfile
// system should not carry out the subsequent compaction in case of data redundant
LOGGER.error(
"{} [Compaction][Recover] failed to remove target file {}",
fullStorageGroupName,
targetResource);
return false;
}
}
// delete compaction mods files
List<TsFileResource> sourceTsFileResourceList = new ArrayList<>();
for (TsFileIdentifier sourceFileIdentifier : sourceFileIdentifiers) {
sourceTsFileResourceList.add(new TsFileResource(sourceFileIdentifier.getFileFromDataDirs()));
}
try {
CompactionUtils.deleteCompactionModsFile(sourceTsFileResourceList, Collections.emptyList());
} catch (IOException e) {
LOGGER.error(
"{} [Compaction][Recover] Exception occurs while deleting compaction mods file",
fullStorageGroupName,
e);
return false;
}
return true;
}
/**
* Some source files lost: delete remaining source files, including: tsfile, resource file, mods
* file and compaction mods file.
*
* @throws IOException the io operations on file fails
*/
private boolean handleWithSomeSourceFilesLost(
List<TsFileIdentifier> targetFileIdentifiers,
List<TsFileIdentifier> deletedTargetFileIdentifiers,
List<TsFileIdentifier> sourceFileIdentifiers)
throws IOException {
// some source files have been deleted, while target file must exist and complete.
if (!checkIsTargetFilesComplete(targetFileIdentifiers, deletedTargetFileIdentifiers)) {
return false;
}
boolean handleSuccess = true;
for (TsFileIdentifier sourceFileIdentifier : sourceFileIdentifiers) {
if (!deleteFile(sourceFileIdentifier)) {
handleSuccess = false;
}
}
return handleSuccess;
}
/**
* This method find the File object of given filePath by searching it in every data directory. If
* the file is not found, it will return null.
*/
private File getFileFromDataDirs(String filePath) {
String[] dataDirs = IoTDBDescriptor.getInstance().getConfig().getLocalDataDirs();
for (String dataDir : dataDirs) {
File f = new File(dataDir, filePath);
if (f.exists()) {
return f;
}
}
return null;
}
private boolean checkIsTargetFilesComplete(
List<TsFileIdentifier> targetFileIdentifiers,
List<TsFileIdentifier> deletedTargetFileIdentifiers)
throws IOException {
for (TsFileIdentifier targetFileIdentifier : targetFileIdentifiers) {
targetFileIdentifier.setFilename(
targetFileIdentifier
.getFilename()
.replace(
isInnerSpace
? IoTDBConstant.INNER_COMPACTION_TMP_FILE_SUFFIX
: IoTDBConstant.CROSS_COMPACTION_TMP_FILE_SUFFIX,
TsFileConstant.TSFILE_SUFFIX));
boolean isTargetFileDeleted = deletedTargetFileIdentifiers.contains(targetFileIdentifier);
if (isTargetFileDeleted) {
if (!deleteFile(targetFileIdentifier)) {
return false;
}
continue;
}
// xxx.tsfile
File targetFile = getFileFromDataDirs(targetFileIdentifier.getFilePath());
if (targetFile == null
|| !TsFileUtils.isTsFileComplete(new TsFileResource(targetFile).getTsFile())) {
LOGGER.error(
"{} [Compaction][ExceptionHandler] target file {} is not complete, "
+ "and some source files is lost, do nothing.",
fullStorageGroupName,
targetFileIdentifier.getFilePath());
return false;
}
}
return true;
}
/**
* Delete tsfile and its corresponding files, including resource file, mods file and compaction
* mods file. Return true if the file is not existed or if the file is existed and has been
* deleted correctly. Otherwise, return false.
*/
private boolean deleteFile(TsFileIdentifier tsFileIdentifier) {
boolean success = true;
// delete tsfile
File file = tsFileIdentifier.getFileFromDataDirs();
if (!checkAndDeleteFile(file)) {
success = false;
}
// delete resource file
file = getFileFromDataDirs(tsFileIdentifier.getFilePath() + TsFileResource.RESOURCE_SUFFIX);
if (!checkAndDeleteFile(file)) {
success = false;
}
// delete mods file
file = getFileFromDataDirs(tsFileIdentifier.getFilePath() + ModificationFile.FILE_SUFFIX);
if (!checkAndDeleteFile(file)) {
success = false;
}
// delete compaction mods file
file =
getFileFromDataDirs(
tsFileIdentifier.getFilePath() + ModificationFile.COMPACTION_FILE_SUFFIX);
if (!checkAndDeleteFile(file)) {
success = false;
}
return success;
}
/**
* Return true if the file is not existed or if the file is existed and has been deleted
* correctly. Otherwise, return false.
*/
private boolean checkAndDeleteFile(File file) {
if (file == null || !file.exists()) {
return true;
}
try {
Files.delete(file.toPath());
} catch (IOException e) {
LOGGER.error(
"{} [Compaction][Recover] failed to remove file {}, exception: {}",
fullStorageGroupName,
file,
e);
return false;
}
return true;
}
}