package org.apache.cassandra.io;
/*
*
* 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.
*
*/
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import org.apache.log4j.Logger;
import org.apache.commons.lang.StringUtils;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.utils.BloomFilter;
/**
* This class is built on top of the SequenceFile. It stores
* data on disk in sorted fashion. However the sorting is upto
* the application. This class expects keys to be handed to it
* in sorted order.
*
* A separate index file is maintained as well, containing the
* SSTable keys and the offset into the SSTable at which they are found.
* Every 1/indexInterval key is read into memory when the SSTable is opened.
*
* Finally, a bloom filter file is also kept for the keys in each SSTable.
*/
public abstract class SSTable
{
static final Logger logger = Logger.getLogger(SSTable.class);
public static final int FILES_ON_DISK = 3; // data, index, and bloom filter
protected String path;
protected String indexPath;
protected IPartitioner partitioner;
protected BloomFilter bf;
protected String columnFamilyName;
protected IndexSummary indexSummary;
public static final String TEMPFILE_MARKER = "tmp";
public SSTable(String filename, IPartitioner partitioner)
{
assert filename.endsWith("-Data.db");
columnFamilyName = parseColumnFamilyName(filename);
this.path = filename;
this.indexPath = indexFilename(path);
this.partitioner = partitioner;
}
protected static String parseColumnFamilyName(String filename)
{
return new File(filename).getName().split("-")[0];
}
public static String indexFilename(String dataFile)
{
String[] parts = dataFile.split("-");
parts[parts.length - 1] = "Index.db";
return StringUtils.join(parts, "-");
}
public String indexFilename()
{
return indexPath;
}
protected static String compactedFilename(String dataFile)
{
String[] parts = dataFile.split("-");
parts[parts.length - 1] = "Compacted";
return StringUtils.join(parts, "-");
}
/**
* We use a ReferenceQueue to manage deleting files that have been compacted
* and for which no more SSTable references exist. But this is not guaranteed
* to run for each such file because of the semantics of the JVM gc. So,
* we write a marker to `compactedFilename` when a file is compacted;
* if such a marker exists on startup, the file should be removed.
*
* @return true if the file was deleted
*/
public static boolean deleteIfCompacted(String dataFilename) throws IOException
{
if (new File(compactedFilename(dataFilename)).exists())
{
FileUtils.deleteWithConfirm(new File(dataFilename));
FileUtils.deleteWithConfirm(new File(SSTable.indexFilename(dataFilename)));
FileUtils.deleteWithConfirm(new File(SSTable.filterFilename(dataFilename)));
FileUtils.deleteWithConfirm(new File(SSTable.compactedFilename(dataFilename)));
logger.info("Deleted " + dataFilename);
return true;
}
return false;
}
protected String compactedFilename()
{
return compactedFilename(path);
}
protected static String filterFilename(String dataFile)
{
String[] parts = dataFile.split("-");
parts[parts.length - 1] = "Filter.db";
return StringUtils.join(parts, "-");
}
public String filterFilename()
{
return filterFilename(path);
}
public String getFilename()
{
return path;
}
/** @return full paths to all the files associated w/ this SSTable */
public List<String> getAllFilenames()
{
// TODO streaming relies on the -Data (getFilename) file to be last, this is clunky
return Arrays.asList(indexFilename(), filterFilename(), getFilename());
}
public String getColumnFamilyName()
{
return columnFamilyName;
}
public String getTableName()
{
return parseTableName(path);
}
public static String parseTableName(String filename)
{
return new File(filename).getParentFile().getName();
}
public static long getTotalBytes(Iterable<SSTableReader> sstables)
{
long sum = 0;
for (SSTableReader sstable : sstables)
{
sum += sstable.length();
}
return sum;
}
public long bytesOnDisk()
{
long bytes = 0;
for (String fname : getAllFilenames())
{
bytes += new File(fname).length();
}
return bytes;
}
@Override
public String toString()
{
return getClass().getName() + "(" +
"path='" + path + '\'' +
')';
}
public static class PositionSize
{
public final long position;
public final long size;
public PositionSize(long position, long size)
{
this.position = position;
this.size = size;
}
}
}