package org.radargun.stages.query;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.radargun.config.Property;
import org.radargun.logging.Log;
import org.radargun.logging.LogFactory;
import org.radargun.reporting.Report;
import org.radargun.traits.InternalsExposition;
import org.radargun.traits.Query;
import org.radargun.traits.Queryable;
import org.radargun.utils.MinMax;
/**
* Logic for creating the query builders and retrieving query results.
*
* @author Radim Vansa <rvansa@redhat.com>
*/
public class QueryBase {
private static final Log log = LogFactory.getLog(QueryBase.class);
@Property(doc = "Check whether all invocations got the same result, and fail if not. Default is false.")
private boolean checkSameResult = false;
@Property(doc = "Number of queries generated. Makes sense only when the conditions contain random data. Default is 1.")
private int numQueries = 1;
@Property(doc = "Full names of the attribute queried from InternalsExposition. Expecting values parse-able as long values. Default are none.")
private List<String> exposedAttributes = Collections.EMPTY_LIST;
protected Query.Builder[] builders;
private AtomicInteger minResultSize = new AtomicInteger(-1);
private AtomicInteger maxResultSize = new AtomicInteger(-1);
public void init(Queryable queryable, QueryConfiguration query) {
if (queryable == null) {
return; // called from master, ignore
}
builders = new Query.Builder[numQueries];
for (int i = 0; i < numQueries; ++i) {
builders[i] = constructBuilder(queryable, query);
}
}
public static Query.Builder constructBuilder(Queryable queryable, QueryConfiguration query) {
Class<?> clazz;
try {
clazz = Class.forName(query.clazz);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Cannot load class " + query.clazz, e);
}
Query.Builder builder = queryable.getBuilder(null, clazz);
if (query.conditions != null) {
for (Condition condition : query.conditions) {
condition.apply(builder);
}
}
if (query.orderBy != null) {
for (OrderBy ob : query.orderBy) {
builder.orderBy(new Query.SelectExpression(ob.attribute, ob.asc));
}
} else if (query.orderByAggregatedColumns != null) {
for (Condition.OrderedSelectExpressionElement orderByAggregatedColumn : query.orderByAggregatedColumns) {
builder.orderBy(orderByAggregatedColumn.toSelectExpression());
}
}
if (query.projection != null) {
Query.SelectExpression[] projections = new Query.SelectExpression[query.projection.length];
for (int i = 0; i < query.projection.length; i++) {
projections[i] = new Query.SelectExpression(query.projection[i]);
}
builder.projection(projections);
} else if (query.projectionAggregated != null && !query.projectionAggregated.isEmpty()) {
Query.SelectExpression[] projections = new Query.SelectExpression[query.projectionAggregated.size()];
for (int i = 0; i < query.projectionAggregated.size(); i++) {
projections[i] = query.projectionAggregated.get(i).toSelectExpression();
}
builder.projection(projections);
}
if (query.groupBy != null) {
builder.groupBy(query.groupBy);
if (query.having != null) {
for (Condition condition : query.having) {
condition.apply(builder);
}
}
}
if (query.offset >= 0) {
builder.offset(query.offset);
}
if (query.limit >= 0) {
builder.limit(query.limit);
}
return builder;
}
public QueryBase.Data createQueryData(InternalsExposition internalsExposition) {
Map<String, String> exposedAttributeValues = new HashMap<>();
if (internalsExposition != null) {
for (String attribute : exposedAttributes) {
internalsExposition.getCustomStatistics(attribute);
}
}
return new QueryBase.Data(minResultSize.get(), maxResultSize.get(), exposedAttributeValues);
}
private long parse(String str) {
if (str == null) return 0;
return Long.parseLong(str);
}
public void checkAndRecordResults(Map<Integer, Data> results, Report.Test test, int iteration) {
MinMax.Int totalResultSize = new MinMax.Int();
Map<Integer, Report.SlaveResult> slaveResultSizes = new HashMap<>();
Map<String, Map<Integer, Report.SlaveResult>> exposedResults = new HashMap<>();
Map<String, MinMax.Long> exposedAggregations = new HashMap<>();
for (Map.Entry<Integer, QueryBase.Data> entry : results.entrySet()) {
int slaveIndex = entry.getKey();
QueryBase.Data data = entry.getValue();
if (totalResultSize.isSet() && (totalResultSize.min() != data.minResultSize || totalResultSize.max() != data.maxResultSize)) {
String message = String.format("The size got from %d -> %d .. %d is not the same as from other slaves -> %d .. %d ",
slaveIndex, data.minResultSize, data.maxResultSize, totalResultSize.min(), totalResultSize.max());
if (checkSameResult) {
throw new IllegalStateException(message);
} else {
log.info(message);
}
}
for (String attribute : exposedAttributes) {
String value = data.exposedAttributeValues.get(attribute);
if (value == null) continue;
Map<Integer, Report.SlaveResult> slaveResult = exposedResults.get(attribute);
if (slaveResult == null) {
exposedResults.put(attribute, slaveResult = new HashMap<>());
}
slaveResult.put(slaveIndex, new Report.SlaveResult(value, false));
MinMax.Long aggreg = exposedAggregations.get(attribute);
if (aggreg == null) {
exposedAggregations.put(attribute, aggreg = new MinMax.Long());
}
try {
aggreg.add(parse(value));
} catch (NumberFormatException e) {
aggreg.add(0);
}
}
}
if (test != null) {
test.addResult(iteration, new Report.TestResult("Query result size", slaveResultSizes, totalResultSize.toString(), false));
for (Map.Entry<String, Map<Integer, Report.SlaveResult>> entry : exposedResults.entrySet()) {
test.addResult(iteration, new Report.TestResult("Exposed: " + entry.getKey(),
entry.getValue(), exposedAggregations.get(entry.getKey()).toString(), false));
}
} else {
log.info("No test name - results are not recorded");
}
}
public int getMinResultSize() {
return minResultSize.get();
}
public void updateMinResultSize(int size) {
int min = minResultSize.get();
while ((min < 0 || min > size) && !minResultSize.compareAndSet(min, size)) {
min = minResultSize.get();
}
}
public int getMaxResultSize() {
return maxResultSize.get();
}
public void updateMaxResultSize(int size) {
int max = maxResultSize.get();
while ((max < 0 || max < size) && !maxResultSize.compareAndSet(max, size)) {
max = maxResultSize.get();
}
}
public boolean isCheckSameResult() {
return checkSameResult;
}
public int getNumQueries() {
return numQueries;
}
public Query buildQuery(int queryNumber) {
return builders[queryNumber].build();
}
public static class Data implements Serializable {
public final int minResultSize;
public final int maxResultSize;
public final Map<String, String> exposedAttributeValues;
public Data(int minResultSize, int maxResultSize, Map<String, String> exposedAttributeValues) {
this.minResultSize = minResultSize;
this.maxResultSize = maxResultSize;
this.exposedAttributeValues = exposedAttributeValues;
}
}
}