package org.apache.cassandra.db.lifecycle;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.io.FSError;
import org.apache.cassandra.io.FSReadError;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.utils.NativeLibrary;
final class LogReplica implements AutoCloseable
{
private static final Logger logger = LoggerFactory.getLogger(LogReplica.class);
private final File file;
private int directoryDescriptor;
private final Map<String, String> errors = new HashMap<>();
static LogReplica create(File directory, String fileName)
{
int folderFD = NativeLibrary.tryOpenDirectory(directory.getPath());
if (folderFD == -1)
throw new FSReadError(new IOException(String.format("Invalid folder descriptor trying to create log replica %s", directory.getPath())), directory.getPath());
return new LogReplica(new File(fileName), folderFD);
}
static LogReplica open(File file)
{
int folderFD = NativeLibrary.tryOpenDirectory(file.getParentFile().getPath());
if (folderFD == -1)
throw new FSReadError(new IOException(String.format("Invalid folder descriptor trying to create log replica %s", file.getParentFile().getPath())), file.getParentFile().getPath());
return new LogReplica(file, folderFD);
}
LogReplica(File file, int directoryDescriptor)
{
this.file = file;
this.directoryDescriptor = directoryDescriptor;
}
File file()
{
return file;
}
List<String> readLines()
{
return FileUtils.readLines(file);
}
String getFileName()
{
return file.getName();
}
String getDirectory()
{
return file.getParent();
}
void append(LogRecord record)
{
boolean existed = exists();
try
{
FileUtils.appendAndSync(file, record.toString());
}
catch (FSError e)
{
logger.error("Failed to sync file {}", file, e);
FileUtils.handleFSErrorAndPropagate(e);
}
if (!existed)
syncDirectory();
}
void syncDirectory()
{
try
{
if (directoryDescriptor >= 0)
NativeLibrary.trySync(directoryDescriptor);
}
catch (FSError e)
{
logger.error("Failed to sync directory descriptor {}", directoryDescriptor, e);
FileUtils.handleFSErrorAndPropagate(e);
}
}
void delete()
{
LogTransaction.delete(file);
syncDirectory();
}
boolean exists()
{
return file.exists();
}
public void close()
{
if (directoryDescriptor >= 0)
{
NativeLibrary.tryCloseFD(directoryDescriptor);
directoryDescriptor = -1;
}
}
@Override
public String toString()
{
return String.format("[%s] ", file);
}
void setError(String line, String error)
{
errors.put(line, error);
}
void printContentsWithAnyErrors(StringBuilder str)
{
str.append(file.getPath());
str.append(System.lineSeparator());
FileUtils.readLines(file).forEach(line -> printLineWithAnyError(str, line));
}
private void printLineWithAnyError(StringBuilder str, String line)
{
str.append('\t');
str.append(line);
str.append(System.lineSeparator());
String error = errors.get(line);
if (error != null)
{
str.append("\t\t***");
str.append(error);
str.append(System.lineSeparator());
}
}
}