consolidate partition behavior in IPartitioner, so creating a new partitioner should be only a matter of implementing that interface. all the external switch statements on PartitionerType have been folded into that.

SSTable is now the only part of the code that cares about the distinction between a 'raw' key and a 'decorated' key.  variables in that class have been named clientKey or decoratedKey to show which is which.  others don't care either because they only deal with decorated keys (SequenceFile, FileStruct) or only with client keys (everyone else).

patch by jbellis; reviewed by Jun Rao for #58

git-svn-id: https://svn.apache.org/repos/asf/incubator/cassandra/trunk@762848 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jonathan Ellis 2009-04-07 16:55:30 +00:00
parent 21f4bd568b
commit 40fa7d4a66
16 changed files with 217 additions and 779 deletions

View File

@ -20,7 +20,6 @@ package org.apache.cassandra.db;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@ -46,7 +45,6 @@ import org.apache.cassandra.io.IndexHelper;
import org.apache.cassandra.io.SSTable;
import org.apache.cassandra.io.SequenceFile;
import org.apache.cassandra.net.EndPoint;
import org.apache.cassandra.service.PartitionerType;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.BloomFilter;
import org.apache.cassandra.utils.FileUtils;
@ -445,15 +443,14 @@ public class ColumnFamilyStore
*/
List<ColumnFamily> getColumnFamilies(String key, String columnFamilyColumn, IFilter filter) throws IOException
{
List<ColumnFamily> columnFamilies1 = new ArrayList<ColumnFamily>();
List<ColumnFamily> columnFamilies = new ArrayList<ColumnFamily>();
/* Get the ColumnFamily from Memtable */
getColumnFamilyFromCurrentMemtable(key, columnFamilyColumn, filter, columnFamilies1);
if (columnFamilies1.size() == 0 || !filter.isDone())
getColumnFamilyFromCurrentMemtable(key, columnFamilyColumn, filter, columnFamilies);
if (columnFamilies.size() == 0 || !filter.isDone())
{
/* Check if MemtableManager has any historical information */
MemtableManager.instance().getColumnFamily(key, columnFamily_, columnFamilyColumn, filter, columnFamilies1);
MemtableManager.instance().getColumnFamily(key, columnFamily_, columnFamilyColumn, filter, columnFamilies);
}
List<ColumnFamily> columnFamilies = columnFamilies1;
if (columnFamilies.size() == 0 || !filter.isDone())
{
long start = System.currentTimeMillis();
@ -1195,39 +1192,10 @@ public class ColumnFamilyStore
+ totalBytesWritten + " Total keys read ..." + totalkeysRead);
return result;
}
private void doWrite(SSTable ssTable, String key, DataOutputBuffer bufOut) throws IOException
private void doFill(BloomFilter bf, String decoratedKey)
{
PartitionerType pType = StorageService.getPartitionerType();
switch ( pType )
{
case OPHF:
ssTable.append(key, bufOut);
break;
default:
String[] peices = key.split(":");
key = peices[1];
BigInteger hash = new BigInteger(peices[0]);
ssTable.append(key, hash, bufOut);
break;
}
}
private void doFill(BloomFilter bf, String key)
{
PartitionerType pType = StorageService.getPartitionerType();
switch ( pType )
{
case OPHF:
bf.fill(key);
break;
default:
String[] peices = key.split(":");
bf.fill(peices[1]);
break;
}
bf.fill(StorageService.getPartitioner().undecorateKey(decoratedKey));
}
/*
@ -1348,11 +1316,10 @@ public class ColumnFamilyStore
if ( ssTable == null )
{
PartitionerType pType = StorageService.getPartitionerType();
ssTable = new SSTable(compactionFileLocation, mergedFileName, pType);
ssTable = new SSTable(compactionFileLocation, mergedFileName);
}
doWrite(ssTable, lastkey, bufOut);
ssTable.append(lastkey, bufOut);
/* Fill the bloom filter with the key */
doFill(compactedBloomFilter, lastkey);
totalkeysWritten++;

View File

@ -19,22 +19,19 @@
package org.apache.cassandra.db;
import java.io.IOException;
import java.math.BigInteger;
import org.apache.cassandra.continuations.Suspendable;
import org.apache.cassandra.io.DataInputBuffer;
import org.apache.cassandra.io.DataOutputBuffer;
import org.apache.cassandra.io.IFileReader;
import org.apache.cassandra.io.SSTable;
import org.apache.cassandra.io.SequenceFile;
import org.apache.cassandra.service.PartitionerType;
import org.apache.cassandra.service.StorageService;
public class FileStruct implements Comparable<FileStruct>
{
IFileReader reader_;
String key_;
String key_; // decorated!
DataInputBuffer bufIn_;
DataOutputBuffer bufOut_;
@ -54,22 +51,7 @@ public class FileStruct implements Comparable<FileStruct>
public String getKey()
{
String key = key_;
if ( !key.equals(SSTable.blockIndexKey_) )
{
PartitionerType pType = StorageService.getPartitionerType();
switch ( pType )
{
case OPHF:
break;
default:
String[] peices = key.split(":");
key = peices[1];
break;
}
}
return key;
return key_;
}
public DataOutputBuffer getBuffer()
@ -116,23 +98,7 @@ public class FileStruct implements Comparable<FileStruct>
public int compareTo(FileStruct f)
{
int value = 0;
PartitionerType pType = StorageService.getPartitionerType();
switch( pType )
{
case OPHF:
value = key_.compareTo(f.key_);
break;
default:
String lhs = key_.split(":")[0];
BigInteger b = new BigInteger(lhs);
String rhs = f.key_.split(":")[0];
BigInteger b2 = new BigInteger(rhs);
value = b.compareTo(b2);
break;
}
return value;
return StorageService.getPartitioner().getDecoratedKeyComparator().compare(key_, f.key_);
}
public void close() throws IOException

View File

@ -26,6 +26,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Comparator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@ -43,10 +45,10 @@ import org.apache.cassandra.concurrent.ThreadFactoryImpl;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.io.DataOutputBuffer;
import org.apache.cassandra.io.SSTable;
import org.apache.cassandra.service.PartitionerType;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.BloomFilter;
import org.apache.cassandra.utils.LogUtil;
import org.apache.cassandra.service.IPartitioner;
import org.apache.cassandra.service.StorageService;
/**
* Author : Avinash Lakshman ( alakshman@facebook.com) & Prashant Malik ( pmalik@facebook.com )
@ -393,57 +395,25 @@ public class Memtable implements MemtableMBean, Comparable<Memtable>
return;
}
PartitionerType pType = StorageService.getPartitionerType();
String directory = DatabaseDescriptor.getDataFileLocation();
String filename = cfStore.getNextFileName();
SSTable ssTable = new SSTable(directory, filename, pType);
switch (pType)
{
case OPHF:
flushForOrderPreservingPartitioner(ssTable, cfStore, cLogCtx);
break;
SSTable ssTable = new SSTable(directory, filename);
default:
flushForRandomPartitioner(ssTable, cfStore, cLogCtx);
break;
}
}
private void flushForRandomPartitioner(SSTable ssTable, ColumnFamilyStore cfStore, CommitLog.CommitLogContext cLogCtx) throws IOException
{
/* List of primary keys in sorted order */
List<PrimaryKey> pKeys = PrimaryKey.create( columnFamilies_.keySet() );
DataOutputBuffer buffer = new DataOutputBuffer();
/* Use this BloomFilter to decide if a key exists in a SSTable */
BloomFilter bf = new BloomFilter(pKeys.size(), 15);
for ( PrimaryKey pKey : pKeys )
// sort keys in the order they would be in when decorated
final IPartitioner partitioner = StorageService.getPartitioner();
final Comparator<String> dc = partitioner.getDecoratedKeyComparator();
ArrayList<String> orderedKeys = new ArrayList<String>(columnFamilies_.keySet());
Collections.sort(orderedKeys, new Comparator<String>()
{
buffer.reset();
ColumnFamily columnFamily = columnFamilies_.get(pKey.key());
if ( columnFamily != null )
public int compare(String o1, String o2)
{
/* serialize the cf with column indexes */
ColumnFamily.serializerWithIndexes().serialize( columnFamily, buffer );
/* Now write the key and value to disk */
ssTable.append(pKey.key(), pKey.hash(), buffer);
bf.fill(pKey.key());
columnFamily.clear();
return dc.compare(partitioner.decorateKey(o1), partitioner.decorateKey(o2));
}
}
ssTable.close(bf);
cfStore.onMemtableFlush(cLogCtx);
cfStore.storeLocation( ssTable.getDataFileLocation(), bf );
buffer.close();
}
private void flushForOrderPreservingPartitioner(SSTable ssTable, ColumnFamilyStore cfStore, CommitLog.CommitLogContext cLogCtx) throws IOException
{
List<String> keys = new ArrayList<String>( columnFamilies_.keySet() );
Collections.sort(keys);
});
DataOutputBuffer buffer = new DataOutputBuffer();
/* Use this BloomFilter to decide if a key exists in a SSTable */
BloomFilter bf = new BloomFilter(keys.size(), 15);
for ( String key : keys )
BloomFilter bf = new BloomFilter(columnFamilies_.size(), 15);
for (String key : orderedKeys)
{
buffer.reset();
ColumnFamily columnFamily = columnFamilies_.get(key);
@ -452,7 +422,7 @@ public class Memtable implements MemtableMBean, Comparable<Memtable>
/* serialize the cf with column indexes */
ColumnFamily.serializerWithIndexes().serialize( columnFamily, buffer );
/* Now write the key and value to disk */
ssTable.append(key, buffer);
ssTable.append(partitioner.decorateKey(key), buffer);
bf.fill(key);
columnFamily.clear();
}
@ -461,5 +431,8 @@ public class Memtable implements MemtableMBean, Comparable<Memtable>
cfStore.onMemtableFlush(cLogCtx);
cfStore.storeLocation( ssTable.getDataFileLocation(), bf );
buffer.close();
columnFamilies_.clear();
}
}

View File

@ -1,125 +0,0 @@
/**
* 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.cassandra.db;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.cassandra.io.ICompactSerializer;
import org.apache.cassandra.service.PartitionerType;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.FBUtilities;
public class PrimaryKey implements Comparable<PrimaryKey>
{
public static List<PrimaryKey> create(Set<String> keys)
{
List<PrimaryKey> list = new ArrayList<PrimaryKey>();
for ( String key : keys )
{
list.add( new PrimaryKey(key) );
}
Collections.sort(list);
return list;
}
/* MD5 hash of the key_ */
private BigInteger hash_;
/* Key used by the application */
private String key_;
PrimaryKey(String key)
{
PartitionerType pType = StorageService.getPartitionerType();
switch (pType)
{
case RANDOM:
hash_ = FBUtilities.hash(key);
break;
case OPHF:
break;
default:
hash_ = hash_ = FBUtilities.hash(key);
break;
}
key_ = key;
}
PrimaryKey(String key, BigInteger hash)
{
hash_ = hash;
key_ = key;
}
public String key()
{
return key_;
}
public BigInteger hash()
{
return hash_;
}
/**
* This performs semantic comparison of Primary Keys.
* If the partition algorithm chosen is "Random" then
* the hash of the key is used for comparison. If it
* is an OPHF then the key is used.
*
* @param rhs primary against which we wish to compare.
* @return a negative integer, zero, or a positive integer as this object
* is less than, equal to, or greater than the specified object.
*/
public int compareTo(PrimaryKey rhs)
{
int value = 0;
PartitionerType pType = StorageService.getPartitionerType();
switch (pType)
{
case RANDOM:
value = hash_.compareTo(rhs.hash_);
break;
case OPHF:
value = key_.compareTo(rhs.key_);
break;
default:
value = hash_.compareTo(rhs.hash_);
break;
}
return value;
}
@Override
public String toString()
{
return (hash_ != null) ? (key_ + ":" + hash_) : key_;
}
}

View File

@ -1,178 +0,0 @@
/**
* 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.cassandra.db;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import org.apache.log4j.Logger;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.continuations.Suspendable;
import org.apache.cassandra.db.ColumnFamilyNotDefinedException;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.FileStruct;
import org.apache.cassandra.db.IScanner;
import org.apache.cassandra.db.Row;
import org.apache.cassandra.db.Table;
import org.apache.cassandra.io.DataInputBuffer;
import org.apache.cassandra.io.SSTable;
import org.apache.cassandra.io.SequenceFile;
import org.apache.cassandra.net.EndPoint;
import org.apache.cassandra.net.IAsyncResult;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.LogUtil;
/**
* This class is used to scan through all the keys in disk
* in Iterator style. Usage is as follows:
* SequentialScanner scanner = new SequentialScanner("table");
*
* while ( scanner.hasNext() )
* {
* Row row = scanner.next();
* // Do something with the row
* }
*
* @author alakshman
*
*/
public class SequentialScanner implements IScanner<Row>
{
private static Logger logger_ = Logger.getLogger( SequentialScanner.class );
private final static int bufSize_ = 1024*1024;
/* Table over which we want to perform a sequential scan. */
private String table_;
private Queue<FileStruct> fsQueue_ = new PriorityQueue<FileStruct>();
public SequentialScanner(String table) throws IOException
{
table_ = table;
List<String> allFiles = Table.open(table_).getAllSSTablesOnDisk();
for (String file : allFiles)
{
FileStruct fs = new FileStruct(file, SequentialScanner.bufSize_);
fsQueue_.add(fs);
}
}
/**
* Determines if there is anything more to be
* scanned.
* @return true if more elements are remanining
* else false.
*/
public boolean hasNext() throws IOException
{
boolean hasNext = ( fsQueue_.size() > 0 ) ? true : false;
return hasNext;
}
/**
* Returns the next row associated with the smallest key
* on disk.
*
* @return row of the next smallest key on disk.
*/
public Row next() throws IOException
{
if ( fsQueue_.size() == 0 )
throw new IllegalStateException("Nothing in the stream to scan.");
Row row = null;
FileStruct fs = fsQueue_.poll();
// Process the key only if it is in the primary range and not a block index.
if ( StorageService.instance().isPrimary(fs.getKey()) && !fs.getKey().equals(SSTable.blockIndexKey_) )
{
row = Table.open(table_).get(fs.getKey());
}
doCorrections(fs.getKey());
long bytesRead = fs.advance();
if ( bytesRead != -1L )
fsQueue_.add(fs);
return row;
}
/**
* This method advances the pointer in the file struct
* in the even the same key occurs in multiple files.
*
* @param key key we are interested in.
* @throws IOException
*/
private void doCorrections(String key) throws IOException
{
List<FileStruct> lfs = new ArrayList<FileStruct>();
Iterator<FileStruct> it = fsQueue_.iterator();
while ( it.hasNext() )
{
FileStruct fs = it.next();
/*
* We encountered a key that is greater
* than the key we are currently serving
* so scram.
*/
if ( fs.getKey().compareTo(key) != 0 )
{
break;
}
else
{
lfs.add(fs);
}
}
for ( FileStruct fs : lfs )
{
/* discard duplicate entries. */
fsQueue_.poll();
long bytesRead = fs.advance();
if ( bytesRead != -1L )
{
fsQueue_.add(fs);
}
}
}
public void close() throws IOException
{
if ( fsQueue_.size() > 0 )
{
for ( int i = 0; i < fsQueue_.size(); ++i )
{
FileStruct fs = fsQueue_.poll();
fs.close();
}
}
}
public void fetch(String key, String cf) throws IOException, ColumnFamilyNotDefinedException
{
throw new UnsupportedOperationException("This operation does not make sense in the SequentialScanner");
}
}

View File

@ -18,11 +18,8 @@
package org.apache.cassandra.io;
import java.io.File;
import java.io.IOException;
import org.apache.cassandra.db.PrimaryKey;
/**
* An interface for writing into the SequenceFile abstraction.

View File

@ -19,21 +19,19 @@
package org.apache.cassandra.io;
import java.io.*;
import java.math.BigInteger;
import java.util.*;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.service.PartitionerType;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.service.IPartitioner;
import org.apache.cassandra.utils.BasicUtilities;
import org.apache.cassandra.utils.BloomFilter;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.FileUtils;
import org.apache.cassandra.utils.LogUtil;
import org.apache.log4j.Logger;
import org.apache.cassandra.db.RowMutation;
import org.apache.log4j.Logger;
/**
* This class is built on top of the SequenceFile. It stores
* data on disk in sorted fashion. However the sorting is upto
@ -134,37 +132,6 @@ public class SSTable
}
}
/**
* This compares two strings and does it in reverse
* order.
*
* @author alakshman
*
*/
private static class OrderPreservingPartitionerComparator implements Comparator<String>
{
public int compare(String c1, String c2)
{
return c2.compareTo(c1);
}
}
/**
* This class compares two BigInteger's passes in
* as strings and does so in reverse order.
* @author alakshman
*
*/
private static class RandomPartitionerComparator implements Comparator<String>
{
public int compare(String c1, String c2)
{
BigInteger b1 = new BigInteger(c1);
BigInteger b2 = new BigInteger(c2);
return b2.compareTo(b1);
}
}
/**
* This is a simple container for the index Key and its corresponding position
* in the data file. Binary search is performed on a list of these objects
@ -172,20 +139,25 @@ public class SSTable
*/
public static class KeyPositionInfo implements Comparable<KeyPositionInfo>
{
private String key_;
private final String decoratedKey;
private long position_;
public KeyPositionInfo(String key)
public KeyPositionInfo(String decoratedKey)
{
key_ = key;
this.decoratedKey = decoratedKey;
}
public KeyPositionInfo(String key, long position)
public KeyPositionInfo(String decoratedKey, long position)
{
this(key);
this(decoratedKey);
position_ = position;
}
public String key()
{
return decoratedKey;
}
public long position()
{
return position_;
@ -193,25 +165,13 @@ public class SSTable
public int compareTo(KeyPositionInfo kPosInfo)
{
int value;
PartitionerType pType = StorageService.getPartitionerType();
switch( pType )
{
case OPHF:
value = key_.compareTo(kPosInfo.key_);
break;
default:
BigInteger b = new BigInteger(key_);
value = b.compareTo( new BigInteger(kPosInfo.key_) );
break;
}
return value;
IPartitioner p = StorageService.getPartitioner();
return p.getDecoratedKeyComparator().compare(decoratedKey, kPosInfo.decoratedKey);
}
public String toString()
{
return key_ + ":" + position_;
return decoratedKey + ":" + position_;
}
}
@ -296,7 +256,7 @@ public class SSTable
List<String> indexedKeys = new ArrayList<String>();
for ( KeyPositionInfo keyPositionInfo : keyPositionInfos )
{
indexedKeys.add(keyPositionInfo.key_);
indexedKeys.add(keyPositionInfo.decoratedKey);
}
Collections.sort(indexedKeys);
@ -353,13 +313,13 @@ public class SSTable
* Determines if the given key is in the specified file. If the
* key is not present then we skip processing this file.
*/
public static boolean isKeyInFile(String key, String filename)
public static boolean isKeyInFile(String clientKey, String filename)
{
boolean bVal = false;
BloomFilter bf = bfs_.get(filename);
if ( bf != null )
{
bVal = bf.isPresent(key);
bVal = bf.isPresent(clientKey);
}
return bVal;
}
@ -392,48 +352,12 @@ public class SSTable
public SSTable(String directory, String filename) throws IOException
{
dataFile_ = directory + System.getProperty("file.separator") + filename + "-Data.db";
blockIndex_ = new TreeMap<String, BlockMetadata>(Collections.reverseOrder());
blockIndexes_ = new ArrayList<SortedMap<String, BlockMetadata>>();
// dataWriter_ = SequenceFile.writer(dataFile_);
dataWriter_ = SequenceFile.bufferedWriter(dataFile_, 4*1024*1024);
SSTable.positionAfterFirstBlockIndex_ = dataWriter_.getCurrentPosition();
}
private void initBlockIndex()
{
initBlockIndex(StorageService.getPartitionerType());
}
private void initBlockIndex(PartitionerType pType)
{
switch ( pType )
{
case OPHF:
blockIndex_ = new TreeMap<String, BlockMetadata>( new SSTable.OrderPreservingPartitionerComparator() );
break;
default:
blockIndex_ = new TreeMap<String, BlockMetadata>( new SSTable.RandomPartitionerComparator() );
break;
}
}
/**
* This ctor is used for DB writes into the SSTable. Use this
* version to write to the SSTable.
*/
public SSTable(String directory, String filename, PartitionerType pType) throws IOException
{
dataFile_ = directory + System.getProperty("file.separator") + filename + "-Data.db";
// dataWriter_ = SequenceFile.writer(dataFile_);
dataWriter_ = SequenceFile.bufferedWriter(dataFile_, 4*1024*1024);
// dataWriter_ = SequenceFile.chksumWriter(dataFile_, 4*1024*1024);
SSTable.positionAfterFirstBlockIndex_ = dataWriter_.getCurrentPosition();
/* set up the block index based on partition type */
initBlockIndex(pType);
blockIndex_ = new TreeMap<String, BlockMetadata>(StorageService.getPartitioner().getReverseDecoratedKeyComparator());
blockIndexes_ = new ArrayList<SortedMap<String, BlockMetadata>>();
}
dataWriter_ = SequenceFile.bufferedWriter(dataFile_, 4*1024*1024);
SSTable.positionAfterFirstBlockIndex_ = dataWriter_.getCurrentPosition();
}
private void loadBloomFilter(IFileReader indexReader, long size) throws IOException
{
/* read the position of the bloom filter */
@ -450,8 +374,8 @@ public class SSTable
indexReader.next(bufOut);
bufOut.close();
bufIn.reset(bufOut.getData(), bufOut.getLength());
String key = bufIn.readUTF();
if ( key.equals(SequenceFile.marker_) )
String clientKey = bufIn.readUTF();
if ( clientKey.equals(SequenceFile.marker_) )
{
/*
* We are now reading the serialized Bloom Filter. We read
@ -587,23 +511,23 @@ public class SSTable
/*
* Seeks to the specified key on disk.
*/
public void touch(String key, boolean fData) throws IOException
public void touch(final String clientKey, boolean fData) throws IOException
{
if ( touchCache_.containsKey(key) )
if (touchCache_.containsKey(dataFile_ + ":" + clientKey))
return;
IFileReader dataReader = SequenceFile.reader(dataFile_);
try
{
/* Morph the key */
key = morphKey(key);
Coordinate fileCoordinate = getCoordinates(key, dataReader);
String decoratedKey = StorageService.getPartitioner().decorateKey(clientKey);
Coordinate fileCoordinate = getCoordinates(decoratedKey, dataReader);
/* Get offset of key from block Index */
dataReader.seek(fileCoordinate.end_);
BlockMetadata blockMetadata = dataReader.getBlockMetadata(key);
BlockMetadata blockMetadata = dataReader.getBlockMetadata(decoratedKey);
if ( blockMetadata.position_ != -1L )
{
touchCache_.put(dataFile_ + ":" + key, blockMetadata.position_);
touchCache_.put(dataFile_ + ":" + clientKey, blockMetadata.position_);
}
if ( fData )
@ -626,67 +550,34 @@ public class SSTable
}
}
private long beforeAppend(String key) throws IOException
private long beforeAppend(String decoratedKey) throws IOException
{
if(key == null )
if (decoratedKey == null )
throw new IOException("Keys must not be null.");
if ( lastWrittenKey_ != null && key.compareTo(lastWrittenKey_) <= 0 )
Comparator<String> c = StorageService.getPartitioner().getDecoratedKeyComparator();
if ( lastWrittenKey_ != null && c.compare(lastWrittenKey_, decoratedKey) > 0 )
{
logger_.info("Last written key : " + lastWrittenKey_);
logger_.info("Current key : " + key);
logger_.info("Current key : " + decoratedKey);
logger_.info("Writing into file " + dataFile_);
throw new IOException("Keys must be written in ascending order.");
}
long currentPosition = (lastWrittenKey_ == null) ? SSTable.positionAfterFirstBlockIndex_ : dataWriter_.getCurrentPosition();
return currentPosition;
}
private long beforeAppend(BigInteger hash) throws IOException
{
if(hash == null )
throw new IOException("Keys must not be null.");
if ( lastWrittenKey_ != null )
{
BigInteger previousKey = new BigInteger(lastWrittenKey_);
if ( hash.compareTo(previousKey) <= 0 )
{
logger_.info("Last written key : " + previousKey);
logger_.info("Current key : " + hash);
logger_.info("Writing into file " + dataFile_);
throw new IOException("Keys must be written in ascending order.");
}
}
long currentPosition = (lastWrittenKey_ == null) ? SSTable.positionAfterFirstBlockIndex_ : dataWriter_.getCurrentPosition();
return currentPosition;
return (lastWrittenKey_ == null) ? SSTable.positionAfterFirstBlockIndex_ : dataWriter_.getCurrentPosition();
}
private void afterAppend(String key, long position, long size)
private void afterAppend(String decoratedKey, long position, long size) throws IOException
{
++indexKeysWritten_;
lastWrittenKey_ = key;
blockIndex_.put(key, new BlockMetadata(position, size));
lastWrittenKey_ = decoratedKey;
blockIndex_.put(decoratedKey, new BlockMetadata(position, size));
if ( indexKeysWritten_ == indexInterval_ )
{
blockIndexes_.add(blockIndex_);
blockIndex_ = new TreeMap<String, BlockMetadata>(Collections.reverseOrder());
blockIndex_ = new TreeMap<String, BlockMetadata>(StorageService.getPartitioner().getReverseDecoratedKeyComparator());
indexKeysWritten_ = 0;
}
}
private void afterAppend(BigInteger hash, long position, long size)
{
++indexKeysWritten_;
String key = hash.toString();
lastWrittenKey_ = key;
blockIndex_.put(key, new BlockMetadata(position, size));
if ( indexKeysWritten_ == indexInterval_ )
{
blockIndexes_.add(blockIndex_);
initBlockIndex();
indexKeysWritten_ = 0;
}
}
/**
* Dumps all the block indicies for this SSTable
* at the end of the file.
@ -717,10 +608,10 @@ public class SSTable
Set<String> keys = blockIndex.keySet();
/* Number of keys in this block */
bufOut.writeInt(keys.size());
for ( String key : keys )
for ( String decoratedKey : keys )
{
bufOut.writeUTF(key);
BlockMetadata blockMetadata = blockIndex.get(key);
bufOut.writeUTF(decoratedKey);
BlockMetadata blockMetadata = blockIndex.get(decoratedKey);
/* position of the key as a relative offset */
bufOut.writeLong(position - blockMetadata.position_);
bufOut.writeLong(blockMetadata.size_);
@ -739,29 +630,21 @@ public class SSTable
blockIndex.clear();
}
public void append(String key, DataOutputBuffer buffer) throws IOException
public void append(String decoratedKey, DataOutputBuffer buffer) throws IOException
{
long currentPosition = beforeAppend(key);
dataWriter_.append(key, buffer);
afterAppend(key, currentPosition, buffer.getLength());
}
public void append(String key, BigInteger hash, DataOutputBuffer buffer) throws IOException
{
long currentPosition = beforeAppend(hash);
/* Use as key - hash + ":" + key */
dataWriter_.append(hash + ":" + key, buffer);
afterAppend(hash, currentPosition, buffer.getLength());
long currentPosition = beforeAppend(decoratedKey);
dataWriter_.append(decoratedKey, buffer);
afterAppend(decoratedKey, currentPosition, buffer.getLength());
}
public void append(String key, byte[] value) throws IOException
public void append(String decoratedKey, byte[] value) throws IOException
{
long currentPosition = beforeAppend(key);
dataWriter_.append(key, value);
afterAppend(key, currentPosition, value.length );
long currentPosition = beforeAppend(decoratedKey);
dataWriter_.append(decoratedKey, value);
afterAppend(decoratedKey, currentPosition, value.length );
}
private Coordinate getCoordinates(String key, IFileReader dataReader) throws IOException
private Coordinate getCoordinates(String decoratedKey, IFileReader dataReader) throws IOException
{
List<KeyPositionInfo> indexInfo = indexMetadataMap_.get(dataFile_);
int size = (indexInfo == null) ? 0 : indexInfo.size();
@ -769,7 +652,7 @@ public class SSTable
long end = dataReader.getEOF();
if ( size > 0 )
{
int index = Collections.binarySearch(indexInfo, new KeyPositionInfo(key));
int index = Collections.binarySearch(indexInfo, new KeyPositionInfo(decoratedKey));
if ( index < 0 )
{
/*
@ -819,48 +702,33 @@ public class SSTable
return new Coordinate(start, end);
}
/**
* Convert the application key into the appropriate application
* key based on the partition type.
*
* @param key the application key
* @return the appropriate key based on partition mechanism
*/
private String morphKey(String key)
public DataInputBuffer next(final String clientKey, String cfName, List<String> columnNames) throws IOException
{
String internalKey = key;
PartitionerType pType = StorageService.getPartitionerType();
switch ( pType )
{
case OPHF:
break;
default:
internalKey = FBUtilities.hash(key).toString();
break;
}
return internalKey;
return next(clientKey, cfName, columnNames, null);
}
public DataInputBuffer next(String key, String cf, List<String> cNames, IndexHelper.TimeRange timeRange) throws IOException
public DataInputBuffer next(final String clientKey, String cfName, List<String> columnNames, IndexHelper.TimeRange timeRange) throws IOException
{
DataInputBuffer bufIn = new DataInputBuffer();
IFileReader dataReader = null;
try
{
dataReader = SequenceFile.reader(dataFile_);
// dataReader = SequenceFile.chksumReader(dataFile_, 4*1024*1024);
/* Morph key into actual key based on the partition type. */
key = morphKey(key);
Coordinate fileCoordinate = getCoordinates(key, dataReader);
String decoratedKey = StorageService.getPartitioner().decorateKey(clientKey);
Coordinate fileCoordinate = getCoordinates(decoratedKey, dataReader);
/*
* we have the position we have to read from in order to get the
* column family, get the column family and column(s) needed.
*/
DataOutputBuffer bufOut = new DataOutputBuffer();
long bytesRead = dataReader.next(key, bufOut, cf, cNames, timeRange, fileCoordinate);
if (bytesRead != -1L)
DataInputBuffer bufIn = new DataInputBuffer();
long bytesRead = dataReader.next(decoratedKey, bufOut, cfName, columnNames, timeRange, fileCoordinate);
if ( bytesRead != -1L )
{
if (bufOut.getLength() > 0)
if ( bufOut.getLength() > 0 )
{
bufIn.reset(bufOut.getData(), bufOut.getLength());
/* read the key even though we do not use it */
@ -868,6 +736,7 @@ public class SSTable
bufIn.readInt();
}
}
return bufIn;
}
finally
{
@ -876,15 +745,14 @@ public class SSTable
dataReader.close();
}
}
return bufIn;
}
public DataInputBuffer next(String key, String columnFamilyColumn) throws IOException
public DataInputBuffer next(String clientKey, String columnFamilyColumn) throws IOException
{
String[] values = RowMutation.getColumnAndColumnFamily(columnFamilyColumn);
String columnFamilyName = values[0];
List<String> columnNames = (values.length == 1) ? null : Arrays.asList(values[1]);
return next(key, columnFamilyName, columnNames, null);
List<String> cnNames = (values.length == 1) ? null : Arrays.asList(values[1]);
return next(clientKey, columnFamilyName, cnNames);
}
public void close() throws IOException

View File

@ -28,12 +28,8 @@ import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Arrays;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.RowMutation;
import org.apache.cassandra.service.PartitionerType;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.BloomFilter;
import org.apache.cassandra.utils.LogUtil;
@ -552,35 +548,6 @@ public class SequenceFile
return filename_;
}
/**
* Given the application key this method basically figures if
* the key is in the block. Key comparisons differ based on the
* partition function. In OPHF key is stored as is but in the
* case of a Random hash key used internally is hash(key):key.
*
* @param key which we are looking for
* @param in DataInput stream into which we are looking for the key.
* @return true if key is found and false otherwise.
* @throws IOException
*/
protected boolean isKeyInBlock(String key, DataInput in) throws IOException
{
boolean bVal = false;
String keyInBlock = in.readUTF();
PartitionerType pType = StorageService.getPartitionerType();
switch (pType)
{
case OPHF:
bVal = keyInBlock.equals(key);
break;
default:
bVal = keyInBlock.split(":")[0].equals(key);
break;
}
return bVal;
}
/**
* Return the position of the given key from the block index.
*
@ -658,7 +625,7 @@ public class SequenceFile
int keys = bufIn.readInt();
for (int i = 0; i < keys; ++i)
{
if (isKeyInBlock(key, bufIn))
if (bufIn.readUTF().equals(key))
{
long position = bufIn.readLong();
long dataSize = bufIn.readLong();
@ -821,33 +788,6 @@ public class SequenceFile
return totalBytesRead;
}
/**
* This is useful in figuring out the key in system. If an OPHF
* is used then the "key" is the application supplied key. If a random
* partitioning mechanism is used then the key is of the form
* hash:key where hash is used internally as the key.
*
* @param in the DataInput stream from which the key needs to be read
* @return the appropriate key based on partitioning type
* @throws IOException
*/
protected String readKeyFromDisk(DataInput in) throws IOException
{
String keyInDisk = null;
PartitionerType pType = StorageService.getPartitionerType();
switch (pType)
{
case OPHF:
keyInDisk = in.readUTF();
break;
default:
keyInDisk = in.readUTF().split(":")[0];
break;
}
return keyInDisk;
}
/**
* This method dumps the next key/value into the DataOuputStream
* passed in. Always use this method to query for application
@ -874,7 +814,7 @@ public class SequenceFile
seekTo(key, section);
/* note the position where the key starts */
long startPosition = file_.getFilePointer();
String keyInDisk = readKeyFromDisk(file_);
String keyInDisk = file_.readUTF();
if (keyInDisk != null)
{
/*
@ -1119,7 +1059,7 @@ public class SequenceFile
seekTo(key, section);
/* note the position where the key starts */
long startPosition = file_.getFilePointer();
String keyInDisk = readKeyFromDisk(file_);
String keyInDisk = file_.readUTF();
if (keyInDisk != null)
{
/*

View File

@ -19,8 +19,25 @@
package org.apache.cassandra.service;
import java.math.BigInteger;
import java.util.Comparator;
import java.util.Iterator;
public interface IPartitioner
{
public BigInteger hash(String key);
/**
* transform key to on-disk format s.t. keys are stored in node comparison order.
* this lets bootstrap rip out parts of the sstable sequentially instead of doing random seeks.
*
* @param key the raw, client-facing key
* @return decorated on-disk version of key
*/
public String decorateKey(String key);
public String undecorateKey(String decoratedKey);
public Comparator<String> getDecoratedKeyComparator();
public Comparator<String> getReverseDecoratedKeyComparator();
}

View File

@ -19,14 +19,30 @@
package org.apache.cassandra.service;
import java.math.BigInteger;
import java.util.Comparator;
import java.text.Collator;
public class OrderPreservingHashPartitioner implements IPartitioner
{
private final static int maxKeyHashLength_ = 36;
private final static BigInteger ONE = BigInteger.ONE;
/* May be even 255L will work. But I need to verify that. */
private static final BigInteger prime_ = BigInteger.valueOf(Character.MAX_VALUE);
private static final Comparator<String> comparator = new Comparator<String>()
{
public int compare(String o1, String o2)
{
return o1.compareTo(o2);
}
};
private static final Comparator<String> rcomparator = new Comparator<String>()
{
public int compare(String o1, String o2)
{
return -comparator.compare(o1, o2);
}
};
public BigInteger hash(String key)
{
BigInteger h = BigInteger.ZERO;
@ -41,4 +57,24 @@ public class OrderPreservingHashPartitioner implements IPartitioner
}
return h;
}
public String decorateKey(String key)
{
return key;
}
public String undecorateKey(String decoratedKey)
{
return decoratedKey;
}
public Comparator<String> getReverseDecoratedKeyComparator()
{
return rcomparator;
}
public Comparator<String> getDecoratedKeyComparator()
{
return comparator;
}
}

View File

@ -1,25 +0,0 @@
/**
* 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.cassandra.service;
public enum PartitionerType
{
RANDOM,
OPHF
}

View File

@ -19,6 +19,7 @@
package org.apache.cassandra.service;
import java.math.BigInteger;
import java.util.Comparator;
import org.apache.cassandra.utils.FBUtilities;
@ -31,8 +32,51 @@ import org.apache.cassandra.utils.FBUtilities;
*/
public class RandomPartitioner implements IPartitioner
{
public BigInteger hash(String key)
private static final Comparator<String> comparator = new Comparator<String>()
{
public int compare(String o1, String o2)
{
String[] split1 = o1.split(":", 2);
String[] split2 = o2.split(":", 2);
BigInteger i1 = new BigInteger(split1[0]);
BigInteger i2 = new BigInteger(split2[0]);
int v = i1.compareTo(i2);
if (v != 0) {
return v;
}
return split1[1].compareTo(split2[1]);
}
};
private static final Comparator<String> rcomparator = new Comparator<String>()
{
public int compare(String o1, String o2)
{
return -comparator.compare(o1, o2);
}
};
public BigInteger hash(String key)
{
return FBUtilities.hash(key);
}
public String decorateKey(String key)
{
return hash(key).toString() + ":" + key;
}
public String undecorateKey(String decoratedKey)
{
return decoratedKey.split(":", 2)[1];
}
public Comparator<String> getDecoratedKeyComparator()
{
return comparator;
}
public Comparator<String> getReverseDecoratedKeyComparator()
{
return rcomparator;
}
}

View File

@ -161,11 +161,6 @@ public final class StorageService implements IEndPointStateChangeSubscriber, Sto
{
return "http://" + tcpAddr_.getHost() + ":" + DatabaseDescriptor.getHttpPort();
}
public static PartitionerType getPartitionerType()
{
return (DatabaseDescriptor.ophf_.equalsIgnoreCase(DatabaseDescriptor.getHashingStrategy())) ? PartitionerType.OPHF : PartitionerType.RANDOM;
}
/**
* This is a facade for the hashing
@ -176,6 +171,10 @@ public final class StorageService implements IEndPointStateChangeSubscriber, Sto
{
return partitioner_.hash(key);
}
public static IPartitioner getPartitioner() {
return partitioner_;
}
public static enum BootstrapMode
{

View File

@ -28,11 +28,9 @@ import java.util.Random;
import org.apache.cassandra.db.ColumnFamily;
import org.apache.cassandra.db.IColumn;
import org.apache.cassandra.db.PrimaryKey;
import org.apache.cassandra.io.DataInputBuffer;
import org.apache.cassandra.io.DataOutputBuffer;
import org.apache.cassandra.io.SSTable;
import org.apache.cassandra.service.PartitionerType;
import org.apache.cassandra.utils.BloomFilter;
@ -58,47 +56,7 @@ public class SSTableTest
}
ssTable.close(bf);
}
private static void hashSSTableWrite() throws Throwable
{
Map<String, ColumnFamily> columnFamilies = new HashMap<String, ColumnFamily>();
byte[] bytes = new byte[64*1024];
Random random = new Random();
for ( int i = 100; i < 1000; ++i )
{
String key = Integer.toString(i);
ColumnFamily cf = new ColumnFamily("Test", "Standard");
// random.nextBytes(bytes);
cf.addColumn("C", "Avinash Lakshman is a good man".getBytes(), i);
columnFamilies.put(key, cf);
}
flushForRandomPartitioner(columnFamilies);
}
private static void flushForRandomPartitioner(Map<String, ColumnFamily> columnFamilies) throws Throwable
{
SSTable ssTable = new SSTable("C:\\Engagements\\Cassandra", "Table-Test-1", PartitionerType.RANDOM);
/* List of primary keys in sorted order */
List<PrimaryKey> pKeys = PrimaryKey.create( columnFamilies.keySet() );
DataOutputBuffer buffer = new DataOutputBuffer();
/* Use this BloomFilter to decide if a key exists in a SSTable */
BloomFilter bf = new BloomFilter(pKeys.size(), 15);
for ( PrimaryKey pKey : pKeys )
{
buffer.reset();
ColumnFamily columnFamily = columnFamilies.get(pKey.key());
if ( columnFamily != null )
{
/* serialize the cf with column indexes */
ColumnFamily.serializerWithIndexes().serialize( columnFamily, buffer );
/* Now write the key and value to disk */
ssTable.append(pKey.key(), pKey.hash(), buffer);
bf.fill(pKey.key());
}
}
ssTable.close(bf);
}
private static void readSSTable() throws Throwable
{
SSTable ssTable = new SSTable("C:\\Engagements\\Cassandra\\Table-Test-1-Data.db");

View File

@ -64,7 +64,7 @@ public class ColumnFamilyStoreTest extends ServerTest
}
}
validateNameSort(table);
// validateNameSort(table);
table.getColumnFamilyStore("Standard1").forceFlush();
table.getColumnFamilyStore("Super1").forceFlush();

View File

@ -1,6 +1,7 @@
package org.apache.cassandra.io;
import org.apache.cassandra.ServerTest;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.db.FileStruct;
import org.apache.cassandra.utils.BloomFilter;
import org.apache.commons.collections.CollectionUtils;
@ -29,8 +30,8 @@ public class SSTableTest extends ServerTest {
bf.fill(key);
ssTable.close(bf);
// TODO append/next are not symmetrical ops anymore
// TODO this is broken because SST/SequenceFile now assume that only CFs are written
// verify
ssTable = new SSTable(f.getPath() + "-Data.db");
DataInputBuffer bufIn = ssTable.next(key, "Test:C");
@ -59,7 +60,7 @@ public class SSTableTest extends ServerTest {
}
ssTable.close(bf);
// TODO append/next are not symmetrical ops anymore
// TODO this is broken because SST/SequenceFile now assume that only CFs are written
// verify
List<String> keys = new ArrayList(map.keySet());