granularity method in QueryMetrics. (#4570)

* granularity method in QueryMetrics.

PR to emit granularity dimension for timeseries, search, groupBy,
select and topN queries.

* QueryMetricsFactory classes for search and select queries.

* Empty implementation  for  Granularity() method.

* Review comment changes.

* Remove unused import.

* empty query() method.

* checkstyle fix.

* Import fix.
This commit is contained in:
Akash Dwivedi 2017-10-04 09:42:52 -07:00 committed by cheddar
parent 07aa405a6f
commit 2ee32399ff
21 changed files with 1021 additions and 36 deletions

View File

@ -22,6 +22,7 @@ package io.druid.query;
import com.metamx.emitter.service.ServiceEmitter;
import io.druid.collections.bitmap.BitmapFactory;
import io.druid.query.filter.Filter;
import io.druid.query.search.SearchQueryMetricsFactory;
import org.joda.time.Interval;
import java.util.List;
@ -110,30 +111,35 @@ import java.util.List;
*
* Making subinterfaces of QueryMetrics for emitting custom dimensions and/or metrics for specific query types
* -----------------------------------------------------------------------------------------------------------
* If a query type (e. g. {@link io.druid.query.search.SearchQuery} (it's runners) needs to emit custom
* dimensions and/or metrics which doesn't make sense for all other query types, the following steps should be executed:
* 1. Create `interface SearchQueryMetrics extends QueryMetrics` (here and below "Search" is the query type) with
* additional methods (see "Adding new methods" section above).
* If a query type (e. g. {@link io.druid.query.metadata.metadata.SegmentMetadataQuery} (it's runners) needs to emit
* custom dimensions and/or metrics which doesn't make sense for all other query types, the following steps should be
* executed:
*
* 2. Create `class DefaultSearchQueryMetrics implements SearchQueryMetrics`. This class should implement extra methods
* from SearchQueryMetrics interfaces with empty bodies, AND DELEGATE ALL OTHER METHODS TO A QueryMetrics OBJECT,
* provided as a sole parameter in DefaultSearchQueryMetrics constructor.
* 1. Create `interface SegmentMetadataQueryMetrics extends QueryMetrics` (here and below "SegmentMetadata" is the
* query type) with additional methods (see "Adding new methods" section above).
*
* 3. Create `interface SearchQueryMetricsFactory` with a single method
* `SearchQueryMetrics makeMetrics(SearchQuery query);`.
* 2. Create `class DefaultSegmentMetadataQueryMetrics implements SegmentMetadataQueryMetrics`. This class should
* implement extra methods from SegmentMetadataQueryMetrics interfaces with empty bodies, AND DELEGATE ALL OTHER
* METHODS TO A QueryMetrics OBJECT, provided as a sole parameter in DefaultSegmentMetadataQueryMetrics constructor.
*
* 4. Create `class DefaultSearchQueryMetricsFactory implements SearchQueryMetricsFactory`, which accepts {@link
* GenericQueryMetricsFactory} as injected constructor parameter, and implements makeMetrics() as
* `return new DefaultSearchQueryMetrics(genericQueryMetricsFactory.makeMetrics(query));`
* NOTE: query(), dataSource(), queryType(), interval(), hasFilters(), duration() and queryId() methods or any
* "pre-query-execution-time" methods should either have a empty body or throw exception.
*
* 5. Inject and use SearchQueryMetricsFactory instead of {@link GenericQueryMetricsFactory} in {@link
* io.druid.query.search.SearchQueryQueryToolChest}.
* 3. Create `interface SegmentMetadataQueryMetricsFactory` with a single method
* `SegmentMetadataQueryMetrics makeMetrics(SegmentMetadataQuery query);`.
*
* 6. Establish injection of SearchQueryMetricsFactory using config and provider method in QueryToolChestModule
* (see how it is done in QueryToolChestModule for existing query types with custom metrics, e. g. {@link
* io.druid.query.topn.TopNQueryMetricsFactory}), if the query type belongs to the core druid-processing, e. g.
* SearchQuery. If the query type defined in an extension, you can specify
* `binder.bind(ScanQueryMetricsFactory.class).to(DefaultScanQueryMetricsFactory.class)` in the extension's
* 4. Create `class DefaultSegmentMetadataQueryMetricsFactory implements SegmentMetadataQueryMetricsFactory`,
* which accepts {@link GenericQueryMetricsFactory} as injected constructor parameter, and implements makeMetrics() as
* `return new DefaultSegmentMetadataQueryMetrics(genericQueryMetricsFactory.makeMetrics(query));`
*
* 5. Inject and use SegmentMetadataQueryMetricsFactory instead of {@link GenericQueryMetricsFactory} in
* {@link io.druid.query.metadata.SegmentMetadataQueryQueryToolChest}.
*
* 6. Establish injection of SegmentMetadataQueryMetricsFactory using config and provider method in
* QueryToolChestModule (see how it is done in {@link io.druid.guice.QueryToolChestModule} for existing query types
* with custom metrics, e. g. {@link SearchQueryMetricsFactory}), if the query type
* belongs to the core druid-processing, e. g. SegmentMetadataQuery. If the query type defined in an extension, you
* can specify `binder.bind(ScanQueryMetricsFactory.class).to(DefaultScanQueryMetricsFactory.class)` in the extension's
* Guice module, if the query type is defined in an extension, e. g. ScanQuery. Or establish similar configuration,
* as for the core query types.
*
@ -146,6 +152,9 @@ import java.util.List;
* dimensions than the default generic QueryMetrics. So those subinterfaces shouldn't be taken as direct examples for
* following the plan specified above.
*
* Refer {@link SearchQueryMetricsFactory}
* and {@link io.druid.query.select.SelectQueryMetricsFactory} as an implementation example of this procedure.
*
* @param <QueryType>
*/
public interface QueryMetrics<QueryType extends Query<?>>

View File

@ -38,6 +38,7 @@ public class DefaultGroupByQueryMetrics extends DefaultQueryMetrics<GroupByQuery
numDimensions(query);
numMetrics(query);
numComplexMetrics(query);
granularity(query);
}
@Override
@ -58,4 +59,10 @@ public class DefaultGroupByQueryMetrics extends DefaultQueryMetrics<GroupByQuery
int numComplexAggs = DruidMetrics.findNumComplexAggs(query.getAggregatorSpecs());
setDimension("numComplexMetrics", String.valueOf(numComplexAggs));
}
@Override
public void granularity(GroupByQuery query)
{
//Don't emit by default
}
}

View File

@ -42,4 +42,9 @@ public interface GroupByQueryMetrics extends QueryMetrics<GroupByQuery>
* method.
*/
void numComplexMetrics(GroupByQuery query);
/**
* Sets the granularity of {@link GroupByQuery#getGranularity()} of the given query as dimension.
*/
void granularity(GroupByQuery query);
}

View File

@ -0,0 +1,240 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets 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 io.druid.query.search;
import com.metamx.emitter.service.ServiceEmitter;
import io.druid.collections.bitmap.BitmapFactory;
import io.druid.java.util.common.ISE;
import io.druid.query.BitmapResultFactory;
import io.druid.query.Query;
import io.druid.query.QueryMetrics;
import io.druid.query.filter.Filter;
import org.joda.time.Interval;
import java.util.List;
/**
* This class is implemented with delegation to another QueryMetrics for compatibility, see "Making subinterfaces of
* QueryMetrics for emitting custom dimensions and/or metrics for specific query types" section in {@link QueryMetrics}
* javadoc.
*/
public class DefaultSearchQueryMetrics implements SearchQueryMetrics
{
private QueryMetrics<Query<?>> delegateQueryMetrics;
// queryMetrics.query(query) must already be called on the provided queryMetrics.
public DefaultSearchQueryMetrics(QueryMetrics<Query<?>> queryMetrics)
{
this.delegateQueryMetrics = queryMetrics;
}
@Override
public void query(SearchQuery query)
{
//delegateQueryMetrics.query(query) must already be called on the provided queryMetrics.
}
@Override
public void dataSource(SearchQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void queryType(SearchQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void interval(SearchQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void hasFilters(SearchQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void duration(SearchQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void queryId(SearchQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void granularity(SearchQuery query)
{
// Don't emit by default
}
@Override
public void context(SearchQuery query)
{
delegateQueryMetrics.context(query);
}
@Override
public void server(String host)
{
delegateQueryMetrics.server(host);
}
@Override
public void remoteAddress(String remoteAddress)
{
delegateQueryMetrics.remoteAddress(remoteAddress);
}
@Override
public void status(String status)
{
delegateQueryMetrics.status(status);
}
@Override
public void success(boolean success)
{
delegateQueryMetrics.success(success);
}
@Override
public void segment(String segmentIdentifier)
{
delegateQueryMetrics.segment(segmentIdentifier);
}
@Override
public void chunkInterval(Interval interval)
{
delegateQueryMetrics.chunkInterval(interval);
}
@Override
public void preFilters(List<Filter> preFilters)
{
delegateQueryMetrics.preFilters(preFilters);
}
@Override
public void postFilters(List<Filter> postFilters)
{
delegateQueryMetrics.postFilters(postFilters);
}
@Override
public BitmapResultFactory<?> makeBitmapResultFactory(BitmapFactory factory)
{
return delegateQueryMetrics.makeBitmapResultFactory(factory);
}
@Override
public QueryMetrics reportQueryTime(long timeNs)
{
return delegateQueryMetrics.reportQueryTime(timeNs);
}
@Override
public QueryMetrics reportQueryBytes(long byteCount)
{
return delegateQueryMetrics.reportQueryBytes(byteCount);
}
@Override
public QueryMetrics reportWaitTime(long timeNs)
{
return delegateQueryMetrics.reportWaitTime(timeNs);
}
@Override
public QueryMetrics reportSegmentTime(long timeNs)
{
return delegateQueryMetrics.reportSegmentTime(timeNs);
}
@Override
public QueryMetrics reportSegmentAndCacheTime(long timeNs)
{
return delegateQueryMetrics.reportSegmentAndCacheTime(timeNs);
}
@Override
public QueryMetrics reportIntervalChunkTime(long timeNs)
{
return delegateQueryMetrics.reportIntervalChunkTime(timeNs);
}
@Override
public QueryMetrics reportCpuTime(long timeNs)
{
return delegateQueryMetrics.reportCpuTime(timeNs);
}
@Override
public QueryMetrics reportNodeTimeToFirstByte(long timeNs)
{
return delegateQueryMetrics.reportNodeTimeToFirstByte(timeNs);
}
@Override
public QueryMetrics reportNodeTime(long timeNs)
{
return delegateQueryMetrics.reportNodeTime(timeNs);
}
@Override
public QueryMetrics reportNodeBytes(long byteCount)
{
return delegateQueryMetrics.reportNodeBytes(byteCount);
}
@Override
public QueryMetrics reportBitmapConstructionTime(long timeNs)
{
return delegateQueryMetrics.reportBitmapConstructionTime(timeNs);
}
@Override
public QueryMetrics reportSegmentRows(long numRows)
{
return delegateQueryMetrics.reportSegmentRows(numRows);
}
@Override
public QueryMetrics reportPreFilteredRows(long numRows)
{
return delegateQueryMetrics.reportPreFilteredRows(numRows);
}
@Override
public void emit(ServiceEmitter emitter)
{
delegateQueryMetrics.emit(emitter);
}
}

View File

@ -0,0 +1,50 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets 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 io.druid.query.search;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
import io.druid.query.DefaultGenericQueryMetricsFactory;
import io.druid.query.GenericQueryMetricsFactory;
public class DefaultSearchQueryMetricsFactory implements SearchQueryMetricsFactory
{
private static final SearchQueryMetricsFactory INSTANCE =
new DefaultSearchQueryMetricsFactory(DefaultGenericQueryMetricsFactory.instance());
private final GenericQueryMetricsFactory genericQueryMetricsFactory;
@Inject
public DefaultSearchQueryMetricsFactory(GenericQueryMetricsFactory genericQueryMetricsFactory)
{
this.genericQueryMetricsFactory = genericQueryMetricsFactory;
}
@VisibleForTesting
public static SearchQueryMetricsFactory instance()
{
return INSTANCE;
}
@Override
public SearchQueryMetrics makeMetrics(SearchQuery query)
{
return new DefaultSearchQueryMetrics(genericQueryMetricsFactory.makeMetrics(query));
}
}

View File

@ -0,0 +1,31 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets 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 io.druid.query.search;
import io.druid.query.QueryMetrics;
public interface SearchQueryMetrics extends QueryMetrics<SearchQuery>
{
/**
* Sets the granularity of {@link SearchQuery#getGranularity()} of the given query as dimension.
*/
void granularity(SearchQuery query);
}

View File

@ -0,0 +1,36 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets 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 io.druid.query.search;
/**
* Implementations could be injected using
*
* PolyBind
* .optionBinder(binder, Key.get(SearchQueryMetricsFactory.class))
* .addBinding("myCustomSearchQueryMetricsFactory")
* .to(MyCustomSearchQueryMetricsFactory.class);
*
* And then setting property:
* druid.query.search.queryMetricsFactory=myCustomSearchQueryMetricsFactory
*/
public interface SearchQueryMetricsFactory
{
SearchQueryMetrics makeMetrics(SearchQuery searchQuery);
}

View File

@ -37,12 +37,9 @@ import io.druid.java.util.common.guava.Sequence;
import io.druid.java.util.common.guava.Sequences;
import io.druid.java.util.common.guava.nary.BinaryFn;
import io.druid.query.CacheStrategy;
import io.druid.query.DefaultGenericQueryMetricsFactory;
import io.druid.query.GenericQueryMetricsFactory;
import io.druid.query.IntervalChunkingQueryRunnerDecorator;
import io.druid.query.Query;
import io.druid.query.QueryContexts;
import io.druid.query.QueryMetrics;
import io.druid.query.QueryPlus;
import io.druid.query.QueryRunner;
import io.druid.query.QueryToolChest;
@ -73,7 +70,7 @@ public class SearchQueryQueryToolChest extends QueryToolChest<Result<SearchResul
private final SearchQueryConfig config;
private final IntervalChunkingQueryRunnerDecorator intervalChunkingQueryRunnerDecorator;
private final GenericQueryMetricsFactory queryMetricsFactory;
private final SearchQueryMetricsFactory queryMetricsFactory;
@VisibleForTesting
public SearchQueryQueryToolChest(
@ -81,14 +78,14 @@ public class SearchQueryQueryToolChest extends QueryToolChest<Result<SearchResul
IntervalChunkingQueryRunnerDecorator intervalChunkingQueryRunnerDecorator
)
{
this(config, intervalChunkingQueryRunnerDecorator, DefaultGenericQueryMetricsFactory.instance());
this(config, intervalChunkingQueryRunnerDecorator, DefaultSearchQueryMetricsFactory.instance());
}
@Inject
public SearchQueryQueryToolChest(
SearchQueryConfig config,
IntervalChunkingQueryRunnerDecorator intervalChunkingQueryRunnerDecorator,
GenericQueryMetricsFactory queryMetricsFactory
SearchQueryMetricsFactory queryMetricsFactory
)
{
this.config = config;
@ -124,9 +121,11 @@ public class SearchQueryQueryToolChest extends QueryToolChest<Result<SearchResul
}
@Override
public QueryMetrics<Query<?>> makeMetrics(SearchQuery query)
public SearchQueryMetrics makeMetrics(SearchQuery query)
{
return queryMetricsFactory.makeMetrics(query);
SearchQueryMetrics metrics = queryMetricsFactory.makeMetrics(query);
metrics.query(query);
return metrics;
}
@Override

View File

@ -0,0 +1,239 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets 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 io.druid.query.select;
import com.metamx.emitter.service.ServiceEmitter;
import io.druid.collections.bitmap.BitmapFactory;
import io.druid.java.util.common.ISE;
import io.druid.query.BitmapResultFactory;
import io.druid.query.Query;
import io.druid.query.QueryMetrics;
import io.druid.query.filter.Filter;
import org.joda.time.Interval;
import java.util.List;
/**
* This class is implemented with delegation to another QueryMetrics for compatibility, see "Making subinterfaces of
* QueryMetrics for emitting custom dimensions and/or metrics for specific query types" section in {@link QueryMetrics}
* javadoc.
*/
public class DefaultSelectQueryMetrics implements SelectQueryMetrics
{
private QueryMetrics<Query<?>> delegateQueryMetrics;
// queryMetrics.query(query) must already be called on the provided queryMetrics.
public DefaultSelectQueryMetrics(QueryMetrics<Query<?>> queryMetrics)
{
this.delegateQueryMetrics = queryMetrics;
}
@Override
public void query(SelectQuery query)
{
// delegateQueryMetrics.query(query) must already be called on the provided queryMetrics.
}
@Override
public void dataSource(SelectQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void queryType(SelectQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void interval(SelectQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void hasFilters(SelectQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void duration(SelectQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void queryId(SelectQuery query)
{
throw new ISE("Unsupported method in default query metrics implementation.");
}
@Override
public void granularity(SelectQuery query)
{
// Don't emit by default
}
@Override
public void context(SelectQuery query)
{
delegateQueryMetrics.context(query);
}
@Override
public void server(String host)
{
delegateQueryMetrics.server(host);
}
@Override
public void remoteAddress(String remoteAddress)
{
delegateQueryMetrics.remoteAddress(remoteAddress);
}
@Override
public void status(String status)
{
delegateQueryMetrics.status(status);
}
@Override
public void success(boolean success)
{
delegateQueryMetrics.success(success);
}
@Override
public void segment(String segmentIdentifier)
{
delegateQueryMetrics.segment(segmentIdentifier);
}
@Override
public void chunkInterval(Interval interval)
{
delegateQueryMetrics.chunkInterval(interval);
}
@Override
public void preFilters(List<Filter> preFilters)
{
delegateQueryMetrics.preFilters(preFilters);
}
@Override
public void postFilters(List<Filter> postFilters)
{
delegateQueryMetrics.postFilters(postFilters);
}
@Override
public BitmapResultFactory<?> makeBitmapResultFactory(BitmapFactory factory)
{
return delegateQueryMetrics.makeBitmapResultFactory(factory);
}
@Override
public QueryMetrics reportQueryTime(long timeNs)
{
return delegateQueryMetrics.reportQueryTime(timeNs);
}
@Override
public QueryMetrics reportQueryBytes(long byteCount)
{
return delegateQueryMetrics.reportQueryBytes(byteCount);
}
@Override
public QueryMetrics reportWaitTime(long timeNs)
{
return delegateQueryMetrics.reportWaitTime(timeNs);
}
@Override
public QueryMetrics reportSegmentTime(long timeNs)
{
return delegateQueryMetrics.reportSegmentTime(timeNs);
}
@Override
public QueryMetrics reportSegmentAndCacheTime(long timeNs)
{
return delegateQueryMetrics.reportSegmentAndCacheTime(timeNs);
}
@Override
public QueryMetrics reportIntervalChunkTime(long timeNs)
{
return delegateQueryMetrics.reportIntervalChunkTime(timeNs);
}
@Override
public QueryMetrics reportCpuTime(long timeNs)
{
return delegateQueryMetrics.reportCpuTime(timeNs);
}
@Override
public QueryMetrics reportNodeTimeToFirstByte(long timeNs)
{
return delegateQueryMetrics.reportNodeTimeToFirstByte(timeNs);
}
@Override
public QueryMetrics reportNodeTime(long timeNs)
{
return delegateQueryMetrics.reportNodeTime(timeNs);
}
@Override
public QueryMetrics reportNodeBytes(long byteCount)
{
return delegateQueryMetrics.reportNodeBytes(byteCount);
}
@Override
public QueryMetrics reportBitmapConstructionTime(long timeNs)
{
return delegateQueryMetrics.reportBitmapConstructionTime(timeNs);
}
@Override
public QueryMetrics reportSegmentRows(long numRows)
{
return delegateQueryMetrics.reportSegmentRows(numRows);
}
@Override
public QueryMetrics reportPreFilteredRows(long numRows)
{
return delegateQueryMetrics.reportPreFilteredRows(numRows);
}
@Override
public void emit(ServiceEmitter emitter)
{
delegateQueryMetrics.emit(emitter);
}
}

View File

@ -0,0 +1,51 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets 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 io.druid.query.select;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
import io.druid.query.DefaultGenericQueryMetricsFactory;
import io.druid.query.GenericQueryMetricsFactory;
public class DefaultSelectQueryMetricsFactory implements SelectQueryMetricsFactory
{
private static final SelectQueryMetricsFactory INSTANCE =
new DefaultSelectQueryMetricsFactory(DefaultGenericQueryMetricsFactory.instance());
private final GenericQueryMetricsFactory genericQueryMetricsFactory;
@Inject
public DefaultSelectQueryMetricsFactory(GenericQueryMetricsFactory genericQueryMetricsFactory)
{
this.genericQueryMetricsFactory = genericQueryMetricsFactory;
}
@VisibleForTesting
public static SelectQueryMetricsFactory instance()
{
return INSTANCE;
}
@Override
public SelectQueryMetrics makeMetrics(SelectQuery query)
{
return new DefaultSelectQueryMetrics(genericQueryMetricsFactory.makeMetrics(query));
}
}

View File

@ -0,0 +1,30 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets 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 io.druid.query.select;
import io.druid.query.QueryMetrics;
public interface SelectQueryMetrics extends QueryMetrics<SelectQuery>
{
/**
* Sets the granularity of {@link SelectQuery#getGranularity()} of the given query as dimension.
*/
void granularity(SelectQuery query);
}

View File

@ -0,0 +1,36 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets 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 io.druid.query.select;
/**
* Implementations could be injected using
*
* PolyBind
* .optionBinder(binder, Key.get(SelectQueryMetricsFactory.class))
* .addBinding("myCustomSelectQueryMetricsFactory")
* .to(MyCustomSelectQueryMetricsFactory.class);
*
* And then setting property:
* druid.query.select.queryMetricsFactory=myCustomSelectQueryMetricsFactory
*/
public interface SelectQueryMetricsFactory
{
SelectQueryMetrics makeMetrics(SelectQuery selectQuery);
}

View File

@ -38,11 +38,8 @@ import io.druid.java.util.common.guava.Comparators;
import io.druid.java.util.common.guava.Sequence;
import io.druid.java.util.common.guava.nary.BinaryFn;
import io.druid.query.CacheStrategy;
import io.druid.query.DefaultGenericQueryMetricsFactory;
import io.druid.query.GenericQueryMetricsFactory;
import io.druid.query.IntervalChunkingQueryRunnerDecorator;
import io.druid.query.Query;
import io.druid.query.QueryMetrics;
import io.druid.query.QueryPlus;
import io.druid.query.QueryRunner;
import io.druid.query.QueryToolChest;
@ -83,7 +80,7 @@ public class SelectQueryQueryToolChest extends QueryToolChest<Result<SelectResul
private final ObjectMapper jsonMapper;
private final IntervalChunkingQueryRunnerDecorator intervalChunkingQueryRunnerDecorator;
private final Supplier<SelectQueryConfig> configSupplier;
private final GenericQueryMetricsFactory queryMetricsFactory;
private final SelectQueryMetricsFactory queryMetricsFactory;
public SelectQueryQueryToolChest(
ObjectMapper jsonMapper,
@ -91,7 +88,7 @@ public class SelectQueryQueryToolChest extends QueryToolChest<Result<SelectResul
Supplier<SelectQueryConfig> configSupplier
)
{
this(jsonMapper, intervalChunkingQueryRunnerDecorator, configSupplier, new DefaultGenericQueryMetricsFactory(jsonMapper));
this(jsonMapper, intervalChunkingQueryRunnerDecorator, configSupplier, DefaultSelectQueryMetricsFactory.instance());
}
@Inject
@ -99,7 +96,7 @@ public class SelectQueryQueryToolChest extends QueryToolChest<Result<SelectResul
ObjectMapper jsonMapper,
IntervalChunkingQueryRunnerDecorator intervalChunkingQueryRunnerDecorator,
Supplier<SelectQueryConfig> configSupplier,
GenericQueryMetricsFactory queryMetricsFactory
SelectQueryMetricsFactory queryMetricsFactory
)
{
this.jsonMapper = jsonMapper;
@ -139,9 +136,11 @@ public class SelectQueryQueryToolChest extends QueryToolChest<Result<SelectResul
}
@Override
public QueryMetrics<Query<?>> makeMetrics(SelectQuery query)
public SelectQueryMetrics makeMetrics(SelectQuery query)
{
return queryMetricsFactory.makeMetrics(query);
SelectQueryMetrics queryMetrics = queryMetricsFactory.makeMetrics(query);
queryMetrics.query(query);
return queryMetrics;
}
@Override

View File

@ -37,6 +37,7 @@ public class DefaultTimeseriesQueryMetrics extends DefaultQueryMetrics<Timeserie
super.query(query);
numMetrics(query);
numComplexMetrics(query);
granularity(query);
}
@Override
@ -51,4 +52,10 @@ public class DefaultTimeseriesQueryMetrics extends DefaultQueryMetrics<Timeserie
int numComplexAggs = DruidMetrics.findNumComplexAggs(query.getAggregatorSpecs());
setDimension("numComplexMetrics", String.valueOf(numComplexAggs));
}
@Override
public void granularity(TimeseriesQuery query)
{
// Don't emit by default
}
}

View File

@ -37,4 +37,9 @@ public interface TimeseriesQueryMetrics extends QueryMetrics<TimeseriesQuery>
* method.
*/
void numComplexMetrics(TimeseriesQuery query);
/**
* Sets the granularity of {@link TimeseriesQuery#getGranularity()} of the given query as dimension.
*/
void granularity(TimeseriesQuery query);
}

View File

@ -41,6 +41,7 @@ public class DefaultTopNQueryMetrics extends DefaultQueryMetrics<TopNQuery> impl
dimension(query);
numMetrics(query);
numComplexMetrics(query);
granularity(query);
}
@Override
@ -68,6 +69,12 @@ public class DefaultTopNQueryMetrics extends DefaultQueryMetrics<TopNQuery> impl
setDimension("numComplexMetrics", String.valueOf(numComplexAggs));
}
@Override
public void granularity(TopNQuery query)
{
// Don't emit by default
}
@Override
public void dimensionCardinality(int cardinality)
{

View File

@ -50,6 +50,11 @@ public interface TopNQueryMetrics extends QueryMetrics<TopNQuery>
*/
void numComplexMetrics(TopNQuery query);
/**
* Sets the granularity of {@link TopNQuery#getGranularity()} of the given query as dimension.
*/
void granularity(TopNQuery query);
void dimensionCardinality(int cardinality);
void algorithm(TopNAlgorithm algorithm);

View File

@ -66,7 +66,6 @@ public class DefaultQueryMetricsTest
.filters(new SelectorDimFilter("tags", "t3", null))
.build();
queryMetrics.query(query);
queryMetrics.reportQueryTime(0).emit(serviceEmitter);
Map<String, Object> actualEvent = cachingEmitter.getLastEmittedEvent().toMap();
Assert.assertEquals(12, actualEvent.size());

View File

@ -0,0 +1,103 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets 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 io.druid.query.search;
import com.google.common.collect.ImmutableSet;
import com.metamx.emitter.service.ServiceEmitter;
import io.druid.query.CachingEmitter;
import io.druid.query.DefaultQueryMetricsTest;
import io.druid.query.DruidMetrics;
import io.druid.query.Druids;
import io.druid.query.QueryRunnerTestHelper;
import io.druid.query.dimension.DefaultDimensionSpec;
import io.druid.query.dimension.ListFilteredDimensionSpec;
import org.joda.time.Interval;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class DefaultSearchQueryMetricsTest
{
/**
* Tests that passed a query {@link DefaultSearchQueryMetrics} produces events with a certain set of dimensions.
*/
@Test
public void testDefaultSearchQueryMetricsQuery()
{
CachingEmitter cachingEmitter = new CachingEmitter();
ServiceEmitter serviceEmitter = new ServiceEmitter("", "", cachingEmitter);
SearchQuery query = Druids
.newSearchQueryBuilder()
.dataSource(QueryRunnerTestHelper.dataSource)
.granularity(QueryRunnerTestHelper.dayGran)
.intervals(QueryRunnerTestHelper.fullOnInterval)
.dimensions(new ListFilteredDimensionSpec(
new DefaultDimensionSpec("tags", "tags"),
ImmutableSet.of("t3"),
null
))
.build();
SearchQueryMetrics queryMetrics = DefaultSearchQueryMetricsFactory.instance().makeMetrics(query);
queryMetrics.query(query);
queryMetrics.reportQueryTime(0).emit(serviceEmitter);
Map<String, Object> actualEvent = cachingEmitter.getLastEmittedEvent().toMap();
Assert.assertEquals(12, actualEvent.size());
Assert.assertTrue(actualEvent.containsKey("feed"));
Assert.assertTrue(actualEvent.containsKey("timestamp"));
Assert.assertEquals("", actualEvent.get("host"));
Assert.assertEquals("", actualEvent.get("service"));
Assert.assertEquals(QueryRunnerTestHelper.dataSource, actualEvent.get(DruidMetrics.DATASOURCE));
Assert.assertEquals(query.getType(), actualEvent.get(DruidMetrics.TYPE));
List<Interval> expectedIntervals = QueryRunnerTestHelper.fullOnInterval.getIntervals();
List<String> expectedStringIntervals =
expectedIntervals.stream().map(Interval::toString).collect(Collectors.toList());
Assert.assertEquals(expectedStringIntervals, actualEvent.get(DruidMetrics.INTERVAL));
Assert.assertEquals("false", actualEvent.get("hasFilters"));
Assert.assertEquals(expectedIntervals.get(0).toDuration().toString(), actualEvent.get("duration"));
Assert.assertEquals("", actualEvent.get(DruidMetrics.ID));
// Metric
Assert.assertEquals("query/time", actualEvent.get("metric"));
Assert.assertEquals(0L, actualEvent.get("value"));
}
@Test
public void testDefaultSearchQueryMetricsMetricNamesAndUnits()
{
SearchQuery query = Druids
.newSearchQueryBuilder()
.dataSource(QueryRunnerTestHelper.dataSource)
.granularity(QueryRunnerTestHelper.dayGran)
.intervals(QueryRunnerTestHelper.fullOnInterval)
.build();
CachingEmitter cachingEmitter = new CachingEmitter();
ServiceEmitter serviceEmitter = new ServiceEmitter("", "", cachingEmitter);
SearchQueryMetrics queryMetrics = DefaultSearchQueryMetricsFactory.instance().makeMetrics(query);
DefaultQueryMetricsTest.testQueryMetricsDefaultMetricNamesAndUnits(cachingEmitter, serviceEmitter, queryMetrics);
}
}

View File

@ -0,0 +1,99 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets 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 io.druid.query.select;
import com.metamx.emitter.service.ServiceEmitter;
import io.druid.query.CachingEmitter;
import io.druid.query.DefaultQueryMetricsTest;
import io.druid.query.DruidMetrics;
import io.druid.query.Druids;
import io.druid.query.QueryRunnerTestHelper;
import org.joda.time.Interval;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class DefaultSelectQueryMetricsTest
{
/**
* Tests that passed a query {@link DefaultSelectQueryMetrics} produces events with a certain set of dimensions.
*/
@Test
public void testDefaultSelectQueryMetricsQuery()
{
CachingEmitter cachingEmitter = new CachingEmitter();
ServiceEmitter serviceEmitter = new ServiceEmitter("", "", cachingEmitter);
SelectQuery query = Druids
.newSelectQueryBuilder()
.dataSource(QueryRunnerTestHelper.dataSource)
.granularity(QueryRunnerTestHelper.dayGran)
.intervals(QueryRunnerTestHelper.fullOnInterval)
.descending(true)
.pagingSpec(PagingSpec.newSpec(1))
.build();
SelectQueryMetrics queryMetrics = DefaultSelectQueryMetricsFactory.instance().makeMetrics(query);
queryMetrics.query(query);
queryMetrics.reportQueryTime(0).emit(serviceEmitter);
Map<String, Object> actualEvent = cachingEmitter.getLastEmittedEvent().toMap();
Assert.assertEquals(12, actualEvent.size());
Assert.assertTrue(actualEvent.containsKey("feed"));
Assert.assertTrue(actualEvent.containsKey("timestamp"));
Assert.assertEquals("", actualEvent.get("host"));
Assert.assertEquals("", actualEvent.get("service"));
Assert.assertEquals(QueryRunnerTestHelper.dataSource, actualEvent.get(DruidMetrics.DATASOURCE));
Assert.assertEquals(query.getType(), actualEvent.get(DruidMetrics.TYPE));
List<Interval> expectedIntervals = QueryRunnerTestHelper.fullOnInterval.getIntervals();
List<String> expectedStringIntervals =
expectedIntervals.stream().map(Interval::toString).collect(Collectors.toList());
Assert.assertEquals(expectedStringIntervals, actualEvent.get(DruidMetrics.INTERVAL));
Assert.assertEquals("false", actualEvent.get("hasFilters"));
Assert.assertEquals(expectedIntervals.get(0).toDuration().toString(), actualEvent.get("duration"));
Assert.assertEquals("", actualEvent.get(DruidMetrics.ID));
// Metric
Assert.assertEquals("query/time", actualEvent.get("metric"));
Assert.assertEquals(0L, actualEvent.get("value"));
}
@Test
public void testDefaultSelectQueryMetricsMetricNamesAndUnits()
{
CachingEmitter cachingEmitter = new CachingEmitter();
ServiceEmitter serviceEmitter = new ServiceEmitter("", "", cachingEmitter);
SelectQuery query = Druids
.newSelectQueryBuilder()
.dataSource(QueryRunnerTestHelper.dataSource)
.granularity(QueryRunnerTestHelper.dayGran)
.intervals(QueryRunnerTestHelper.fullOnInterval)
.descending(true)
.pagingSpec(PagingSpec.newSpec(1))
.build();
SelectQueryMetrics queryMetrics = DefaultSelectQueryMetricsFactory.instance().makeMetrics(query);
DefaultQueryMetricsTest.testQueryMetricsDefaultMetricNamesAndUnits(cachingEmitter, serviceEmitter, queryMetrics);
}
}

View File

@ -44,10 +44,14 @@ import io.druid.query.scan.ScanQuery;
import io.druid.query.scan.ScanQueryConfig;
import io.druid.query.scan.ScanQueryQueryToolChest;
import io.druid.query.search.SearchQueryQueryToolChest;
import io.druid.query.search.DefaultSearchQueryMetricsFactory;
import io.druid.query.search.SearchQuery;
import io.druid.query.search.SearchQueryConfig;
import io.druid.query.search.SearchQueryMetricsFactory;
import io.druid.query.select.DefaultSelectQueryMetricsFactory;
import io.druid.query.select.SelectQuery;
import io.druid.query.select.SelectQueryConfig;
import io.druid.query.select.SelectQueryMetricsFactory;
import io.druid.query.select.SelectQueryQueryToolChest;
import io.druid.query.timeboundary.TimeBoundaryQuery;
import io.druid.query.timeboundary.TimeBoundaryQueryQueryToolChest;
@ -71,6 +75,8 @@ public class QueryToolChestModule implements Module
public static final String GROUPBY_QUERY_METRICS_FACTORY_PROPERTY = "druid.query.groupBy.queryMetricsFactory";
public static final String TIMESERIES_QUERY_METRICS_FACTORY_PROPERTY = "druid.query.timeseries.queryMetricsFactory";
public static final String TOPN_QUERY_METRICS_FACTORY_PROPERTY = "druid.query.topN.queryMetricsFactory";
public static final String SELECT_QUERY_METRICS_FACTORY_PROPERTY = "druid.query.select.queryMetricsFactory";
public static final String SEARCH_QUERY_METRICS_FACTORY_PROPERTY = "druid.query.search.queryMetricsFactory";
public final Map<Class<? extends Query>, Class<? extends QueryToolChest>> mappings =
ImmutableMap.<Class<? extends Query>, Class<? extends QueryToolChest>>builder()
@ -147,5 +153,27 @@ public class QueryToolChestModule implements Module
.optionBinder(binder, Key.get(TopNQueryMetricsFactory.class))
.addBinding("default")
.to(DefaultTopNQueryMetricsFactory.class);
PolyBind.createChoice(
binder,
SELECT_QUERY_METRICS_FACTORY_PROPERTY,
Key.get(SelectQueryMetricsFactory.class),
Key.get(DefaultSelectQueryMetricsFactory.class)
);
PolyBind
.optionBinder(binder, Key.get(SelectQueryMetricsFactory.class))
.addBinding("default")
.to(DefaultSelectQueryMetricsFactory.class);
PolyBind.createChoice(
binder,
SEARCH_QUERY_METRICS_FACTORY_PROPERTY,
Key.get(SearchQueryMetricsFactory.class),
Key.get(DefaultSearchQueryMetricsFactory.class)
);
PolyBind
.optionBinder(binder, Key.get(SearchQueryMetricsFactory.class))
.addBinding("default")
.to(DefaultSearchQueryMetricsFactory.class);
}
}