diff --git a/processing/src/main/java/io/druid/query/QueryMetrics.java b/processing/src/main/java/io/druid/query/QueryMetrics.java index e9bf74f1a86..8138c4197c3 100644 --- a/processing/src/main/java/io/druid/query/QueryMetrics.java +++ b/processing/src/main/java/io/druid/query/QueryMetrics.java @@ -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 */ public interface QueryMetrics> diff --git a/processing/src/main/java/io/druid/query/groupby/DefaultGroupByQueryMetrics.java b/processing/src/main/java/io/druid/query/groupby/DefaultGroupByQueryMetrics.java index 808269e949a..5fee46adbb6 100644 --- a/processing/src/main/java/io/druid/query/groupby/DefaultGroupByQueryMetrics.java +++ b/processing/src/main/java/io/druid/query/groupby/DefaultGroupByQueryMetrics.java @@ -38,6 +38,7 @@ public class DefaultGroupByQueryMetrics extends DefaultQueryMetrics * method. */ void numComplexMetrics(GroupByQuery query); + + /** + * Sets the granularity of {@link GroupByQuery#getGranularity()} of the given query as dimension. + */ + void granularity(GroupByQuery query); } diff --git a/processing/src/main/java/io/druid/query/search/DefaultSearchQueryMetrics.java b/processing/src/main/java/io/druid/query/search/DefaultSearchQueryMetrics.java new file mode 100644 index 00000000000..43fc752548c --- /dev/null +++ b/processing/src/main/java/io/druid/query/search/DefaultSearchQueryMetrics.java @@ -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> delegateQueryMetrics; + + + // queryMetrics.query(query) must already be called on the provided queryMetrics. + public DefaultSearchQueryMetrics(QueryMetrics> 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 preFilters) + { + delegateQueryMetrics.preFilters(preFilters); + } + + @Override + public void postFilters(List 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); + } +} diff --git a/processing/src/main/java/io/druid/query/search/DefaultSearchQueryMetricsFactory.java b/processing/src/main/java/io/druid/query/search/DefaultSearchQueryMetricsFactory.java new file mode 100644 index 00000000000..47485ed90b8 --- /dev/null +++ b/processing/src/main/java/io/druid/query/search/DefaultSearchQueryMetricsFactory.java @@ -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)); + } +} diff --git a/processing/src/main/java/io/druid/query/search/SearchQueryMetrics.java b/processing/src/main/java/io/druid/query/search/SearchQueryMetrics.java new file mode 100644 index 00000000000..c9ccb9d42f2 --- /dev/null +++ b/processing/src/main/java/io/druid/query/search/SearchQueryMetrics.java @@ -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 +{ + /** + * Sets the granularity of {@link SearchQuery#getGranularity()} of the given query as dimension. + */ + void granularity(SearchQuery query); + +} diff --git a/processing/src/main/java/io/druid/query/search/SearchQueryMetricsFactory.java b/processing/src/main/java/io/druid/query/search/SearchQueryMetricsFactory.java new file mode 100644 index 00000000000..842e289288e --- /dev/null +++ b/processing/src/main/java/io/druid/query/search/SearchQueryMetricsFactory.java @@ -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); +} diff --git a/processing/src/main/java/io/druid/query/search/SearchQueryQueryToolChest.java b/processing/src/main/java/io/druid/query/search/SearchQueryQueryToolChest.java index 9dfa05d3f23..74148d66aea 100644 --- a/processing/src/main/java/io/druid/query/search/SearchQueryQueryToolChest.java +++ b/processing/src/main/java/io/druid/query/search/SearchQueryQueryToolChest.java @@ -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> makeMetrics(SearchQuery query) + public SearchQueryMetrics makeMetrics(SearchQuery query) { - return queryMetricsFactory.makeMetrics(query); + SearchQueryMetrics metrics = queryMetricsFactory.makeMetrics(query); + metrics.query(query); + return metrics; } @Override diff --git a/processing/src/main/java/io/druid/query/select/DefaultSelectQueryMetrics.java b/processing/src/main/java/io/druid/query/select/DefaultSelectQueryMetrics.java new file mode 100644 index 00000000000..3fbae8a9903 --- /dev/null +++ b/processing/src/main/java/io/druid/query/select/DefaultSelectQueryMetrics.java @@ -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> delegateQueryMetrics; + + // queryMetrics.query(query) must already be called on the provided queryMetrics. + public DefaultSelectQueryMetrics(QueryMetrics> 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 preFilters) + { + delegateQueryMetrics.preFilters(preFilters); + } + + @Override + public void postFilters(List 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); + } +} diff --git a/processing/src/main/java/io/druid/query/select/DefaultSelectQueryMetricsFactory.java b/processing/src/main/java/io/druid/query/select/DefaultSelectQueryMetricsFactory.java new file mode 100644 index 00000000000..5c90dae4777 --- /dev/null +++ b/processing/src/main/java/io/druid/query/select/DefaultSelectQueryMetricsFactory.java @@ -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)); + } +} diff --git a/processing/src/main/java/io/druid/query/select/SelectQueryMetrics.java b/processing/src/main/java/io/druid/query/select/SelectQueryMetrics.java new file mode 100644 index 00000000000..f4757979c6a --- /dev/null +++ b/processing/src/main/java/io/druid/query/select/SelectQueryMetrics.java @@ -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 +{ + /** + * Sets the granularity of {@link SelectQuery#getGranularity()} of the given query as dimension. + */ + void granularity(SelectQuery query); +} diff --git a/processing/src/main/java/io/druid/query/select/SelectQueryMetricsFactory.java b/processing/src/main/java/io/druid/query/select/SelectQueryMetricsFactory.java new file mode 100644 index 00000000000..cae36336466 --- /dev/null +++ b/processing/src/main/java/io/druid/query/select/SelectQueryMetricsFactory.java @@ -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); +} diff --git a/processing/src/main/java/io/druid/query/select/SelectQueryQueryToolChest.java b/processing/src/main/java/io/druid/query/select/SelectQueryQueryToolChest.java index bc6da76de27..72cdbdac73b 100644 --- a/processing/src/main/java/io/druid/query/select/SelectQueryQueryToolChest.java +++ b/processing/src/main/java/io/druid/query/select/SelectQueryQueryToolChest.java @@ -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 configSupplier; - private final GenericQueryMetricsFactory queryMetricsFactory; + private final SelectQueryMetricsFactory queryMetricsFactory; public SelectQueryQueryToolChest( ObjectMapper jsonMapper, @@ -91,7 +88,7 @@ public class SelectQueryQueryToolChest extends QueryToolChest configSupplier ) { - this(jsonMapper, intervalChunkingQueryRunnerDecorator, configSupplier, new DefaultGenericQueryMetricsFactory(jsonMapper)); + this(jsonMapper, intervalChunkingQueryRunnerDecorator, configSupplier, DefaultSelectQueryMetricsFactory.instance()); } @Inject @@ -99,7 +96,7 @@ public class SelectQueryQueryToolChest extends QueryToolChest configSupplier, - GenericQueryMetricsFactory queryMetricsFactory + SelectQueryMetricsFactory queryMetricsFactory ) { this.jsonMapper = jsonMapper; @@ -139,9 +136,11 @@ public class SelectQueryQueryToolChest extends QueryToolChest> makeMetrics(SelectQuery query) + public SelectQueryMetrics makeMetrics(SelectQuery query) { - return queryMetricsFactory.makeMetrics(query); + SelectQueryMetrics queryMetrics = queryMetricsFactory.makeMetrics(query); + queryMetrics.query(query); + return queryMetrics; } @Override diff --git a/processing/src/main/java/io/druid/query/timeseries/DefaultTimeseriesQueryMetrics.java b/processing/src/main/java/io/druid/query/timeseries/DefaultTimeseriesQueryMetrics.java index d8a015bd2a3..b7f0f9a4507 100644 --- a/processing/src/main/java/io/druid/query/timeseries/DefaultTimeseriesQueryMetrics.java +++ b/processing/src/main/java/io/druid/query/timeseries/DefaultTimeseriesQueryMetrics.java @@ -37,6 +37,7 @@ public class DefaultTimeseriesQueryMetrics extends DefaultQueryMetrics * method. */ void numComplexMetrics(TimeseriesQuery query); + + /** + * Sets the granularity of {@link TimeseriesQuery#getGranularity()} of the given query as dimension. + */ + void granularity(TimeseriesQuery query); } diff --git a/processing/src/main/java/io/druid/query/topn/DefaultTopNQueryMetrics.java b/processing/src/main/java/io/druid/query/topn/DefaultTopNQueryMetrics.java index 0ad35e3c073..91a81b32c91 100644 --- a/processing/src/main/java/io/druid/query/topn/DefaultTopNQueryMetrics.java +++ b/processing/src/main/java/io/druid/query/topn/DefaultTopNQueryMetrics.java @@ -41,6 +41,7 @@ public class DefaultTopNQueryMetrics extends DefaultQueryMetrics impl dimension(query); numMetrics(query); numComplexMetrics(query); + granularity(query); } @Override @@ -68,6 +69,12 @@ public class DefaultTopNQueryMetrics extends DefaultQueryMetrics impl setDimension("numComplexMetrics", String.valueOf(numComplexAggs)); } + @Override + public void granularity(TopNQuery query) + { + // Don't emit by default + } + @Override public void dimensionCardinality(int cardinality) { diff --git a/processing/src/main/java/io/druid/query/topn/TopNQueryMetrics.java b/processing/src/main/java/io/druid/query/topn/TopNQueryMetrics.java index 6a70b8dcf44..25eb0fbd225 100644 --- a/processing/src/main/java/io/druid/query/topn/TopNQueryMetrics.java +++ b/processing/src/main/java/io/druid/query/topn/TopNQueryMetrics.java @@ -50,6 +50,11 @@ public interface TopNQueryMetrics extends QueryMetrics */ 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); diff --git a/processing/src/test/java/io/druid/query/DefaultQueryMetricsTest.java b/processing/src/test/java/io/druid/query/DefaultQueryMetricsTest.java index d6b1876952b..aee5c6e8fb4 100644 --- a/processing/src/test/java/io/druid/query/DefaultQueryMetricsTest.java +++ b/processing/src/test/java/io/druid/query/DefaultQueryMetricsTest.java @@ -66,7 +66,6 @@ public class DefaultQueryMetricsTest .filters(new SelectorDimFilter("tags", "t3", null)) .build(); queryMetrics.query(query); - queryMetrics.reportQueryTime(0).emit(serviceEmitter); Map actualEvent = cachingEmitter.getLastEmittedEvent().toMap(); Assert.assertEquals(12, actualEvent.size()); diff --git a/processing/src/test/java/io/druid/query/search/DefaultSearchQueryMetricsTest.java b/processing/src/test/java/io/druid/query/search/DefaultSearchQueryMetricsTest.java new file mode 100644 index 00000000000..dbb2db70320 --- /dev/null +++ b/processing/src/test/java/io/druid/query/search/DefaultSearchQueryMetricsTest.java @@ -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 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 expectedIntervals = QueryRunnerTestHelper.fullOnInterval.getIntervals(); + List 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); + } +} diff --git a/processing/src/test/java/io/druid/query/select/DefaultSelectQueryMetricsTest.java b/processing/src/test/java/io/druid/query/select/DefaultSelectQueryMetricsTest.java new file mode 100644 index 00000000000..6aeefe2b295 --- /dev/null +++ b/processing/src/test/java/io/druid/query/select/DefaultSelectQueryMetricsTest.java @@ -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 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 expectedIntervals = QueryRunnerTestHelper.fullOnInterval.getIntervals(); + List 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); + } +} diff --git a/server/src/main/java/io/druid/guice/QueryToolChestModule.java b/server/src/main/java/io/druid/guice/QueryToolChestModule.java index 68355f33dc2..2f63e8a4ab2 100644 --- a/server/src/main/java/io/druid/guice/QueryToolChestModule.java +++ b/server/src/main/java/io/druid/guice/QueryToolChestModule.java @@ -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> mappings = ImmutableMap., Class>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); } }