blob: 6d3cd92f4a5c07984b18e7956ff5b88b0da1ba5f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. 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. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*/
package org.apache.usergrid.persistence.index;
import org.apache.usergrid.persistence.index.query.ParsedQuery;
import org.apache.usergrid.persistence.index.query.tree.Operand;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class QueryAnalyzer {
public static final String v_predicate_count = "sort_predicate_count_exceeded";
public static final String v_operand_count = "operand_count_exceeded";
public static final String v_large_collection = "large_collection_size_bytes";
public static final String v_large_index = "large_index_size_bytes";
public static final String v_full_collection_sort = "full_collection_sort";
public static final String k_violation = "violation";
public static final String k_limit = "limit";
public static final String k_actual = "actual";
public static List<Map<String, Object>> analyze(final ParsedQuery parsedQuery, final long collectionSizeInBytes,
final long indexSizeInBytes, final IndexFig indexFig ) {
List<Map<String, Object>> violations = new ArrayList<>();
// get configured breaker values
final int errorPredicateCount = indexFig.getQueryBreakerErrorSortPredicateCount();
final int errorOperandCount = indexFig.getQueryBreakerErrorOperandCount();
final long errorCollectionSizeBytes = indexFig.getQueryBreakerErrorCollectionSizeBytes();
final long errorIndexSizeBytes = indexFig.getQueryBreakerErrorIndexSizeBytes();
// get the actual values to compare against the configured enforcement values
int queryPredicatesSize = parsedQuery.getSortPredicates().size();
int queryOperandCount = getTotalChildCount(parsedQuery.getRootOperand());
// large indexes can cause issues, this is never returned from the API and only logged
if( indexSizeInBytes > errorIndexSizeBytes ){
violations.add(new HashMap<String, Object>(3){{
put(k_violation, v_large_index);
put(k_limit, errorIndexSizeBytes);
put(k_actual, indexSizeInBytes);
}});
}
// large collections mean that sorts and other complex queries can impact the query service (Elasticsearch)
if (collectionSizeInBytes > errorCollectionSizeBytes ){
violations.add(new HashMap<String, Object>(3){{
put(k_violation, v_large_collection);
put(k_limit, errorCollectionSizeBytes);
put(k_actual, collectionSizeInBytes);
}});
// query like "select * order by created asc"
if(parsedQuery.getSelectFieldMappings().size() < 1 &&
!parsedQuery.getOriginalQuery().toLowerCase().contains("where") &&
parsedQuery.getSortPredicates().size() > 0 ){
violations.add(new HashMap<String, Object>(3){{
put(k_violation, v_full_collection_sort);
put(k_limit, null);
put(k_actual, null);
}});
}
}
// complex queries can be determined from the # of operands and sort predicates
if ( queryPredicatesSize > errorPredicateCount){
violations.add(new HashMap<String, Object>(3){{
put(k_violation, v_predicate_count);
put(k_limit, errorPredicateCount);
put(k_actual, queryPredicatesSize);
}});
}
if (queryOperandCount > errorOperandCount){
violations.add(new HashMap<String, Object>(3){{
put(k_violation, v_operand_count);
put(k_limit, errorOperandCount);
put(k_actual, queryOperandCount);
}});
}
return violations;
}
public static String violationsAsString(List<Map<String, Object>> violations, String originalQuery){
final StringBuilder logMessage = new StringBuilder();
logMessage.append( "QueryAnalyzer Violations Detected [").append(violations.size()).append("]: [" );
violations.forEach(violation -> {
final StringBuilder violationMessage = new StringBuilder();
violation.forEach((k,v) -> {
violationMessage.append(k).append(":").append(v).append(",");
});
violationMessage.deleteCharAt(violationMessage.length()-1);
logMessage.append(" (").append(violationMessage).append(") ");
});
logMessage.append("]");
logMessage.append(" [Original Query: ").append(originalQuery).append("]");
return logMessage.toString();
}
private static int getTotalChildCount(Operand rootOperand){
int count = 0;
if( rootOperand != null) {
count ++;
if (rootOperand.getChildren() != null) {
for (Object child : rootOperand.getChildren()) {
if (child instanceof Operand) {
count += getTotalChildCount((Operand) child);
}
}
}
}
return count;
}
}