/**
* 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.stress;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.cassandra.stress.generatedata.KeyGen;
import org.apache.cassandra.stress.generatedata.RowGen;
import org.apache.cassandra.stress.settings.Command;
import org.apache.cassandra.stress.settings.CqlVersion;
import org.apache.cassandra.stress.settings.SettingsCommandMixed;
import org.apache.cassandra.stress.settings.StressSettings;
import org.apache.cassandra.stress.util.JavaDriverClient;
import org.apache.cassandra.stress.util.ThriftClient;
import org.apache.cassandra.stress.util.Timer;
import org.apache.cassandra.thrift.ColumnParent;
import org.apache.cassandra.thrift.InvalidRequestException;
import org.apache.cassandra.transport.SimpleClient;
import org.apache.cassandra.utils.ByteBufferUtil;
public abstract class Operation
{
public final long index;
protected final State state;
public Operation(State state, long idx)
{
index = idx;
this.state = state;
}
public static interface RunOp
{
public boolean run() throws Exception;
public String key();
public int keyCount();
}
// one per thread!
public static final class State
{
public final StressSettings settings;
public final Timer timer;
public final Command type;
public final KeyGen keyGen;
public final RowGen rowGen;
public final List<ColumnParent> columnParents;
public final StressMetrics metrics;
public final SettingsCommandMixed.CommandSelector readWriteSelector;
private Object cqlCache;
public State(Command type, StressSettings settings, StressMetrics metrics)
{
this.type = type;
this.timer = metrics.getTiming().newTimer();
if (type == Command.MIXED)
readWriteSelector = ((SettingsCommandMixed) settings.command).selector();
else
readWriteSelector = null;
this.settings = settings;
this.keyGen = settings.keys.newKeyGen();
this.rowGen = settings.columns.newRowGen();
this.metrics = metrics;
if (!settings.columns.useSuperColumns)
columnParents = Collections.singletonList(new ColumnParent(settings.schema.columnFamily));
else
{
ColumnParent[] cp = new ColumnParent[settings.columns.superColumns];
for (int i = 0 ; i < cp.length ; i++)
cp[i] = new ColumnParent("Super1").setSuper_column(ByteBufferUtil.bytes("S" + i));
columnParents = Arrays.asList(cp);
}
}
public boolean isCql3()
{
return settings.mode.cqlVersion == CqlVersion.CQL3;
}
public boolean isCql2()
{
return settings.mode.cqlVersion == CqlVersion.CQL2;
}
public Object getCqlCache()
{
return cqlCache;
}
public void storeCqlCache(Object val)
{
cqlCache = val;
}
}
protected ByteBuffer getKey()
{
return state.keyGen.getKeys(1, index).get(0);
}
protected List<ByteBuffer> getKeys(int count)
{
return state.keyGen.getKeys(count, index);
}
protected List<ByteBuffer> generateColumnValues()
{
return state.rowGen.generate(index);
}
/**
* Run operation
* @param client Cassandra Thrift client connection
* @throws IOException on any I/O error.
*/
public abstract void run(ThriftClient client) throws IOException;
public void run(SimpleClient client) throws IOException {
throw new UnsupportedOperationException();
}
public void run(JavaDriverClient client) throws IOException {
throw new UnsupportedOperationException();
}
public void timeWithRetry(RunOp run) throws IOException
{
state.timer.start();
boolean success = false;
String exceptionMessage = null;
for (int t = 0; t < state.settings.command.tries; t++)
{
if (success)
break;
try
{
success = run.run();
}
catch (Exception e)
{
System.err.println(e);
exceptionMessage = getExceptionMessage(e);
success = false;
}
}
state.timer.stop(run.keyCount());
if (!success)
{
error(String.format("Operation [%d] retried %d times - error executing for key %s %s%n",
index,
state.settings.command.tries,
run.key(),
(exceptionMessage == null) ? "" : "(" + exceptionMessage + ")"));
}
}
protected String getExceptionMessage(Exception e)
{
String className = e.getClass().getSimpleName();
String message = (e instanceof InvalidRequestException) ? ((InvalidRequestException) e).getWhy() : e.getMessage();
return (message == null) ? "(" + className + ")" : String.format("(%s): %s", className, message);
}
protected void error(String message) throws IOException
{
if (!state.settings.command.ignoreErrors)
throw new IOException(message);
else
System.err.println(message);
}
public static ByteBuffer getColumnNameBytes(int i)
{
return ByteBufferUtil.bytes("C" + i);
}
public static String getColumnName(int i)
{
return "C" + i;
}
}