Skip to content

Commit

Permalink
Logs a compact description of druid filters.
Browse files Browse the repository at this point in the history
--A map is logged that describes the structure of the filter being sent
to druid. For each filter it includes a count of the number of instances
of that filter.
  • Loading branch information
archolewa committed Dec 8, 2016
1 parent 797cccb commit a0b798f
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2016 Yahoo Inc.
// Licensed under the terms of the Apache license. Please see LICENSE.md file distributed with this work for terms.
package com.yahoo.bard.webservice.druid.model.filter;

import java.util.List;

/**
* A Druid filter that is defined by applying an operation to at least one other filter. For example, {@code not} and
* {@code and} filters are complex. A {@code selector} filter is not.
*/
public interface ComplexFilter {

/**
* Returns the filters that are operated on by this filter.
*
* @return The filters operated on by this filter
*/
List<Filter> getFields();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Licensed under the terms of the Apache license. Please see LICENSE.md file distributed with this work for terms.
package com.yahoo.bard.webservice.druid.model.filter;

import com.fasterxml.jackson.annotation.JsonIgnore;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -53,6 +55,7 @@ public boolean equals(Object obj) {
}

@Override
@JsonIgnore
public List<Filter> getFields() {
return Collections.singletonList(getField());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2016 Yahoo Inc.
// Licensed under the terms of the Apache license. Please see LICENSE.md file distributed with this work for terms.
package com.yahoo.bard.webservice.logging.blocks;

import com.yahoo.bard.webservice.druid.model.filter.ComplexFilter;
import com.yahoo.bard.webservice.druid.model.filter.Filter;
import com.yahoo.bard.webservice.logging.LogInfo;

import com.fasterxml.jackson.annotation.JsonAutoDetect;

import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.Map;

/**
* Logs some structural data about the filter sent to Druid, without actually logging the entire (potentially massive)
* filter.
*/
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class DruidFilterInfo implements LogInfo {

protected final Map<String, Long> numEachFilterType;

/**
* Constructor.
*
* @param filter The filter that needs to be analyzed
*/
public DruidFilterInfo(Filter filter) {
numEachFilterType = buildFilterCount(filter);
}

/**
* Performs a DFS search of the filter tree, populating the specified map with the number of instances of each
* filter type appearing in the filter.
*
* @param filter The filter that needs to be traversed
*
* @return A map containing a count of each type of filter
*/
private Map<String, Long> buildFilterCount(Filter filter) {
if (filter == null) {
return Collections.emptyMap();
}
Map<String, Long> filterTypeCounter = new LinkedHashMap<>();
Deque<Filter> filterStack = new ArrayDeque<>();
filterStack.add(filter);
while (!filterStack.isEmpty()) {
Filter currentFilter = filterStack.pop();
filterTypeCounter.merge(currentFilter.getClass().getSimpleName(), 1L, (old, increment) -> old + increment);
if (currentFilter instanceof ComplexFilter) {
filterStack.addAll(((ComplexFilter) currentFilter).getFields());
}
}
return filterTypeCounter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.yahoo.bard.webservice.logging.RequestLog;
import com.yahoo.bard.webservice.logging.blocks.BardQueryInfo;
import com.yahoo.bard.webservice.logging.blocks.DataRequest;
import com.yahoo.bard.webservice.logging.blocks.DruidFilterInfo;
import com.yahoo.bard.webservice.table.Table;
import com.yahoo.bard.webservice.util.Either;
import com.yahoo.bard.webservice.web.DataApiRequest;
Expand Down Expand Up @@ -407,6 +408,7 @@ public void getData(

RequestLog.switchTiming("logRequestMetrics");
logRequestMetrics(apiRequest, readCache, druidQuery);
RequestLog.record(new DruidFilterInfo(apiRequest.getFilter()));
RequestLog.switchTiming(REQUEST_WORKFLOW_TIMER);

// Process the request
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.yahoo.bard.webservice.logging.blocks

import com.yahoo.bard.webservice.data.dimension.Dimension
import com.yahoo.bard.webservice.druid.model.filter.AndFilter
import com.yahoo.bard.webservice.druid.model.filter.Filter
import com.yahoo.bard.webservice.druid.model.filter.NotFilter
import com.yahoo.bard.webservice.druid.model.filter.OrFilter
import com.yahoo.bard.webservice.druid.model.filter.SelectorFilter

import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll

class DruidFilterInfoSpec extends Specification {

@Shared Filter selector = new SelectorFilter(Stub(Dimension), "value")

static final String SELECTOR_NAME = SelectorFilter.class.simpleName
static final String AND_NAME = AndFilter.class.simpleName
static final String OR_NAME = OrFilter.class.simpleName
static final String NOT_NAME = NotFilter.class.simpleName

@Unroll
def "The DruidFilterInfo correctly analyzes #filter"() {
expect:
new DruidFilterInfo(filter).numEachFilterType == expectedMap

where:
filter | expectedMap
null | [:]
selector | [(SELECTOR_NAME): 1L]
new AndFilter([selector, selector]) | [(AND_NAME): 1L, (SELECTOR_NAME): 2L]
new NotFilter(new AndFilter([selector, selector])) | [(NOT_NAME): 1L, (AND_NAME): 1L, (SELECTOR_NAME): 2L]
new OrFilter([new AndFilter([selector, selector]), new NotFilter(new AndFilter([selector, selector]))]) | [(OR_NAME): 1L, (AND_NAME): 2L, (NOT_NAME): 1L, (SELECTOR_NAME): 4L]
}
}

0 comments on commit a0b798f

Please sign in to comment.