blob: f4b9617f9ad8b58da601b024ba771f34ad1178d0 [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.cassandra.io.sstable;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.cassandra.db.columniterator.OnDiskAtomIterator;
import org.apache.cassandra.io.sstable.format.SSTableFormat;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.sstable.format.Version;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.Util;
import org.apache.cassandra.config.KSMetaData;
import org.apache.cassandra.db.ColumnFamily;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.DecoratedKey;
import org.apache.cassandra.db.DeletionInfo;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.composites.CellNameType;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.locator.SimpleStrategy;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.StreamPlan;
import org.apache.cassandra.streaming.StreamSession;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
/**
* Tests backwards compatibility for SSTables
*/
public class LegacySSTableTest
{
public static final String LEGACY_SSTABLE_PROP = "legacy-sstable-root";
public static final String KSNAME = "Keyspace1";
public static final String CFNAME = "Standard1";
public static Set<String> TEST_DATA;
public static File LEGACY_SSTABLE_ROOT;
@BeforeClass
public static void defineSchema() throws ConfigurationException
{
SchemaLoader.prepareServer();
SchemaLoader.createKeyspace(KSNAME,
SimpleStrategy.class,
KSMetaData.optsWithRF(1),
SchemaLoader.standardCFMD(KSNAME, CFNAME));
beforeClass();
}
public static void beforeClass()
{
Keyspace.setInitialized();
String scp = System.getProperty(LEGACY_SSTABLE_PROP);
assert scp != null;
LEGACY_SSTABLE_ROOT = new File(scp).getAbsoluteFile();
assert LEGACY_SSTABLE_ROOT.isDirectory();
TEST_DATA = new HashSet<String>();
for (int i = 100; i < 1000; ++i)
TEST_DATA.add(Integer.toString(i));
}
/**
* Get a descriptor for the legacy sstable at the given version.
*/
protected Descriptor getDescriptor(String ver)
{
File directory = new File(LEGACY_SSTABLE_ROOT + File.separator + ver + File.separator + KSNAME);
return new Descriptor(ver, directory, KSNAME, CFNAME, 0, Descriptor.Type.FINAL, SSTableFormat.Type.LEGACY);
}
/**
* Generates a test SSTable for use in this classes' tests. Uncomment and run against an older build
* and the output will be copied to a version subdirectory in 'LEGACY_SSTABLE_ROOT'
*
@Test
public void buildTestSSTable() throws IOException
{
// write the output in a version specific directory
Descriptor dest = getDescriptor(Descriptor.Version.current_version);
assert dest.directory.mkdirs() : "Could not create " + dest.directory + ". Might it already exist?";
SSTableReader ssTable = SSTableUtils.prepare().ks(KSNAME).cf(CFNAME).dest(dest).write(TEST_DATA);
assert ssTable.descriptor.generation == 0 :
"In order to create a generation 0 sstable, please run this test alone.";
System.out.println(">>> Wrote " + dest);
}
*/
@Test
public void testStreaming() throws Throwable
{
StorageService.instance.initServer();
for (File version : LEGACY_SSTABLE_ROOT.listFiles())
if (Version.validate(version.getName()) && SSTableFormat.Type.LEGACY.info.getVersion(version.getName()).isCompatible())
testStreaming(version.getName());
}
private void testStreaming(String version) throws Exception
{
SSTableReader sstable = SSTableReader.open(getDescriptor(version));
IPartitioner p = StorageService.getPartitioner();
List<Range<Token>> ranges = new ArrayList<>();
ranges.add(new Range<>(p.getMinimumToken(), p.getToken(ByteBufferUtil.bytes("100"))));
ranges.add(new Range<>(p.getToken(ByteBufferUtil.bytes("100")), p.getMinimumToken()));
ArrayList<StreamSession.SSTableStreamingSections> details = new ArrayList<>();
details.add(new StreamSession.SSTableStreamingSections(sstable.ref(),
sstable.getPositionsForRanges(ranges),
sstable.estimatedKeysForRanges(ranges), sstable.getSSTableMetadata().repairedAt));
new StreamPlan("LegacyStreamingTest").transferFiles(FBUtilities.getBroadcastAddress(), details)
.execute().get();
ColumnFamilyStore cfs = Keyspace.open(KSNAME).getColumnFamilyStore(CFNAME);
assert cfs.getSSTables().size() == 1;
sstable = cfs.getSSTables().iterator().next();
CellNameType type = sstable.metadata.comparator;
for (String keystring : TEST_DATA)
{
ByteBuffer key = ByteBufferUtil.bytes(keystring);
OnDiskAtomIterator iter = sstable.iterator(Util.dk(key), FBUtilities.singleton(Util.cellname(key), type));
ColumnFamily cf = iter.getColumnFamily();
// check not deleted (CASSANDRA-6527)
assert cf.deletionInfo().equals(DeletionInfo.live());
assert iter.next().name().toByteBuffer().equals(key);
}
sstable.selfRef().release();
}
@Test
public void testVersions() throws Throwable
{
boolean notSkipped = false;
for (File version : LEGACY_SSTABLE_ROOT.listFiles())
{
if (Version.validate(version.getName()) && SSTableFormat.Type.LEGACY.info.getVersion(version.getName()).isCompatible())
{
notSkipped = true;
testVersion(version.getName());
}
}
assert notSkipped;
}
public void testVersion(String version) throws Throwable
{
try
{
SSTableReader reader = SSTableReader.open(getDescriptor(version));
CellNameType type = reader.metadata.comparator;
for (String keystring : TEST_DATA)
{
ByteBuffer key = ByteBufferUtil.bytes(keystring);
// confirm that the bloom filter does not reject any keys/names
DecoratedKey dk = reader.partitioner.decorateKey(key);
OnDiskAtomIterator iter = reader.iterator(dk, FBUtilities.singleton(Util.cellname(key), type));
assert iter.next().name().toByteBuffer().equals(key);
}
// TODO actually test some reads
}
catch (Throwable e)
{
System.err.println("Failed to read " + version);
throw e;
}
}
}