mirror of https://github.com/apache/cassandra
recognize attempt todrop just the index while leaving the column definition
patch by jbellis; reviewed by brandonwilliams for CASSANDRA-2619 git-svn-id: https://svn.apache.org/repos/asf/cassandra/branches/cassandra-0.7@1100464 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
62cd28dfef
commit
5f8991c73a
|
|
@ -12,6 +12,8 @@
|
|||
rebuffering in BRAF (CASSANDRA-2581)
|
||||
* include indexes in snapshots (CASSANDRA-2596)
|
||||
* improve ignoring of obsolete mutations in index maintenance (CASSANDRA-2401)
|
||||
* recognize attempt to drop just the index while leaving the column
|
||||
definition alone (CASSANDRA-2619)
|
||||
|
||||
|
||||
0.7.5
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ public final class CFMetaData
|
|||
private double memtableOperationsInMillions; // default based on throughput
|
||||
// NOTE: if you find yourself adding members to this class, make sure you keep the convert methods in lockstep.
|
||||
|
||||
private final Map<ByteBuffer, ColumnDefinition> column_metadata;
|
||||
final Map<ByteBuffer, ColumnDefinition> column_metadata;
|
||||
|
||||
private CFMetaData(String tableName,
|
||||
String cfName,
|
||||
|
|
@ -664,19 +664,19 @@ public final class CFMetaData
|
|||
memtableThroughputInMb = cf_def.memtable_throughput_in_mb;
|
||||
memtableOperationsInMillions = cf_def.memtable_operations_in_millions;
|
||||
|
||||
// adjust secondary indexes. figure out who is coming and going.
|
||||
// adjust column definitions. figure out who is coming and going.
|
||||
Set<ByteBuffer> toRemove = new HashSet<ByteBuffer>();
|
||||
Set<ByteBuffer> newIndexNames = new HashSet<ByteBuffer>();
|
||||
Set<ByteBuffer> newColumns = new HashSet<ByteBuffer>();
|
||||
Set<org.apache.cassandra.avro.ColumnDef> toAdd = new HashSet<org.apache.cassandra.avro.ColumnDef>();
|
||||
for (org.apache.cassandra.avro.ColumnDef def : cf_def.column_metadata)
|
||||
{
|
||||
newIndexNames.add(def.name);
|
||||
newColumns.add(def.name);
|
||||
if (!column_metadata.containsKey(def.name))
|
||||
toAdd.add(def);
|
||||
}
|
||||
for (ByteBuffer indexName : column_metadata.keySet())
|
||||
if (!newIndexNames.contains(indexName))
|
||||
toRemove.add(indexName);
|
||||
for (ByteBuffer name : column_metadata.keySet())
|
||||
if (!newColumns.contains(name))
|
||||
toRemove.add(name);
|
||||
|
||||
// remove the ones leaving.
|
||||
for (ByteBuffer indexName : toRemove)
|
||||
|
|
|
|||
|
|
@ -177,7 +177,8 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
|
|||
// future: if/when we have modifiable settings for secondary indexes, they'll need to be handled here.
|
||||
for (ByteBuffer indexedColumn : indexedColumns.keySet())
|
||||
{
|
||||
if (!metadata.getColumn_metadata().containsKey(indexedColumn))
|
||||
ColumnDefinition def = metadata.getColumn_metadata().get(indexedColumn);
|
||||
if (def == null || def.getIndexType() == null)
|
||||
removeIndex(indexedColumn);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -207,3 +207,16 @@ keyspaces:
|
|||
rows_cached: 100
|
||||
- name: CFWithoutCache
|
||||
rows_cached: 0
|
||||
|
||||
- name: Keyspace6
|
||||
replica_placement_strategy: org.apache.cassandra.locator.SimpleStrategy
|
||||
replication_factor: 1
|
||||
column_families:
|
||||
- name: Indexed1
|
||||
column_metadata:
|
||||
- name: birthdate
|
||||
validator_class: LongType
|
||||
index_type: KEYS
|
||||
rows_cached: 0
|
||||
keys_cached: 0
|
||||
|
||||
|
|
|
|||
|
|
@ -1,797 +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 static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.apache.avro.util.Utf8;
|
||||
import org.apache.cassandra.CleanupHelper;
|
||||
import org.apache.cassandra.Util;
|
||||
import org.apache.cassandra.config.CFMetaData;
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
import org.apache.cassandra.config.ConfigurationException;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.config.KSMetaData;
|
||||
import org.apache.cassandra.db.filter.QueryFilter;
|
||||
import org.apache.cassandra.db.filter.QueryPath;
|
||||
import org.apache.cassandra.db.marshal.BytesType;
|
||||
import org.apache.cassandra.db.marshal.UTF8Type;
|
||||
import org.apache.cassandra.db.migration.AddColumnFamily;
|
||||
import org.apache.cassandra.db.migration.AddKeyspace;
|
||||
import org.apache.cassandra.db.migration.DropColumnFamily;
|
||||
import org.apache.cassandra.db.migration.DropKeyspace;
|
||||
import org.apache.cassandra.db.migration.Migration;
|
||||
import org.apache.cassandra.db.migration.RenameColumnFamily;
|
||||
import org.apache.cassandra.db.migration.RenameKeyspace;
|
||||
import org.apache.cassandra.db.migration.UpdateColumnFamily;
|
||||
import org.apache.cassandra.db.migration.UpdateKeyspace;
|
||||
import org.apache.cassandra.io.SerDeUtils;
|
||||
import org.apache.cassandra.locator.OldNetworkTopologyStrategy;
|
||||
import org.apache.cassandra.locator.SimpleStrategy;
|
||||
import org.apache.cassandra.thrift.IndexType;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
import org.apache.cassandra.utils.UUIDGen;
|
||||
import org.junit.Test;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
|
||||
|
||||
public class DefsTest extends CleanupHelper
|
||||
{
|
||||
@Test
|
||||
public void testZeroInjection() throws IOException
|
||||
{
|
||||
org.apache.cassandra.avro.CfDef cd = new org.apache.cassandra.avro.CfDef();
|
||||
// populate only fields that must be non-null.
|
||||
cd.keyspace = new Utf8("Lest Ks");
|
||||
cd.name = new Utf8("Mest Cf");
|
||||
|
||||
org.apache.cassandra.avro.CfDef cd2 = SerDeUtils.deserializeWithSchema(SerDeUtils.serializeWithSchema(cd), new org.apache.cassandra.avro.CfDef());
|
||||
assert cd.equals(cd2);
|
||||
// make sure some of the fields didn't get unexpected zeros put in during [de]serialize operations.
|
||||
assert cd.min_compaction_threshold == null;
|
||||
assert cd2.min_compaction_threshold == null;
|
||||
assert cd.row_cache_save_period_in_seconds == null;
|
||||
assert cd2.row_cache_save_period_in_seconds == null;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureStaticCFMIdsAreLessThan1000()
|
||||
{
|
||||
assert CFMetaData.StatusCf.cfId == 0;
|
||||
assert CFMetaData.HintsCf.cfId == 1;
|
||||
assert CFMetaData.MigrationsCf.cfId == 2;
|
||||
assert CFMetaData.SchemaCf.cfId == 3;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCFMetaDataApply() throws ConfigurationException
|
||||
{
|
||||
Map<ByteBuffer, ColumnDefinition> indexes = new HashMap<ByteBuffer, ColumnDefinition>();
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
ByteBuffer name = ByteBuffer.wrap(new byte[] { (byte)i });
|
||||
indexes.put(name, new ColumnDefinition(name, null, IndexType.KEYS, Integer.toString(i)));
|
||||
}
|
||||
CFMetaData cfm = new CFMetaData("Keyspace1",
|
||||
"TestApplyCFM_CF",
|
||||
ColumnFamilyType.Standard,
|
||||
BytesType.instance,
|
||||
null,
|
||||
"No comment",
|
||||
1.0,
|
||||
1.0,
|
||||
0.5,
|
||||
100000,
|
||||
null,
|
||||
500,
|
||||
500,
|
||||
500,
|
||||
500,
|
||||
500,
|
||||
500,
|
||||
500.0,
|
||||
indexes);
|
||||
|
||||
// we'll be adding this one later. make sure it's not already there.
|
||||
assert cfm.getColumn_metadata().get(ByteBuffer.wrap(new byte[] { 5 })) == null;
|
||||
org.apache.cassandra.avro.CfDef cfDef = CFMetaData.convertToAvro(cfm);
|
||||
|
||||
// add one.
|
||||
org.apache.cassandra.avro.ColumnDef addIndexDef = new org.apache.cassandra.avro.ColumnDef();
|
||||
addIndexDef.index_name = "5";
|
||||
addIndexDef.index_type = org.apache.cassandra.avro.IndexType.KEYS;
|
||||
addIndexDef.name = ByteBuffer.wrap(new byte[] { 5 });
|
||||
addIndexDef.validation_class = BytesType.class.getName();
|
||||
cfDef.column_metadata.add(addIndexDef);
|
||||
|
||||
// remove one.
|
||||
org.apache.cassandra.avro.ColumnDef removeIndexDef = new org.apache.cassandra.avro.ColumnDef();
|
||||
removeIndexDef.index_name = "0";
|
||||
removeIndexDef.index_type = org.apache.cassandra.avro.IndexType.KEYS;
|
||||
removeIndexDef.name = ByteBuffer.wrap(new byte[] { 0 });
|
||||
removeIndexDef.validation_class = BytesType.class.getName();
|
||||
assert cfDef.column_metadata.remove(removeIndexDef);
|
||||
|
||||
cfm.apply(cfDef);
|
||||
|
||||
for (int i = 1; i < indexes.size(); i++)
|
||||
assert cfm.getColumn_metadata().get(ByteBuffer.wrap(new byte[] { 1 })) != null;
|
||||
assert cfm.getColumn_metadata().get(ByteBuffer.wrap(new byte[] { 0 })) == null;
|
||||
assert cfm.getColumn_metadata().get(ByteBuffer.wrap(new byte[] { 5 })) != null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidNames() throws IOException
|
||||
{
|
||||
String[] valid = {"1", "a", "_1", "b_", "__", "1_a"};
|
||||
for (String s : valid)
|
||||
assert Migration.isLegalName(s);
|
||||
|
||||
String[] invalid = {"b@t", "dash-y", "", " ", "dot.s", ".hidden"};
|
||||
for (String s : invalid)
|
||||
assert !Migration.isLegalName(s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveAndRestore() throws IOException
|
||||
{
|
||||
// verify dump and reload.
|
||||
UUID first = UUIDGen.makeType1UUIDFromHost(FBUtilities.getLocalAddress());
|
||||
DefsTable.dumpToStorage(first);
|
||||
List<KSMetaData> defs = new ArrayList<KSMetaData>(DefsTable.loadFromStorage(first));
|
||||
|
||||
assert defs.size() > 0;
|
||||
assert defs.size() == DatabaseDescriptor.getNonSystemTables().size();
|
||||
for (KSMetaData loaded : defs)
|
||||
{
|
||||
KSMetaData defined = DatabaseDescriptor.getTableDefinition(loaded.name);
|
||||
assert defined.equals(loaded);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addNewCfToBogusTable() throws InterruptedException
|
||||
{
|
||||
CFMetaData newCf = addTestCF("MadeUpKeyspace", "NewCF", "new cf");
|
||||
try
|
||||
{
|
||||
new AddColumnFamily(newCf).apply();
|
||||
throw new AssertionError("You shouldn't be able to do anything to a keyspace that doesn't exist.");
|
||||
}
|
||||
catch (ConfigurationException expected)
|
||||
{
|
||||
}
|
||||
catch (IOException unexpected)
|
||||
{
|
||||
throw new AssertionError("Unexpected exception.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrations() throws IOException, ConfigurationException
|
||||
{
|
||||
// do a save. make sure it doesn't mess with the defs version.
|
||||
UUID prior = DatabaseDescriptor.getDefsVersion();
|
||||
UUID ver0 = UUIDGen.makeType1UUIDFromHost(FBUtilities.getLocalAddress());
|
||||
DefsTable.dumpToStorage(ver0);
|
||||
assert DatabaseDescriptor.getDefsVersion().equals(prior);
|
||||
|
||||
// add a cf.
|
||||
CFMetaData newCf1 = addTestCF("Keyspace1", "MigrationCf_1", "Migration CF");
|
||||
|
||||
Migration m1 = new AddColumnFamily(newCf1);
|
||||
m1.apply();
|
||||
UUID ver1 = m1.getVersion();
|
||||
assert DatabaseDescriptor.getDefsVersion().equals(ver1);
|
||||
|
||||
// rename it.
|
||||
Migration m2 = new RenameColumnFamily("Keyspace1", "MigrationCf_1", "MigrationCf_2");
|
||||
m2.apply();
|
||||
UUID ver2 = m2.getVersion();
|
||||
assert DatabaseDescriptor.getDefsVersion().equals(ver2);
|
||||
|
||||
// drop it.
|
||||
Migration m3 = new DropColumnFamily("Keyspace1", "MigrationCf_2");
|
||||
m3.apply();
|
||||
UUID ver3 = m3.getVersion();
|
||||
assert DatabaseDescriptor.getDefsVersion().equals(ver3);
|
||||
|
||||
// now lets load the older migrations to see if that code works.
|
||||
Collection<IColumn> serializedMigrations = Migration.getLocalMigrations(ver1, ver3);
|
||||
assert serializedMigrations.size() == 3;
|
||||
|
||||
// test deserialization of the migrations.
|
||||
Migration[] reconstituded = new Migration[3];
|
||||
int i = 0;
|
||||
for (IColumn col : serializedMigrations)
|
||||
{
|
||||
UUID version = UUIDGen.getUUID(col.name());
|
||||
reconstituded[i] = Migration.deserialize(col.value());
|
||||
assert version.equals(reconstituded[i].getVersion());
|
||||
i++;
|
||||
}
|
||||
|
||||
assert m1.getClass().equals(reconstituded[0].getClass());
|
||||
assert m2.getClass().equals(reconstituded[1].getClass());
|
||||
assert m3.getClass().equals(reconstituded[2].getClass());
|
||||
|
||||
// verify that the row mutations are the same. rather than exposing the private fields, serialize and verify.
|
||||
assert m1.serialize().equals(reconstituded[0].serialize());
|
||||
assert m2.serialize().equals(reconstituded[1].serialize());
|
||||
assert m3.serialize().equals(reconstituded[2].serialize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addNewCF() throws ConfigurationException, IOException, ExecutionException, InterruptedException
|
||||
{
|
||||
final String ks = "Keyspace1";
|
||||
final String cf = "BrandNewCf";
|
||||
KSMetaData original = DatabaseDescriptor.getTableDefinition(ks);
|
||||
|
||||
CFMetaData newCf = addTestCF(original.name, cf, "A New Column Family");
|
||||
|
||||
assert !DatabaseDescriptor.getTableDefinition(ks).cfMetaData().containsKey(newCf.cfName);
|
||||
new AddColumnFamily(newCf).apply();
|
||||
|
||||
assert DatabaseDescriptor.getTableDefinition(ks).cfMetaData().containsKey(newCf.cfName);
|
||||
assert DatabaseDescriptor.getTableDefinition(ks).cfMetaData().get(newCf.cfName).equals(newCf);
|
||||
|
||||
// now read and write to it.
|
||||
DecoratedKey dk = Util.dk("key0");
|
||||
RowMutation rm = new RowMutation(ks, dk.key);
|
||||
rm.add(new QueryPath(cf, null, ByteBufferUtil.bytes("col0")), ByteBufferUtil.bytes("value0"), 1L);
|
||||
rm.apply();
|
||||
ColumnFamilyStore store = Table.open(ks).getColumnFamilyStore(cf);
|
||||
assert store != null;
|
||||
store.forceBlockingFlush();
|
||||
|
||||
ColumnFamily cfam = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath(cf), ByteBufferUtil.bytes("col0")));
|
||||
assert cfam.getColumn(ByteBufferUtil.bytes("col0")) != null;
|
||||
IColumn col = cfam.getColumn(ByteBufferUtil.bytes("col0"));
|
||||
assert ByteBufferUtil.bytes("value0").equals(col.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dropCf() throws ConfigurationException, IOException, ExecutionException, InterruptedException
|
||||
{
|
||||
DecoratedKey dk = Util.dk("dropCf");
|
||||
// sanity
|
||||
final KSMetaData ks = DatabaseDescriptor.getTableDefinition("Keyspace1");
|
||||
assert ks != null;
|
||||
final CFMetaData cfm = ks.cfMetaData().get("Standard1");
|
||||
assert cfm != null;
|
||||
|
||||
// write some data, force a flush, then verify that files exist on disk.
|
||||
RowMutation rm = new RowMutation(ks.name, dk.key);
|
||||
for (int i = 0; i < 100; i++)
|
||||
rm.add(new QueryPath(cfm.cfName, null, ByteBufferUtil.bytes(("col" + i))), ByteBufferUtil.bytes("anyvalue"), 1L);
|
||||
rm.apply();
|
||||
ColumnFamilyStore store = Table.open(cfm.tableName).getColumnFamilyStore(cfm.cfName);
|
||||
assert store != null;
|
||||
store.forceBlockingFlush();
|
||||
assert DefsTable.getFiles(cfm.tableName, cfm.cfName).size() > 0;
|
||||
|
||||
new DropColumnFamily(ks.name, cfm.cfName).apply();
|
||||
|
||||
assert !DatabaseDescriptor.getTableDefinition(ks.name).cfMetaData().containsKey(cfm.cfName);
|
||||
|
||||
// any write should fail.
|
||||
rm = new RowMutation(ks.name, dk.key);
|
||||
boolean success = true;
|
||||
try
|
||||
{
|
||||
rm.add(new QueryPath("Standard1", null, ByteBufferUtil.bytes("col0")), ByteBufferUtil.bytes("value0"), 1L);
|
||||
rm.apply();
|
||||
}
|
||||
catch (Throwable th)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
assert !success : "This mutation should have failed since the CF no longer exists.";
|
||||
|
||||
// verify that the files are gone.
|
||||
for (File file : DefsTable.getFiles(cfm.tableName, cfm.cfName))
|
||||
{
|
||||
if (file.getPath().endsWith("Data.db") && !new File(file.getPath().replace("Data.db", "Compacted")).exists())
|
||||
throw new AssertionError("undeleted file " + file);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renameCf() throws ConfigurationException, IOException, ExecutionException, InterruptedException
|
||||
{
|
||||
DecoratedKey dk = Util.dk("key0");
|
||||
final KSMetaData ks = DatabaseDescriptor.getTableDefinition("Keyspace2");
|
||||
assert ks != null;
|
||||
final CFMetaData oldCfm = ks.cfMetaData().get("Standard1");
|
||||
assert oldCfm != null;
|
||||
|
||||
// write some data, force a flush, then verify that files exist on disk.
|
||||
RowMutation rm = new RowMutation(ks.name, dk.key);
|
||||
for (int i = 0; i < 100; i++)
|
||||
rm.add(new QueryPath(oldCfm.cfName, null, ByteBufferUtil.bytes(("col" + i))), ByteBufferUtil.bytes("anyvalue"), 1L);
|
||||
rm.apply();
|
||||
ColumnFamilyStore store = Table.open(oldCfm.tableName).getColumnFamilyStore(oldCfm.cfName);
|
||||
assert store != null;
|
||||
store.forceBlockingFlush();
|
||||
int fileCount = DefsTable.getFiles(oldCfm.tableName, oldCfm.cfName).size();
|
||||
assert fileCount > 0;
|
||||
|
||||
final String cfName = "St4ndard1Replacement";
|
||||
new RenameColumnFamily(oldCfm.tableName, oldCfm.cfName, cfName).apply();
|
||||
|
||||
assert !DatabaseDescriptor.getTableDefinition(ks.name).cfMetaData().containsKey(oldCfm.cfName);
|
||||
assert DatabaseDescriptor.getTableDefinition(ks.name).cfMetaData().containsKey(cfName);
|
||||
|
||||
// verify that new files are there.
|
||||
assert DefsTable.getFiles(oldCfm.tableName, cfName).size() == fileCount;
|
||||
|
||||
// do some reads.
|
||||
store = Table.open(oldCfm.tableName).getColumnFamilyStore(cfName);
|
||||
assert store != null;
|
||||
ColumnFamily cfam = store.getColumnFamily(QueryFilter.getSliceFilter(dk, new QueryPath(cfName), ByteBufferUtil.EMPTY_BYTE_BUFFER, ByteBufferUtil.EMPTY_BYTE_BUFFER, false, 1000));
|
||||
assert cfam.getSortedColumns().size() == 100; // should be good enough?
|
||||
|
||||
// do some writes
|
||||
rm = new RowMutation(ks.name, dk.key);
|
||||
rm.add(new QueryPath(cfName, null, ByteBufferUtil.bytes("col5")), ByteBufferUtil.bytes("updated"), 2L);
|
||||
rm.apply();
|
||||
store.forceBlockingFlush();
|
||||
|
||||
cfam = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath(cfName), ByteBufferUtil.bytes("col5")));
|
||||
assert cfam.getColumnCount() == 1;
|
||||
assert cfam.getColumn(ByteBufferUtil.bytes("col5")).value().equals( ByteBufferUtil.bytes("updated"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addNewKS() throws ConfigurationException, IOException, ExecutionException, InterruptedException
|
||||
{
|
||||
DecoratedKey dk = Util.dk("key0");
|
||||
CFMetaData newCf = addTestCF("NewKeyspace1", "AddedStandard1", "A new cf for a new ks");
|
||||
|
||||
KSMetaData newKs = new KSMetaData(newCf.tableName, SimpleStrategy.class, null, 5, newCf);
|
||||
|
||||
new AddKeyspace(newKs).apply();
|
||||
|
||||
assert DatabaseDescriptor.getTableDefinition(newCf.tableName) != null;
|
||||
assert DatabaseDescriptor.getTableDefinition(newCf.tableName) == newKs;
|
||||
|
||||
// test reads and writes.
|
||||
RowMutation rm = new RowMutation(newCf.tableName, dk.key);
|
||||
rm.add(new QueryPath(newCf.cfName, null, ByteBufferUtil.bytes("col0")), ByteBufferUtil.bytes("value0"), 1L);
|
||||
rm.apply();
|
||||
ColumnFamilyStore store = Table.open(newCf.tableName).getColumnFamilyStore(newCf.cfName);
|
||||
assert store != null;
|
||||
store.forceBlockingFlush();
|
||||
|
||||
ColumnFamily cfam = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath(newCf.cfName), ByteBufferUtil.bytes("col0")));
|
||||
assert cfam.getColumn(ByteBufferUtil.bytes("col0")) != null;
|
||||
IColumn col = cfam.getColumn(ByteBufferUtil.bytes("col0"));
|
||||
assert ByteBufferUtil.bytes("value0").equals(col.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dropKS() throws ConfigurationException, IOException, ExecutionException, InterruptedException
|
||||
{
|
||||
DecoratedKey dk = Util.dk("dropKs");
|
||||
// sanity
|
||||
final KSMetaData ks = DatabaseDescriptor.getTableDefinition("Keyspace1");
|
||||
assert ks != null;
|
||||
final CFMetaData cfm = ks.cfMetaData().get("Standard2");
|
||||
assert cfm != null;
|
||||
|
||||
// write some data, force a flush, then verify that files exist on disk.
|
||||
RowMutation rm = new RowMutation(ks.name, dk.key);
|
||||
for (int i = 0; i < 100; i++)
|
||||
rm.add(new QueryPath(cfm.cfName, null, ByteBufferUtil.bytes(("col" + i))), ByteBufferUtil.bytes("anyvalue"), 1L);
|
||||
rm.apply();
|
||||
ColumnFamilyStore store = Table.open(cfm.tableName).getColumnFamilyStore(cfm.cfName);
|
||||
assert store != null;
|
||||
store.forceBlockingFlush();
|
||||
assert DefsTable.getFiles(cfm.tableName, cfm.cfName).size() > 0;
|
||||
|
||||
new DropKeyspace(ks.name).apply();
|
||||
|
||||
assert DatabaseDescriptor.getTableDefinition(ks.name) == null;
|
||||
|
||||
// write should fail.
|
||||
rm = new RowMutation(ks.name, dk.key);
|
||||
boolean success = true;
|
||||
try
|
||||
{
|
||||
rm.add(new QueryPath("Standard1", null, ByteBufferUtil.bytes("col0")), ByteBufferUtil.bytes("value0"), 1L);
|
||||
rm.apply();
|
||||
}
|
||||
catch (Throwable th)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
assert !success : "This mutation should have failed since the CF no longer exists.";
|
||||
|
||||
// reads should fail too.
|
||||
try
|
||||
{
|
||||
Table.open(ks.name);
|
||||
}
|
||||
catch (Throwable th)
|
||||
{
|
||||
// this is what has historically happened when you try to open a table that doesn't exist.
|
||||
assert th instanceof NullPointerException;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dropKSUnflushed() throws ConfigurationException, IOException, ExecutionException, InterruptedException
|
||||
{
|
||||
DecoratedKey dk = Util.dk("dropKs");
|
||||
// sanity
|
||||
final KSMetaData ks = DatabaseDescriptor.getTableDefinition("Keyspace3");
|
||||
assert ks != null;
|
||||
final CFMetaData cfm = ks.cfMetaData().get("Standard1");
|
||||
assert cfm != null;
|
||||
|
||||
// write some data
|
||||
RowMutation rm = new RowMutation(ks.name, dk.key);
|
||||
for (int i = 0; i < 100; i++)
|
||||
rm.add(new QueryPath(cfm.cfName, null, ByteBufferUtil.bytes(("col" + i))), ByteBufferUtil.bytes("anyvalue"), 1L);
|
||||
rm.apply();
|
||||
|
||||
new DropKeyspace(ks.name).apply();
|
||||
|
||||
assert DatabaseDescriptor.getTableDefinition(ks.name) == null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renameKs() throws ConfigurationException, IOException, ExecutionException, InterruptedException
|
||||
{
|
||||
DecoratedKey dk = Util.dk("renameKs");
|
||||
final KSMetaData oldKs = DatabaseDescriptor.getTableDefinition("Keyspace2");
|
||||
assert oldKs != null;
|
||||
final String cfName = "Standard3";
|
||||
assert oldKs.cfMetaData().containsKey(cfName);
|
||||
assert oldKs.cfMetaData().get(cfName).tableName.equals(oldKs.name);
|
||||
|
||||
// write some data that we hope to read back later.
|
||||
RowMutation rm = new RowMutation(oldKs.name, dk.key);
|
||||
for (int i = 0; i < 10; i++)
|
||||
rm.add(new QueryPath(cfName, null, ByteBufferUtil.bytes(("col" + i))), ByteBufferUtil.bytes("value"), 1L);
|
||||
rm.apply();
|
||||
ColumnFamilyStore store = Table.open(oldKs.name).getColumnFamilyStore(cfName);
|
||||
assert store != null;
|
||||
store.forceBlockingFlush();
|
||||
assert DefsTable.getFiles(oldKs.name, cfName).size() > 0;
|
||||
|
||||
final String newKsName = "RenamedKeyspace2";
|
||||
new RenameKeyspace(oldKs.name, newKsName).apply();
|
||||
KSMetaData newKs = DatabaseDescriptor.getTableDefinition(newKsName);
|
||||
|
||||
assert DatabaseDescriptor.getTableDefinition(oldKs.name) == null;
|
||||
assert newKs != null;
|
||||
assert newKs.name.equals(newKsName);
|
||||
assert newKs.cfMetaData().containsKey(cfName);
|
||||
assert newKs.cfMetaData().get(cfName).tableName.equals(newKsName);
|
||||
assert DefsTable.getFiles(newKs.name, cfName).size() > 0;
|
||||
|
||||
// read on old should fail.
|
||||
try
|
||||
{
|
||||
Table.open(oldKs.name);
|
||||
}
|
||||
catch (Throwable th)
|
||||
{
|
||||
assert th instanceof NullPointerException;
|
||||
}
|
||||
|
||||
// write on old should fail.
|
||||
rm = new RowMutation(oldKs.name, ByteBufferUtil.bytes("any key will do"));
|
||||
boolean success = true;
|
||||
try
|
||||
{
|
||||
rm.add(new QueryPath(cfName, null, ByteBufferUtil.bytes("col0")), ByteBufferUtil.bytes("value0"), 1L);
|
||||
rm.apply();
|
||||
}
|
||||
catch (Throwable th)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
assert !success : "This mutation should have failed since the CF/Table no longer exists.";
|
||||
|
||||
// write on new should work.
|
||||
rm = new RowMutation(newKsName, dk.key);
|
||||
rm.add(new QueryPath(cfName, null, ByteBufferUtil.bytes("col0")), ByteBufferUtil.bytes("newvalue"), 2L);
|
||||
rm.apply();
|
||||
store = Table.open(newKs.name).getColumnFamilyStore(cfName);
|
||||
assert store != null;
|
||||
store.forceBlockingFlush();
|
||||
|
||||
// read on new should work.
|
||||
SortedSet<ByteBuffer> cols = new TreeSet<ByteBuffer>(BytesType.instance);
|
||||
cols.add(ByteBufferUtil.bytes("col0"));
|
||||
cols.add(ByteBufferUtil.bytes("col1"));
|
||||
ColumnFamily cfam = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath(cfName), cols));
|
||||
assert cfam.getColumnCount() == cols.size();
|
||||
// tests new write.
|
||||
|
||||
ByteBuffer val = cfam.getColumn(ByteBufferUtil.bytes("col0")).value();
|
||||
assertEquals(ByteBufferUtil.string(val), "newvalue");
|
||||
// tests old write.
|
||||
val = cfam.getColumn(ByteBufferUtil.bytes("col1")).value();
|
||||
assertEquals(ByteBufferUtil.string(val), "value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createEmptyKsAddNewCf() throws ConfigurationException, IOException, ExecutionException, InterruptedException
|
||||
{
|
||||
assert DatabaseDescriptor.getTableDefinition("EmptyKeyspace") == null;
|
||||
|
||||
KSMetaData newKs = new KSMetaData("EmptyKeyspace", SimpleStrategy.class, null, 5);
|
||||
|
||||
new AddKeyspace(newKs).apply();
|
||||
assert DatabaseDescriptor.getTableDefinition("EmptyKeyspace") != null;
|
||||
|
||||
CFMetaData newCf = addTestCF("EmptyKeyspace", "AddedLater", "A new CF to add to an empty KS");
|
||||
|
||||
//should not exist until apply
|
||||
assert !DatabaseDescriptor.getTableDefinition(newKs.name).cfMetaData().containsKey(newCf.cfName);
|
||||
|
||||
//add the new CF to the empty space
|
||||
new AddColumnFamily(newCf).apply();
|
||||
|
||||
assert DatabaseDescriptor.getTableDefinition(newKs.name).cfMetaData().containsKey(newCf.cfName);
|
||||
assert DatabaseDescriptor.getTableDefinition(newKs.name).cfMetaData().get(newCf.cfName).equals(newCf);
|
||||
|
||||
// now read and write to it.
|
||||
DecoratedKey dk = Util.dk("key0");
|
||||
RowMutation rm = new RowMutation(newKs.name, dk.key);
|
||||
rm.add(new QueryPath(newCf.cfName, null, ByteBufferUtil.bytes("col0")), ByteBufferUtil.bytes("value0"), 1L);
|
||||
rm.apply();
|
||||
ColumnFamilyStore store = Table.open(newKs.name).getColumnFamilyStore(newCf.cfName);
|
||||
assert store != null;
|
||||
store.forceBlockingFlush();
|
||||
|
||||
ColumnFamily cfam = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath(newCf.cfName), ByteBufferUtil.bytes("col0")));
|
||||
assert cfam.getColumn(ByteBufferUtil.bytes("col0")) != null;
|
||||
IColumn col = cfam.getColumn(ByteBufferUtil.bytes("col0"));
|
||||
assert ByteBufferUtil.bytes("value0").equals(col.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateKeyspace() throws ConfigurationException, IOException, ExecutionException, InterruptedException
|
||||
{
|
||||
// create a keyspace to serve as existing.
|
||||
CFMetaData cf = addTestCF("UpdatedKeyspace", "AddedStandard1", "A new cf for a new ks");
|
||||
KSMetaData oldKs = new KSMetaData(cf.tableName, SimpleStrategy.class, null, 5, cf);
|
||||
|
||||
new AddKeyspace(oldKs).apply();
|
||||
|
||||
assert DatabaseDescriptor.getTableDefinition(cf.tableName) != null;
|
||||
assert DatabaseDescriptor.getTableDefinition(cf.tableName) == oldKs;
|
||||
|
||||
// anything with cf defs should fail.
|
||||
CFMetaData cf2 = addTestCF(cf.tableName, "AddedStandard2", "A new cf for a new ks");
|
||||
KSMetaData newBadKs = new KSMetaData(cf.tableName, SimpleStrategy.class, null, 4, cf2);
|
||||
try
|
||||
{
|
||||
new UpdateKeyspace(newBadKs).apply();
|
||||
throw new AssertionError("Should not have been able to update a KS with a KS that described column families.");
|
||||
}
|
||||
catch (ConfigurationException ex)
|
||||
{
|
||||
// expected.
|
||||
}
|
||||
|
||||
// names should match.
|
||||
KSMetaData newBadKs2 = new KSMetaData(cf.tableName + "trash", SimpleStrategy.class, null, 4);
|
||||
try
|
||||
{
|
||||
new UpdateKeyspace(newBadKs2).apply();
|
||||
throw new AssertionError("Should not have been able to update a KS with an invalid KS name.");
|
||||
}
|
||||
catch (ConfigurationException ex)
|
||||
{
|
||||
// expected.
|
||||
}
|
||||
|
||||
KSMetaData newKs = new KSMetaData(cf.tableName, OldNetworkTopologyStrategy.class, null, 1);
|
||||
new UpdateKeyspace(newKs).apply();
|
||||
|
||||
KSMetaData newFetchedKs = DatabaseDescriptor.getKSMetaData(newKs.name);
|
||||
assert newFetchedKs.replicationFactor == newKs.replicationFactor;
|
||||
assert newFetchedKs.replicationFactor != oldKs.replicationFactor;
|
||||
assert newFetchedKs.strategyClass.equals(newKs.strategyClass);
|
||||
assert !newFetchedKs.strategyClass.equals(oldKs.strategyClass);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateColumnFamilyNoIndexes() throws ConfigurationException, IOException, ExecutionException, InterruptedException
|
||||
{
|
||||
// create a keyspace with a cf to update.
|
||||
CFMetaData cf = addTestCF("UpdatedCfKs", "Standard1added", "A new cf that will be updated");
|
||||
KSMetaData ksm = new KSMetaData(cf.tableName, SimpleStrategy.class, null, 1, cf);
|
||||
new AddKeyspace(ksm).apply();
|
||||
|
||||
assert DatabaseDescriptor.getTableDefinition(cf.tableName) != null;
|
||||
assert DatabaseDescriptor.getTableDefinition(cf.tableName) == ksm;
|
||||
assert DatabaseDescriptor.getCFMetaData(cf.tableName, cf.cfName) != null;
|
||||
|
||||
// updating certain fields should fail.
|
||||
org.apache.cassandra.avro.CfDef cf_def = CFMetaData.convertToAvro(cf);
|
||||
cf_def.row_cache_size = 43.3;
|
||||
cf_def.column_metadata = new ArrayList<org.apache.cassandra.avro.ColumnDef>();
|
||||
cf_def.default_validation_class ="BytesType";
|
||||
cf_def.min_compaction_threshold = 5;
|
||||
cf_def.max_compaction_threshold = 31;
|
||||
|
||||
// test valid operations.
|
||||
cf_def.comment = "Modified comment";
|
||||
new UpdateColumnFamily(cf_def).apply(); // doesn't get set back here.
|
||||
|
||||
cf_def.row_cache_size = 2d;
|
||||
new UpdateColumnFamily(cf_def).apply();
|
||||
|
||||
cf_def.key_cache_size = 3d;
|
||||
new UpdateColumnFamily(cf_def).apply();
|
||||
|
||||
cf_def.read_repair_chance = 0.23;
|
||||
new UpdateColumnFamily(cf_def).apply();
|
||||
|
||||
cf_def.gc_grace_seconds = 12;
|
||||
new UpdateColumnFamily(cf_def).apply();
|
||||
|
||||
cf_def.default_validation_class = "UTF8Type";
|
||||
new UpdateColumnFamily(cf_def).apply();
|
||||
|
||||
cf_def.min_compaction_threshold = 3;
|
||||
new UpdateColumnFamily(cf_def).apply();
|
||||
|
||||
cf_def.max_compaction_threshold = 33;
|
||||
new UpdateColumnFamily(cf_def).apply();
|
||||
|
||||
// can't test changing the reconciler because there is only one impl.
|
||||
|
||||
// check the cumulative affect.
|
||||
assert DatabaseDescriptor.getCFMetaData(cf.tableName, cf.cfName).getComment().equals(cf_def.comment);
|
||||
assert DatabaseDescriptor.getCFMetaData(cf.tableName, cf.cfName).getRowCacheSize() == cf_def.row_cache_size;
|
||||
assert DatabaseDescriptor.getCFMetaData(cf.tableName, cf.cfName).getKeyCacheSize() == cf_def.key_cache_size;
|
||||
assert DatabaseDescriptor.getCFMetaData(cf.tableName, cf.cfName).getReadRepairChance() == cf_def.read_repair_chance;
|
||||
assert DatabaseDescriptor.getCFMetaData(cf.tableName, cf.cfName).getGcGraceSeconds() == cf_def.gc_grace_seconds;
|
||||
assert DatabaseDescriptor.getCFMetaData(cf.tableName, cf.cfName).getDefaultValidator() == UTF8Type.instance;
|
||||
|
||||
// todo: we probably don't need to reset old values in the catches anymore.
|
||||
// make sure some invalid operations fail.
|
||||
int oldId = cf_def.id;
|
||||
try
|
||||
{
|
||||
cf_def.id++;
|
||||
cf.apply(cf_def);
|
||||
throw new AssertionError("Should have blown up when you used a different id.");
|
||||
}
|
||||
catch (ConfigurationException expected)
|
||||
{
|
||||
cf_def.id = oldId;
|
||||
}
|
||||
|
||||
CharSequence oldStr = cf_def.name;
|
||||
try
|
||||
{
|
||||
cf_def.name = cf_def.name + "_renamed";
|
||||
cf.apply(cf_def);
|
||||
throw new AssertionError("Should have blown up when you used a different name.");
|
||||
}
|
||||
catch (ConfigurationException expected)
|
||||
{
|
||||
cf_def.name = oldStr;
|
||||
}
|
||||
|
||||
oldStr = cf_def.keyspace;
|
||||
try
|
||||
{
|
||||
cf_def.keyspace = oldStr + "_renamed";
|
||||
cf.apply(cf_def);
|
||||
throw new AssertionError("Should have blown up when you used a different keyspace.");
|
||||
}
|
||||
catch (ConfigurationException expected)
|
||||
{
|
||||
cf_def.keyspace = oldStr;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
cf_def.column_type = ColumnFamilyType.Super.name();
|
||||
cf.apply(cf_def);
|
||||
throw new AssertionError("Should have blwon up when you used a different cf type.");
|
||||
}
|
||||
catch (ConfigurationException expected)
|
||||
{
|
||||
cf_def.column_type = ColumnFamilyType.Standard.name();
|
||||
}
|
||||
|
||||
oldStr = cf_def.comparator_type;
|
||||
try
|
||||
{
|
||||
cf_def.comparator_type = BytesType.class.getSimpleName();
|
||||
cf.apply(cf_def);
|
||||
throw new AssertionError("Should have blown up when you used a different comparator.");
|
||||
}
|
||||
catch (ConfigurationException expected)
|
||||
{
|
||||
cf_def.comparator_type = UTF8Type.class.getSimpleName();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
cf_def.min_compaction_threshold = 34;
|
||||
cf.apply(cf_def);
|
||||
throw new AssertionError("Should have blown up when min > max.");
|
||||
}
|
||||
catch (ConfigurationException expected)
|
||||
{
|
||||
cf_def.min_compaction_threshold = 3;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
cf_def.max_compaction_threshold = 2;
|
||||
cf.apply(cf_def);
|
||||
throw new AssertionError("Should have blown up when max > min.");
|
||||
}
|
||||
catch (ConfigurationException expected)
|
||||
{
|
||||
cf_def.max_compaction_threshold = 33;
|
||||
}
|
||||
}
|
||||
|
||||
private CFMetaData addTestCF(String ks, String cf, String comment)
|
||||
{
|
||||
return new CFMetaData(ks,
|
||||
cf,
|
||||
ColumnFamilyType.Standard,
|
||||
UTF8Type.instance,
|
||||
null,
|
||||
comment,
|
||||
0,
|
||||
1.0,
|
||||
0,
|
||||
CFMetaData.DEFAULT_GC_GRACE_SECONDS,
|
||||
BytesType.instance,
|
||||
CFMetaData.DEFAULT_MIN_COMPACTION_THRESHOLD,
|
||||
CFMetaData.DEFAULT_MAX_COMPACTION_THRESHOLD,
|
||||
CFMetaData.DEFAULT_ROW_CACHE_SAVE_PERIOD_IN_SECONDS,
|
||||
CFMetaData.DEFAULT_KEY_CACHE_SAVE_PERIOD_IN_SECONDS,
|
||||
CFMetaData.DEFAULT_MEMTABLE_LIFETIME_IN_MINS,
|
||||
CFMetaData.DEFAULT_MEMTABLE_THROUGHPUT_IN_MB,
|
||||
CFMetaData.DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS,
|
||||
Collections.<ByteBuffer, ColumnDefinition>emptyMap());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue