blob: 79d8d25312eedfc16dc45092b2d38a8d0bb75a21 [file] [log] [blame]
package org.apache.cassandra.stress.settings;
/*
*
* 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.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.util.*;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.stress.generate.Distribution;
import org.apache.cassandra.stress.generate.DistributionFactory;
import org.apache.cassandra.stress.generate.DistributionFixed;
import org.apache.cassandra.stress.util.ResultLogger;
import org.apache.cassandra.utils.ByteBufferUtil;
/**
* For parsing column options
*/
public class SettingsColumn implements Serializable
{
public final int maxColumnsPerKey;
public transient List<ByteBuffer> names;
public final List<String> namestrs;
public final String comparator;
public final String timestamp;
public final boolean variableColumnCount;
public final boolean slice;
public final DistributionFactory sizeDistribution;
public final DistributionFactory countDistribution;
public SettingsColumn(GroupedOptions options)
{
this((Options) options,
options instanceof NameOptions ? (NameOptions) options : null,
options instanceof CountOptions ? (CountOptions) options : null
);
}
public SettingsColumn(Options options, NameOptions name, CountOptions count)
{
sizeDistribution = options.size.get();
{
timestamp = options.timestamp.value();
comparator = options.comparator.value();
AbstractType parsed = null;
try
{
parsed = TypeParser.parse(comparator);
}
catch (Exception e)
{
System.err.println(e.getMessage());
System.exit(1);
}
if (!(parsed instanceof TimeUUIDType || parsed instanceof AsciiType || parsed instanceof UTF8Type))
{
System.err.println("Currently supported types are: TimeUUIDType, AsciiType, UTF8Type.");
System.exit(1);
}
}
if (name != null)
{
assert count == null;
AbstractType comparator;
try
{
comparator = TypeParser.parse(this.comparator);
} catch (Exception e)
{
throw new IllegalArgumentException(this.comparator + " is not a valid type");
}
final String[] names = name.name.value().split(",");
this.names = new ArrayList<>(names.length);
for (String columnName : names)
this.names.add(comparator.fromString(columnName));
Collections.sort(this.names, BytesType.instance);
this.namestrs = new ArrayList<>();
for (ByteBuffer columnName : this.names)
this.namestrs.add(comparator.getString(columnName));
final int nameCount = this.names.size();
countDistribution = new DistributionFactory()
{
@Override
public Distribution get()
{
return new DistributionFixed(nameCount);
}
@Override
public String getConfigAsString(){return String.format("Count: fixed=%d", nameCount);}
};
}
else
{
this.countDistribution = count.count.get();
ByteBuffer[] names = new ByteBuffer[(int) countDistribution.get().maxValue()];
String[] namestrs = new String[(int) countDistribution.get().maxValue()];
for (int i = 0 ; i < names.length ; i++)
names[i] = ByteBufferUtil.bytes("C" + i);
Arrays.sort(names, BytesType.instance);
try
{
for (int i = 0 ; i < names.length ; i++)
namestrs[i] = ByteBufferUtil.string(names[i]);
}
catch (CharacterCodingException e)
{
throw new RuntimeException(e);
}
this.names = Arrays.asList(names);
this.namestrs = Arrays.asList(namestrs);
}
maxColumnsPerKey = (int) countDistribution.get().maxValue();
variableColumnCount = countDistribution.get().minValue() < maxColumnsPerKey;
slice = options.slice.setByUser();
}
// Option Declarations
private static abstract class Options extends GroupedOptions
{
final OptionSimple superColumns = new OptionSimple("super=", "[0-9]+", "0", "Number of super columns to use (no super columns used if not specified)", false);
final OptionSimple comparator = new OptionSimple("comparator=", "TimeUUIDType|AsciiType|UTF8Type", "AsciiType", "Column Comparator to use", false);
final OptionSimple slice = new OptionSimple("slice", "", null, "If set, range slices will be used for reads, otherwise a names query will be", false);
final OptionSimple timestamp = new OptionSimple("timestamp=", "[0-9]+", null, "If set, all columns will be written with the given timestamp", false);
final OptionDistribution size = new OptionDistribution("size=", "FIXED(34)", "Cell size distribution");
}
private static final class NameOptions extends Options
{
final OptionSimple name = new OptionSimple("names=", ".*", null, "Column names", true);
@Override
public List<? extends Option> options()
{
return Arrays.asList(name, slice, superColumns, comparator, timestamp, size);
}
}
private static final class CountOptions extends Options
{
final OptionDistribution count = new OptionDistribution("n=", "FIXED(5)", "Cell count distribution, per operation");
@Override
public List<? extends Option> options()
{
return Arrays.asList(count, slice, superColumns, comparator, timestamp, size);
}
}
// CLI Utility Methods
public void printSettings(ResultLogger out)
{
out.printf(" Max Columns Per Key: %d%n",maxColumnsPerKey);
out.printf(" Column Names: %s%n",namestrs);
out.printf(" Comparator: %s%n", comparator);
out.printf(" Timestamp: %s%n", timestamp);
out.printf(" Variable Column Count: %b%n", variableColumnCount);
out.printf(" Slice: %b%n", slice);
if (sizeDistribution != null){
out.println(" Size Distribution: " + sizeDistribution.getConfigAsString());
};
if (sizeDistribution != null){
out.println(" Count Distribution: " + countDistribution.getConfigAsString());
};
}
static SettingsColumn get(Map<String, String[]> clArgs)
{
String[] params = clArgs.remove("-col");
if (params == null)
return new SettingsColumn(new CountOptions());
GroupedOptions options = GroupedOptions.select(params, new NameOptions(), new CountOptions());
if (options == null)
{
printHelp();
System.out.println("Invalid -col options provided, see output for valid options");
System.exit(1);
}
return new SettingsColumn(options);
}
static void printHelp()
{
GroupedOptions.printOptions(System.out, "-col", new NameOptions(), new CountOptions());
}
static Runnable helpPrinter()
{
return new Runnable()
{
@Override
public void run()
{
printHelp();
}
};
}
/* Custom serializaiton invoked here to make legacy thrift based table creation work with StressD. This code requires
* the names attribute to be populated. Since the names attribute is set as a List[ByteBuffer] we switch it
* to an array on the way out and back to a buffer when it's being read in.
*/
private void writeObject(ObjectOutputStream oos) throws IOException
{
oos.defaultWriteObject();
ArrayList<byte[]> namesBytes = new ArrayList<>();
for (ByteBuffer buffer : this.names)
namesBytes.add(ByteBufferUtil.getArray(buffer));
oos.writeObject(namesBytes);
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException
{
ois.defaultReadObject();
List<ByteBuffer> namesBuffer = new ArrayList<>();
List<byte[]> namesBytes = (List<byte[]>) ois.readObject();
for (byte[] bytes : namesBytes)
namesBuffer.add(ByteBuffer.wrap(bytes));
this.names = new ArrayList<>(namesBuffer);
}
}