From 7abe1cffaf9ce8e8b64b5fbd31f310bc46a41617 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Wed, 2 Oct 2019 10:21:11 -0700 Subject: [PATCH 001/107] prometheus-emitter --- extensions-contrib/prometheus-emitter/pom.xml | 122 ++++++++++++++++++ .../druid/emitter/prometheus/Metrics.java | 79 ++++++++++++ .../emitter/prometheus/PrometheusEmitter.java | 95 ++++++++++++++ .../prometheus/PrometheusEmitterConfig.java | 44 +++++++ .../prometheus/PrometheusEmitterModule.java | 60 +++++++++ ...rg.apache.druid.initialization.DruidModule | 16 +++ .../prometheus/PrometheusEmitterTest.java | 24 ++++ 7 files changed, 440 insertions(+) create mode 100644 extensions-contrib/prometheus-emitter/pom.xml create mode 100644 extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java create mode 100644 extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java create mode 100644 extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java create mode 100644 extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java create mode 100644 extensions-contrib/prometheus-emitter/src/main/resources/META-INF/services/org.apache.druid.initialization.DruidModule create mode 100644 extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java diff --git a/extensions-contrib/prometheus-emitter/pom.xml b/extensions-contrib/prometheus-emitter/pom.xml new file mode 100644 index 000000000000..112c5ccc7983 --- /dev/null +++ b/extensions-contrib/prometheus-emitter/pom.xml @@ -0,0 +1,122 @@ + + + + + druid + org.apache.druid + 0.17.0-incubating-SNAPSHOT + ../../pom.xml + + 4.0.0 + + org.apache.druid.extensions.contrib + prometheus-emitter + prometheus-emitter + Extension support for collecting Druid metrics with Prometheus + + + + org.apache.druid + druid-core + ${project.parent.version} + provided + + + io.prometheus + simpleclient + 0.7.0 + + + io.prometheus + simpleclient_httpserver + 0.7.0 + + + com.google.code.findbugs + jsr305 + provided + + + com.fasterxml.jackson.core + jackson-annotations + provided + + + joda-time + joda-time + provided + + + com.google.guava + guava + provided + + + com.google.inject + guice + provided + + + com.fasterxml.jackson.core + jackson-databind + provided + + + com.fasterxml.jackson.core + jackson-core + provided + + + junit + junit + test + + + org.easymock + easymock + test + + + pl.pragmatists + JUnitParams + test + + + org.apache.druid + druid-server + ${project.parent.version} + test-jar + test + + + org.apache.druid + druid-processing + ${project.parent.version} + test-jar + test + + + io.dropwizard.metrics + metrics-core + + + diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java new file mode 100644 index 000000000000..ab586109f8cf --- /dev/null +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -0,0 +1,79 @@ +package org.apache.druid.emitter.prometheus; + +import java.util.HashMap; +import java.util.Map; + +import io.prometheus.client.Counter; +import io.prometheus.client.Histogram; +import io.prometheus.client.SimpleCollector; + +public class Metrics { + + private Map map = new HashMap<>(); + + public Metric getByName(String name) { + return map.get(name); + } + + public Metrics(String namespace) { + + map.put("query/time", new Metric( + new String[] { "dataSource", "type" }, + new Histogram.Builder() + .namespace(namespace) + .name("query_time") + .labelNames("dataSource", "type") + .buckets(new double[] { .1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300 }) + .register())); + map.put("query/bytes", + new Metric(new String[] { "dataSource", "type" }, + new Counter.Builder() + .namespace(namespace) + .name("query_bytes_total") + .labelNames("dataSource", "type") + .register())); + map.put("query/node/time", + new Metric(new String[] { "server" }, + new Histogram.Builder() + .namespace(namespace) + .name("query_node_time") + .labelNames("server") + .buckets(new double[] { .1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300 }) + .register())); + map.put("query/node/ttfb", + new Metric(new String[] { "server" }, + new Histogram.Builder() + .namespace(namespace) + .name("query_node_ttfb_time") + .labelNames("server") + .buckets(new double[] { .1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300 }) + .register())); + map.put("query/node/bytes", + new Metric(new String[] { "server" }, + new Counter.Builder() + .namespace(namespace) + .name("query_node_bytes_total") + .labelNames("server") + .register())); + } + + + public static class Metric { + private final String[] dimensions; + private final SimpleCollector collector; + + Metric(String[] dimensions, SimpleCollector collector) { + this.dimensions = dimensions; + this.collector = collector; + } + + public String[] getDimensions() { + return dimensions; + } + + public SimpleCollector getCollector() { + return collector; + } + } + +} diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java new file mode 100644 index 000000000000..ca6c42341841 --- /dev/null +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.emitter.prometheus; + +import java.util.Map; + +import org.apache.druid.java.util.common.logger.Logger; +import org.apache.druid.java.util.emitter.core.Emitter; +import org.apache.druid.java.util.emitter.core.Event; +import org.apache.druid.java.util.emitter.service.ServiceMetricEvent; + +import io.prometheus.client.Counter; +import io.prometheus.client.Gauge; +import io.prometheus.client.Histogram; + +/** + */ +public class PrometheusEmitter implements Emitter { + + private static final Logger log = new Logger(PrometheusEmitter.class); + private final Metrics metrics; + + static PrometheusEmitter of(PrometheusEmitterConfig config) { + return new PrometheusEmitter(config); + } + + public PrometheusEmitter(PrometheusEmitterConfig config) { + metrics = new Metrics(config.getNamespace()); + } + + + @Override + public void start() { + } + + @Override + public void emit(Event event) { + if (event instanceof ServiceMetricEvent) { + emitMetric((ServiceMetricEvent) event); + } + } + + void emitMetric(ServiceMetricEvent metricEvent) { + String host = metricEvent.getHost(); + String service = metricEvent.getService(); + String metric = metricEvent.getMetric(); + Map userDims = metricEvent.getUserDims(); + Number value = metricEvent.getValue(); + + Metrics.Metric byName = metrics.getByName(metric); + String[] labelValues = new String[byName.getDimensions().length]; + String[] labelNames = byName.getDimensions(); + for (int i = 0; i < labelValues.length; i++) { + String labelName = labelNames[i]; + Object userDim = userDims.get(labelName); + labelValues[i] = userDim.toString(); + } + + if (byName.getCollector() instanceof Counter) { + ((Counter) byName.getCollector()).labels(labelValues).inc(value.doubleValue()); + } else if (byName.getCollector() instanceof Gauge) { + ((Gauge) byName.getCollector()).labels(labelValues).set(value.doubleValue()); + } else if (byName.getCollector() instanceof Histogram) { + ((Histogram) byName.getCollector()).labels(labelValues).observe(value.doubleValue()); + } else { + //TODO + } + + } + + @Override + public void flush() { + } + + @Override + public void close() { + } +} diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java new file mode 100644 index 000000000000..aed6c282243f --- /dev/null +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.emitter.prometheus; + +import javax.annotation.Nullable; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + */ +public class PrometheusEmitterConfig +{ + + @JsonProperty + private final String namespace; + + @JsonCreator + public PrometheusEmitterConfig(@JsonProperty("namespace") @Nullable String namespace) + { + this.namespace = namespace != null ? namespace : ""; + } + + public String getNamespace() { + return namespace; + } +} diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java new file mode 100644 index 000000000000..19ef691eb084 --- /dev/null +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.emitter.prometheus; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.Binder; +import com.google.inject.Provides; +import com.google.inject.name.Named; +import org.apache.druid.guice.JsonConfigProvider; +import org.apache.druid.guice.ManageLifecycle; +import org.apache.druid.initialization.DruidModule; +import org.apache.druid.java.util.emitter.core.Emitter; + +import java.util.Collections; +import java.util.List; + +/** + */ +public class PrometheusEmitterModule implements DruidModule +{ + private static final String EMITTER_TYPE = "prometheus"; + + @Override + public List getJacksonModules() + { + return Collections.EMPTY_LIST; + } + + @Override + public void configure(Binder binder) + { + JsonConfigProvider.bind(binder, "druid.emitter." + EMITTER_TYPE, PrometheusEmitterConfig.class); + } + + @Provides + @ManageLifecycle + @Named(EMITTER_TYPE) + public Emitter getEmitter(PrometheusEmitterConfig config) + { + return PrometheusEmitter.of(config); + } +} diff --git a/extensions-contrib/prometheus-emitter/src/main/resources/META-INF/services/org.apache.druid.initialization.DruidModule b/extensions-contrib/prometheus-emitter/src/main/resources/META-INF/services/org.apache.druid.initialization.DruidModule new file mode 100644 index 000000000000..da92fc068bf8 --- /dev/null +++ b/extensions-contrib/prometheus-emitter/src/main/resources/META-INF/services/org.apache.druid.initialization.DruidModule @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +org.apache.druid.emitter.prometheus.PrometheusEmitterModule diff --git a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java new file mode 100644 index 000000000000..666bd1419515 --- /dev/null +++ b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.emitter.prometheus; + +public class PrometheusEmitterTest +{ +} From 580e84616597f428ff20e35e3d7f49711c45778c Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Wed, 2 Oct 2019 13:25:40 -0700 Subject: [PATCH 002/107] use existing jetty server to expose prometheus collection endpoint --- extensions-contrib/prometheus-emitter/pom.xml | 13 ++ .../druid/emitter/prometheus/Metrics.java | 165 +++++++++++------- .../emitter/prometheus/PrometheusEmitter.java | 34 ++-- .../prometheus/PrometheusEmitterConfig.java | 24 ++- .../prometheus/PrometheusEmitterModule.java | 14 +- pom.xml | 1 + 6 files changed, 170 insertions(+), 81 deletions(-) diff --git a/extensions-contrib/prometheus-emitter/pom.xml b/extensions-contrib/prometheus-emitter/pom.xml index 112c5ccc7983..c909308c7024 100644 --- a/extensions-contrib/prometheus-emitter/pom.xml +++ b/extensions-contrib/prometheus-emitter/pom.xml @@ -118,5 +118,18 @@ io.dropwizard.metrics metrics-core + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-servlet + + + io.prometheus + simpleclient_servlet + 0.4.0 + diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java index ab586109f8cf..69fc55b804bc 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -1,79 +1,122 @@ -package org.apache.druid.emitter.prometheus; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ -import java.util.HashMap; -import java.util.Map; +package org.apache.druid.emitter.prometheus; import io.prometheus.client.Counter; import io.prometheus.client.Histogram; import io.prometheus.client.SimpleCollector; -public class Metrics { +import java.util.HashMap; +import java.util.Map; - private Map map = new HashMap<>(); +public class Metrics +{ - public Metric getByName(String name) { - return map.get(name); - } + private Map map = new HashMap<>(); - public Metrics(String namespace) { + public Metric getByName(String name) + { + return map.get(name); + } - map.put("query/time", new Metric( - new String[] { "dataSource", "type" }, - new Histogram.Builder() - .namespace(namespace) - .name("query_time") - .labelNames("dataSource", "type") - .buckets(new double[] { .1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300 }) - .register())); - map.put("query/bytes", - new Metric(new String[] { "dataSource", "type" }, - new Counter.Builder() - .namespace(namespace) - .name("query_bytes_total") - .labelNames("dataSource", "type") - .register())); - map.put("query/node/time", - new Metric(new String[] { "server" }, - new Histogram.Builder() - .namespace(namespace) - .name("query_node_time") - .labelNames("server") - .buckets(new double[] { .1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300 }) - .register())); - map.put("query/node/ttfb", - new Metric(new String[] { "server" }, - new Histogram.Builder() - .namespace(namespace) - .name("query_node_ttfb_time") - .labelNames("server") - .buckets(new double[] { .1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300 }) - .register())); - map.put("query/node/bytes", - new Metric(new String[] { "server" }, - new Counter.Builder() - .namespace(namespace) - .name("query_node_bytes_total") - .labelNames("server") - .register())); - } + public Metrics(String namespace) + { + map.put("query/time", new Metric( + new String[]{"dataSource", "type"}, + new Histogram.Builder() + .namespace(namespace) + .name("query_time") + .labelNames("dataSource", "type") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + )); + map.put( + "query/bytes", + new Metric( + new String[]{"dataSource", "type"}, + new Counter.Builder() + .namespace(namespace) + .name("query_bytes_total") + .labelNames("dataSource", "type") + .register() + ) + ); + map.put( + "query/node/time", + new Metric( + new String[]{"server"}, + new Histogram.Builder() + .namespace(namespace) + .name("query_node_time") + .labelNames("server") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "query/node/ttfb", + new Metric( + new String[]{"server"}, + new Histogram.Builder() + .namespace(namespace) + .name("query_node_ttfb_time") + .labelNames("server") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "query/node/bytes", + new Metric( + new String[]{"server"}, + new Counter.Builder() + .namespace(namespace) + .name("query_node_bytes_total") + .labelNames("server") + .register() + ) + ); + } - public static class Metric { - private final String[] dimensions; - private final SimpleCollector collector; - Metric(String[] dimensions, SimpleCollector collector) { - this.dimensions = dimensions; - this.collector = collector; - } + public static class Metric + { + private final String[] dimensions; + private final SimpleCollector collector; - public String[] getDimensions() { - return dimensions; - } + Metric(String[] dimensions, SimpleCollector collector) + { + this.dimensions = dimensions; + this.collector = collector; + } + + public String[] getDimensions() + { + return dimensions; + } - public SimpleCollector getCollector() { - return collector; - } + public SimpleCollector getCollector() + { + return collector; } + } } diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index ca6c42341841..d1b726e0b054 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -19,45 +19,51 @@ package org.apache.druid.emitter.prometheus; -import java.util.Map; +import io.prometheus.client.Counter; +import io.prometheus.client.Gauge; +import io.prometheus.client.Histogram; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.java.util.emitter.core.Emitter; import org.apache.druid.java.util.emitter.core.Event; import org.apache.druid.java.util.emitter.service.ServiceMetricEvent; - -import io.prometheus.client.Counter; -import io.prometheus.client.Gauge; -import io.prometheus.client.Histogram; +import java.util.Map; /** + * */ -public class PrometheusEmitter implements Emitter { +public class PrometheusEmitter implements Emitter +{ private static final Logger log = new Logger(PrometheusEmitter.class); private final Metrics metrics; - static PrometheusEmitter of(PrometheusEmitterConfig config) { + static PrometheusEmitter of(PrometheusEmitterConfig config) + { return new PrometheusEmitter(config); } - public PrometheusEmitter(PrometheusEmitterConfig config) { + public PrometheusEmitter(PrometheusEmitterConfig config) + { metrics = new Metrics(config.getNamespace()); } @Override - public void start() { + public void start() + { } @Override - public void emit(Event event) { + public void emit(Event event) + { if (event instanceof ServiceMetricEvent) { emitMetric((ServiceMetricEvent) event); } } - void emitMetric(ServiceMetricEvent metricEvent) { + void emitMetric(ServiceMetricEvent metricEvent) + { String host = metricEvent.getHost(); String service = metricEvent.getService(); String metric = metricEvent.getMetric(); @@ -86,10 +92,12 @@ void emitMetric(ServiceMetricEvent metricEvent) { } @Override - public void flush() { + public void flush() + { } @Override - public void close() { + public void close() + { } } diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java index aed6c282243f..c5065192b135 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java @@ -19,12 +19,13 @@ package org.apache.druid.emitter.prometheus; -import javax.annotation.Nullable; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.Nullable; + /** + * */ public class PrometheusEmitterConfig { @@ -32,13 +33,26 @@ public class PrometheusEmitterConfig @JsonProperty private final String namespace; + @JsonProperty + private final String path; + @JsonCreator - public PrometheusEmitterConfig(@JsonProperty("namespace") @Nullable String namespace) + public PrometheusEmitterConfig( + @JsonProperty("namespace") @Nullable String namespace, + @JsonProperty("path") @Nullable String path + ) { - this.namespace = namespace != null ? namespace : ""; + this.namespace = namespace != null ? namespace : "druid"; + this.path = path != null ? path : "/prometheus"; } - public String getNamespace() { + public String getNamespace() + { return namespace; } + + public String getPath() + { + return path; + } } diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java index 19ef691eb084..42ff7e7b2435 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java @@ -19,20 +19,26 @@ package org.apache.druid.emitter.prometheus; + import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.inject.Binder; import com.google.inject.Provides; import com.google.inject.name.Named; +import io.prometheus.client.exporter.MetricsServlet; import org.apache.druid.guice.JsonConfigProvider; import org.apache.druid.guice.ManageLifecycle; import org.apache.druid.initialization.DruidModule; import org.apache.druid.java.util.emitter.core.Emitter; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; import java.util.Collections; import java.util.List; + /** + * */ public class PrometheusEmitterModule implements DruidModule { @@ -53,8 +59,12 @@ public void configure(Binder binder) @Provides @ManageLifecycle @Named(EMITTER_TYPE) - public Emitter getEmitter(PrometheusEmitterConfig config) + public Emitter getEmitter(PrometheusEmitterConfig config, Server server) { + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + server.setHandler(context); + context.addServlet(new ServletHolder(new MetricsServlet()), config.getPath()); return PrometheusEmitter.of(config); } } diff --git a/pom.xml b/pom.xml index 6d28e2f42658..310186c309c7 100644 --- a/pom.xml +++ b/pom.xml @@ -184,6 +184,7 @@ extensions-contrib/moving-average-query extensions-contrib/tdigestsketch extensions-contrib/influxdb-emitter + extensions-contrib/prometheus-emitter distribution From 289b8c8cb19b34f01effe923903bfa411e1c655f Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Wed, 2 Oct 2019 14:13:14 -0700 Subject: [PATCH 003/107] unused variables --- .../org/apache/druid/emitter/prometheus/PrometheusEmitter.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index d1b726e0b054..a77a0a6b5222 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -64,8 +64,6 @@ public void emit(Event event) void emitMetric(ServiceMetricEvent metricEvent) { - String host = metricEvent.getHost(); - String service = metricEvent.getService(); String metric = metricEvent.getMetric(); Map userDims = metricEvent.getUserDims(); Number value = metricEvent.getValue(); From 431817684ad5b90181ada0f437872cb2c6588680 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Wed, 2 Oct 2019 14:49:56 -0700 Subject: [PATCH 004/107] better variable names --- .../emitter/prometheus/PrometheusEmitter.java | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index a77a0a6b5222..530cd94b2c79 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -64,29 +64,30 @@ public void emit(Event event) void emitMetric(ServiceMetricEvent metricEvent) { - String metric = metricEvent.getMetric(); + String name = metricEvent.getMetric(); Map userDims = metricEvent.getUserDims(); Number value = metricEvent.getValue(); - Metrics.Metric byName = metrics.getByName(metric); - String[] labelValues = new String[byName.getDimensions().length]; - String[] labelNames = byName.getDimensions(); - for (int i = 0; i < labelValues.length; i++) { - String labelName = labelNames[i]; - Object userDim = userDims.get(labelName); - labelValues[i] = userDim.toString(); + Metrics.Metric metric = metrics.getByName(name); + if (metric != null) { + String[] labelValues = new String[metric.getDimensions().length]; + String[] labelNames = metric.getDimensions(); + for (int i = 0; i < labelValues.length; i++) { + String labelName = labelNames[i]; + Object userDim = userDims.get(labelName); + labelValues[i] = userDim.toString(); + } + + if (metric.getCollector() instanceof Counter) { + ((Counter) metric.getCollector()).labels(labelValues).inc(value.doubleValue()); + } else if (metric.getCollector() instanceof Gauge) { + ((Gauge) metric.getCollector()).labels(labelValues).set(value.doubleValue()); + } else if (metric.getCollector() instanceof Histogram) { + ((Histogram) metric.getCollector()).labels(labelValues).observe(value.doubleValue()); + } else { + //TODO + } } - - if (byName.getCollector() instanceof Counter) { - ((Counter) byName.getCollector()).labels(labelValues).inc(value.doubleValue()); - } else if (byName.getCollector() instanceof Gauge) { - ((Gauge) byName.getCollector()).labels(labelValues).set(value.doubleValue()); - } else if (byName.getCollector() instanceof Histogram) { - ((Histogram) byName.getCollector()).labels(labelValues).observe(value.doubleValue()); - } else { - //TODO - } - } @Override From 2b365fca3c2973241b7235abb3972d5fa4a678a4 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Wed, 2 Oct 2019 14:56:34 -0700 Subject: [PATCH 005/107] removed unused dependencies --- extensions-contrib/prometheus-emitter/pom.xml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/extensions-contrib/prometheus-emitter/pom.xml b/extensions-contrib/prometheus-emitter/pom.xml index c909308c7024..9be9aba4bb45 100644 --- a/extensions-contrib/prometheus-emitter/pom.xml +++ b/extensions-contrib/prometheus-emitter/pom.xml @@ -45,11 +45,6 @@ simpleclient 0.7.0 - - io.prometheus - simpleclient_httpserver - 0.7.0 - com.google.code.findbugs jsr305 @@ -114,10 +109,6 @@ test-jar test - - io.dropwizard.metrics - metrics-core - org.eclipse.jetty jetty-server From 3ec989e3d977940d09a729994bac26a0f09b5ea2 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Thu, 3 Oct 2019 08:35:47 -0700 Subject: [PATCH 006/107] more metric definitions --- .../druid/emitter/prometheus/Metrics.java | 539 ++++++++++++++++++ 1 file changed, 539 insertions(+) diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java index 69fc55b804bc..a0ae8af32141 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -20,6 +20,7 @@ package org.apache.druid.emitter.prometheus; import io.prometheus.client.Counter; +import io.prometheus.client.Gauge; import io.prometheus.client.Histogram; import io.prometheus.client.SimpleCollector; @@ -36,6 +37,8 @@ public Metric getByName(String name) return map.get(name); } + //TODO: revise metric types + //TODO: revise Histogram bucket values public Metrics(String namespace) { @@ -94,6 +97,542 @@ public Metrics(String namespace) .register() ) ); + map.put( + "query/node/backpressure", + new Metric( + new String[]{"server"}, + new Histogram.Builder() + .namespace(namespace) + .name("query_node_backpressure_time") + .labelNames("server") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "query/intervalChunk/time", + new Metric( + new String[]{}, + new Histogram.Builder() + .namespace(namespace) + .name("query_interval_chunk_time") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "query/segment/time", + new Metric( + new String[]{}, + new Histogram.Builder() + .namespace(namespace) + .name("query_segment_time") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "query/wait/time", + new Metric( + new String[]{}, + new Histogram.Builder() + .namespace(namespace) + .name("query_wait_time") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "segment/scan/pending", + new Metric( + new String[]{}, + new Gauge.Builder() + .namespace(namespace) + .name("segment_scan_pending") + .register() + ) + ); + map.put( + "query/segmentAndCache/time", + new Metric( + new String[]{}, + new Histogram.Builder() + .namespace(namespace) + .name("query_segment_and_cache_time") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "query/cpu/time", + new Metric( + new String[]{"dataSource", "type"}, + new Histogram.Builder() + .namespace(namespace) + .name("query_cpu_time") + .labelNames("dataSource", "type") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "query/count", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_count_total") + .register() + ) + ); + map.put( + "query/success/count", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_success_count_total") + .register() + ) + ); + map.put( + "query/failed/count", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_failed_count_total") + .register() + ) + ); + map.put( + "query/interrupted/count", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_interrupted_count_total") + .register() + ) + ); + map.put( + "query/cache/delta/numEntries", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_cache_delta_numentries_total") + .register() + ) + ); + map.put( + "query/cache/delta/sizeBytes", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_cache_delta_sizebytes_total") + .register() + ) + ); + map.put( + "query/cache/delta/hits", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_cache_delta_hits_total") + .register() + ) + ); + map.put( + "query/cache/delta/misses", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_cache_delta_misses_total") + .register() + ) + ); + map.put( + "query/cache/delta/evictions", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_cache_delta_evictions_total") + .register() + ) + ); + // Leaning toward ignoring this since it should be derived from delta/hits above + // "query/cache/delta/hitRate" : { "dimensions" : [], "type" : "count", "convertRange" : true }, + + map.put( + "query/cache/delta/averageBytes", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_cache_delta_averagebytes_total") + .register() + ) + ); + map.put( + "query/cache/delta/timeouts", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_cache_delta_timeouts_total") + .register() + ) + ); + map.put( + "query/cache/delta/errors", + new Metric( + new String[]{}, + new Counter.Builder() + .namespace(namespace) + .name("query_cache_delta_errors_total") + .register() + ) + ); + + map.put( + "query/cache/total/numentries", + new Metric( + new String[]{}, + new Gauge.Builder() + .namespace(namespace) + .name("query_cache_total_numentries") + .register() + ) + ); + map.put( + "query/cache/total/sizeBytes", + new Metric( + new String[]{}, + new Gauge.Builder() + .namespace(namespace) + .name("query_cache_total_sizebytes") + .register() + ) + ); + map.put( + "query/cache/total/hits", + new Metric( + new String[]{}, + new Gauge.Builder() + .namespace(namespace) + .name("query_cache_total_hits") + .register() + ) + ); + map.put( + "query/cache/total/misses", + new Metric( + new String[]{}, + new Gauge.Builder() + .namespace(namespace) + .name("query_cache_total_misses") + .register() + ) + ); + map.put( + "query/cache/total/evictions", + new Metric( + new String[]{}, + new Gauge.Builder() + .namespace(namespace) + .name("query_cache_total_evictions") + .register() + ) + ); + map.put( + "query/cache/total/hitRate", + new Metric( + new String[]{}, + new Gauge.Builder() + .namespace(namespace) + .name("query_cache_total_hitrate") + .register() + ) + ); + map.put( + "query/cache/total/averageBytes", + new Metric( + new String[]{}, + new Gauge.Builder() + .namespace(namespace) + .name("query_cache_total_averagebytes") + .register() + ) + ); + map.put( + "query/cache/total/timeouts", + new Metric( + new String[]{}, + new Gauge.Builder() + .namespace(namespace) + .name("query_cache_total_timeouts") + .register() + ) + ); + map.put( + "query/cache/total/errors", + new Metric( + new String[]{}, + new Gauge.Builder() + .namespace(namespace) + .name("query_cache_total_errors") + .register() + ) + ); + map.put( + "ingest/events/thrownAway", + new Metric( + new String[]{"dataSource"}, + new Counter.Builder() + .namespace(namespace) + .name("ingest_events_thrownaway_total") + .labelNames("dataSource") + .register() + ) + ); + map.put( + "ingest/events/unparseable", + new Metric( + new String[]{"dataSource"}, + new Counter.Builder() + .namespace(namespace) + .name("ingest_events_unparseable_total") + .labelNames("dataSource") + .register() + ) + ); + map.put( + "ingest/events/duplicate", + new Metric( + new String[]{"dataSource"}, + new Counter.Builder() + .namespace(namespace) + .name("ingest_events_duplicate_total") + .labelNames("dataSource") + .register() + ) + ); + map.put( + "ingest/events/processed", + new Metric( + new String[]{"dataSource"}, + new Counter.Builder() + .namespace(namespace) + .name("ingest_events_processed_total") + .labelNames("dataSource") + .register() + ) + ); + map.put( + "ingest/events/messageGap", + new Metric( + new String[]{"dataSource"}, + new Counter.Builder() + .namespace(namespace) + .name("ingest_events_messagegap_total") + .labelNames("dataSource") + .register() + ) + ); + map.put( + "ingest/rows/output", + new Metric( + new String[]{"dataSource"}, + new Counter.Builder() + .namespace(namespace) + .name("ingest_rows_output_total") + .labelNames("dataSource") + .register() + ) + ); + map.put( + "ingest/persists/count", + new Metric( + new String[]{"dataSource"}, + new Counter.Builder() + .namespace(namespace) + .name("ingest_persists_count_total") + .labelNames("dataSource") + .register() + ) + ); + map.put( + "ingest/persists/time", + new Metric( + new String[]{"dataSource"}, + new Histogram.Builder() + .namespace(namespace) + .name("ingest_persists_time") + .labelNames("dataSource") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "ingest/persists/cpu", + new Metric( + new String[]{"dataSource"}, + new Histogram.Builder() + .namespace(namespace) + .name("ingest_persists_cpu_time") + .labelNames("dataSource") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "ingest/persists/backPressure", + new Metric( + new String[]{"dataSource"}, + new Gauge.Builder() + .namespace(namespace) + .name("ingest_persists_backpressure") + .labelNames("dataSource") + .register() + ) + ); + map.put( + "ingest/persists/failed", + new Metric( + new String[]{"dataSource"}, + new Counter.Builder() + .namespace(namespace) + .name("ingest_persists_failed_total") + .labelNames("dataSource") + .register() + ) + ); + map.put( + "ingest/handoff/failed", + new Metric( + new String[]{"dataSource"}, + new Counter.Builder() + .namespace(namespace) + .name("ingest_handoff_failed_total") + .labelNames("dataSource") + .register() + ) + ); + map.put( + "ingest/merge/time", + new Metric( + new String[]{"dataSource"}, + new Histogram.Builder() + .namespace(namespace) + .name("ingest_merge_time") + .labelNames("dataSource") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "ingest/merge/cpu", + new Metric( + new String[]{"dataSource"}, + new Histogram.Builder() + .namespace(namespace) + .name("ingest_merge_cpu_time") + .labelNames("dataSource") + .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) + .register() + ) + ); + map.put( + "ingest/kafka/lag", + new Metric( + new String[]{"dataSource"}, + new Gauge.Builder() + .namespace(namespace) + .name("ingest_kafka_lag") + .labelNames("dataSource") + .register() + ) + ); + + +// "ingest/kafka/lag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, +// "ingest/kafka/maxLag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, +// "ingest/kafka/avgLag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, +// +// "task/success/count" : { "dimensions" : ["dataSource"], "type" : "count" }, +// "task/failed/count" : { "dimensions" : ["dataSource"], "type" : "count" }, +// "task/running/count" : { "dimensions" : ["dataSource"], "type" : "count" }, +// "task/pending/count" : { "dimensions" : ["dataSource"], "type" : "count" }, +// "task/waiting/count" : { "dimensions" : ["dataSource"], "type" : "count" }, +// +// "task/run/time" : { "dimensions" : ["dataSource", "taskType"], "type" : "timer" }, +// "segment/added/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, +// "segment/moved/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, +// "segment/nuked/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, +// +// "segment/assigned/count" : { "dimensions" : ["tier"], "type" : "count" }, +// "segment/moved/count" : { "dimensions" : ["tier"], "type" : "count" }, +// "segment/dropped/count" : { "dimensions" : ["tier"], "type" : "count" }, +// "segment/deleted/count" : { "dimensions" : ["tier"], "type" : "count" }, +// "segment/unneeded/count" : { "dimensions" : ["tier"], "type" : "count" }, +// "segment/unavailable/count" : { "dimensions" : ["dataSource"], "type" : "count" }, +// "segment/underReplicated/count" : { "dimensions" : ["dataSource", "tier"], "type" : "count" }, +// "segment/cost/raw" : { "dimensions" : ["tier"], "type" : "count" }, +// "segment/cost/normalization" : { "dimensions" : ["tier"], "type" : "count" }, +// "segment/cost/normalized" : { "dimensions" : ["tier"], "type" : "count" }, +// "segment/loadQueue/size" : { "dimensions" : ["server"], "type" : "gauge" }, +// "segment/loadQueue/failed" : { "dimensions" : ["server"], "type" : "gauge" }, +// "segment/loadQueue/count" : { "dimensions" : ["server"], "type" : "gauge" }, +// "segment/dropQueue/count" : { "dimensions" : ["server"], "type" : "gauge" }, +// "segment/size" : { "dimensions" : ["dataSource"], "type" : "gauge" }, +// "segment/overShadowed/count" : { "dimensions" : [], "type" : "gauge" }, +// +// "segment/max" : { "dimensions" : [], "type" : "gauge"}, +// "segment/used" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge" }, +// "segment/usedPercent" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge", "convertRange" : true }, +// "segment/pendingDelete" : { "dimensions" : [], "type" : "gauge"}, +// +// "jvm/pool/committed" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, +// "jvm/pool/init" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, +// "jvm/pool/max" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, +// "jvm/pool/used" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, +// "jvm/bufferpool/count" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, +// "jvm/bufferpool/used" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, +// "jvm/bufferpool/capacity" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, +// "jvm/mem/init" : { "dimensions" : ["memKind"], "type" : "gauge" }, +// "jvm/mem/max" : { "dimensions" : ["memKind"], "type" : "gauge" }, +// "jvm/mem/used" : { "dimensions" : ["memKind"], "type" : "gauge" }, +// "jvm/mem/committed" : { "dimensions" : ["memKind"], "type" : "gauge" }, +// "jvm/gc/count" : { "dimensions" : ["gcName"], "type" : "count" }, +// "jvm/gc/cpu" : { "dimensions" : ["gcName"], "type" : "timer" }, +// +// "ingest/events/buffered" : { "dimensions" : ["serviceName, bufferCapacity"], "type" : "gauge"}, +// +// "sys/swap/free" : { "dimensions" : [], "type" : "gauge"}, +// "sys/swap/max" : { "dimensions" : [], "type" : "gauge"}, +// "sys/swap/pageIn" : { "dimensions" : [], "type" : "gauge"}, +// "sys/swap/pageOut" : { "dimensions" : [], "type" : "gauge"}, +// "sys/disk/write/count" : { "dimensions" : ["fsDevName"], "type" : "count"}, +// "sys/disk/read/count" : { "dimensions" : ["fsDevName"], "type" : "count"}, +// "sys/disk/write/size" : { "dimensions" : ["fsDevName"], "type" : "count"}, +// "sys/disk/read/size" : { "dimensions" : ["fsDevName"], "type" : "count"}, +// "sys/net/write/size" : { "dimensions" : [], "type" : "count"}, +// "sys/net/read/size" : { "dimensions" : [], "type" : "count"}, +// "sys/fs/used" : { "dimensions" : ["fsDevName", "fsDirName", "fsTypeName", "fsSysTypeName", "fsOptions"], "type" : "gauge"}, +// "sys/fs/max" : { "dimensions" : ["fsDevName", "fsDirName", "fsTypeName", "fsSysTypeName", "fsOptions"], "type" : "gauge"}, +// "sys/mem/used" : { "dimensions" : [], "type" : "gauge"}, +// "sys/mem/max" : { "dimensions" : [], "type" : "gauge"}, +// "sys/storage/used" : { "dimensions" : ["fsDirName"], "type" : "gauge"}, +// "sys/cpu" : { "dimensions" : ["cpuName", "cpuTime"], "type" : "gauge"}, +// +// "coordinator-segment/count" : { "dimensions" : ["dataSource"], "type" : "gauge" }, +// "historical-segment/count" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge" } + } From 75095e7c279c7238567b942497b749a96d5945b2 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Thu, 3 Oct 2019 13:40:59 -0700 Subject: [PATCH 007/107] reorganize --- .../prometheus/DimensionsAndCollector.java | 44 ++ .../druid/emitter/prometheus/Metrics.java | 691 +++--------------- .../emitter/prometheus/PrometheusEmitter.java | 4 +- .../prometheus/PrometheusEmitterConfig.java | 13 +- .../src/main/resources/defaultMetrics.json | 128 ++++ 5 files changed, 268 insertions(+), 612 deletions(-) create mode 100644 extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/DimensionsAndCollector.java create mode 100644 extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/DimensionsAndCollector.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/DimensionsAndCollector.java new file mode 100644 index 000000000000..64279d9d0939 --- /dev/null +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/DimensionsAndCollector.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.emitter.prometheus; + +import io.prometheus.client.SimpleCollector; + +public class DimensionsAndCollector +{ + private final String[] dimensions; + private final SimpleCollector collector; + + DimensionsAndCollector(String[] dimensions, SimpleCollector collector) + { + this.dimensions = dimensions; + this.collector = collector; + } + + public String[] getDimensions() + { + return dimensions; + } + + public SimpleCollector getCollector() + { + return collector; + } +} diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java index a0ae8af32141..7d8e58688795 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -19,643 +19,116 @@ package org.apache.druid.emitter.prometheus; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Strings; import io.prometheus.client.Counter; import io.prometheus.client.Gauge; import io.prometheus.client.Histogram; import io.prometheus.client.SimpleCollector; - +import org.apache.druid.java.util.common.ISE; +import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.java.util.common.logger.Logger; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import java.util.SortedSet; public class Metrics { - private Map map = new HashMap<>(); + private static final Logger log = new Logger(Metrics.class); + private final Map map = new HashMap<>(); + private final ObjectMapper mapper = new ObjectMapper(); - public Metric getByName(String name) + public DimensionsAndCollector getByName(String name) { return map.get(name); } - //TODO: revise metric types - //TODO: revise Histogram bucket values - public Metrics(String namespace) + public Metrics(String namespace, String path) { - - map.put("query/time", new Metric( - new String[]{"dataSource", "type"}, - new Histogram.Builder() + Map metrics = readMap(path); + for (String name : metrics.keySet()) { + Metric metric = metrics.get(name); + Metric.Type type = metric.type; + String[] dimensions = metric.dimensions.toArray(new String[0]); + String formattedName = StringUtils.replaceChar(StringUtils.toLowerCase(name), '/', "_"); + SimpleCollector collector = null; + if (Metric.Type.count.equals(type)) { + collector = new Counter.Builder() .namespace(namespace) - .name("query_time") - .labelNames("dataSource", "type") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - )); - map.put( - "query/bytes", - new Metric( - new String[]{"dataSource", "type"}, - new Counter.Builder() - .namespace(namespace) - .name("query_bytes_total") - .labelNames("dataSource", "type") - .register() - ) - ); - map.put( - "query/node/time", - new Metric( - new String[]{"server"}, - new Histogram.Builder() - .namespace(namespace) - .name("query_node_time") - .labelNames("server") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "query/node/ttfb", - new Metric( - new String[]{"server"}, - new Histogram.Builder() - .namespace(namespace) - .name("query_node_ttfb_time") - .labelNames("server") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "query/node/bytes", - new Metric( - new String[]{"server"}, - new Counter.Builder() - .namespace(namespace) - .name("query_node_bytes_total") - .labelNames("server") - .register() - ) - ); - map.put( - "query/node/backpressure", - new Metric( - new String[]{"server"}, - new Histogram.Builder() - .namespace(namespace) - .name("query_node_backpressure_time") - .labelNames("server") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "query/intervalChunk/time", - new Metric( - new String[]{}, - new Histogram.Builder() - .namespace(namespace) - .name("query_interval_chunk_time") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "query/segment/time", - new Metric( - new String[]{}, - new Histogram.Builder() - .namespace(namespace) - .name("query_segment_time") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "query/wait/time", - new Metric( - new String[]{}, - new Histogram.Builder() - .namespace(namespace) - .name("query_wait_time") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "segment/scan/pending", - new Metric( - new String[]{}, - new Gauge.Builder() - .namespace(namespace) - .name("segment_scan_pending") - .register() - ) - ); - map.put( - "query/segmentAndCache/time", - new Metric( - new String[]{}, - new Histogram.Builder() - .namespace(namespace) - .name("query_segment_and_cache_time") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "query/cpu/time", - new Metric( - new String[]{"dataSource", "type"}, - new Histogram.Builder() - .namespace(namespace) - .name("query_cpu_time") - .labelNames("dataSource", "type") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "query/count", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_count_total") - .register() - ) - ); - map.put( - "query/success/count", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_success_count_total") - .register() - ) - ); - map.put( - "query/failed/count", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_failed_count_total") - .register() - ) - ); - map.put( - "query/interrupted/count", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_interrupted_count_total") - .register() - ) - ); - map.put( - "query/cache/delta/numEntries", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_cache_delta_numentries_total") - .register() - ) - ); - map.put( - "query/cache/delta/sizeBytes", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_cache_delta_sizebytes_total") - .register() - ) - ); - map.put( - "query/cache/delta/hits", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_cache_delta_hits_total") - .register() - ) - ); - map.put( - "query/cache/delta/misses", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_cache_delta_misses_total") - .register() - ) - ); - map.put( - "query/cache/delta/evictions", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_cache_delta_evictions_total") - .register() - ) - ); - // Leaning toward ignoring this since it should be derived from delta/hits above - // "query/cache/delta/hitRate" : { "dimensions" : [], "type" : "count", "convertRange" : true }, - - map.put( - "query/cache/delta/averageBytes", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_cache_delta_averagebytes_total") - .register() - ) - ); - map.put( - "query/cache/delta/timeouts", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_cache_delta_timeouts_total") - .register() - ) - ); - map.put( - "query/cache/delta/errors", - new Metric( - new String[]{}, - new Counter.Builder() - .namespace(namespace) - .name("query_cache_delta_errors_total") - .register() - ) - ); - - map.put( - "query/cache/total/numentries", - new Metric( - new String[]{}, - new Gauge.Builder() - .namespace(namespace) - .name("query_cache_total_numentries") - .register() - ) - ); - map.put( - "query/cache/total/sizeBytes", - new Metric( - new String[]{}, - new Gauge.Builder() - .namespace(namespace) - .name("query_cache_total_sizebytes") - .register() - ) - ); - map.put( - "query/cache/total/hits", - new Metric( - new String[]{}, - new Gauge.Builder() - .namespace(namespace) - .name("query_cache_total_hits") - .register() - ) - ); - map.put( - "query/cache/total/misses", - new Metric( - new String[]{}, - new Gauge.Builder() - .namespace(namespace) - .name("query_cache_total_misses") - .register() - ) - ); - map.put( - "query/cache/total/evictions", - new Metric( - new String[]{}, - new Gauge.Builder() - .namespace(namespace) - .name("query_cache_total_evictions") - .register() - ) - ); - map.put( - "query/cache/total/hitRate", - new Metric( - new String[]{}, - new Gauge.Builder() - .namespace(namespace) - .name("query_cache_total_hitrate") - .register() - ) - ); - map.put( - "query/cache/total/averageBytes", - new Metric( - new String[]{}, - new Gauge.Builder() - .namespace(namespace) - .name("query_cache_total_averagebytes") - .register() - ) - ); - map.put( - "query/cache/total/timeouts", - new Metric( - new String[]{}, - new Gauge.Builder() - .namespace(namespace) - .name("query_cache_total_timeouts") - .register() - ) - ); - map.put( - "query/cache/total/errors", - new Metric( - new String[]{}, - new Gauge.Builder() - .namespace(namespace) - .name("query_cache_total_errors") - .register() - ) - ); - map.put( - "ingest/events/thrownAway", - new Metric( - new String[]{"dataSource"}, - new Counter.Builder() - .namespace(namespace) - .name("ingest_events_thrownaway_total") - .labelNames("dataSource") - .register() - ) - ); - map.put( - "ingest/events/unparseable", - new Metric( - new String[]{"dataSource"}, - new Counter.Builder() - .namespace(namespace) - .name("ingest_events_unparseable_total") - .labelNames("dataSource") - .register() - ) - ); - map.put( - "ingest/events/duplicate", - new Metric( - new String[]{"dataSource"}, - new Counter.Builder() - .namespace(namespace) - .name("ingest_events_duplicate_total") - .labelNames("dataSource") - .register() - ) - ); - map.put( - "ingest/events/processed", - new Metric( - new String[]{"dataSource"}, - new Counter.Builder() - .namespace(namespace) - .name("ingest_events_processed_total") - .labelNames("dataSource") - .register() - ) - ); - map.put( - "ingest/events/messageGap", - new Metric( - new String[]{"dataSource"}, - new Counter.Builder() - .namespace(namespace) - .name("ingest_events_messagegap_total") - .labelNames("dataSource") - .register() - ) - ); - map.put( - "ingest/rows/output", - new Metric( - new String[]{"dataSource"}, - new Counter.Builder() - .namespace(namespace) - .name("ingest_rows_output_total") - .labelNames("dataSource") - .register() - ) - ); - map.put( - "ingest/persists/count", - new Metric( - new String[]{"dataSource"}, - new Counter.Builder() - .namespace(namespace) - .name("ingest_persists_count_total") - .labelNames("dataSource") - .register() - ) - ); - map.put( - "ingest/persists/time", - new Metric( - new String[]{"dataSource"}, - new Histogram.Builder() - .namespace(namespace) - .name("ingest_persists_time") - .labelNames("dataSource") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "ingest/persists/cpu", - new Metric( - new String[]{"dataSource"}, - new Histogram.Builder() - .namespace(namespace) - .name("ingest_persists_cpu_time") - .labelNames("dataSource") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "ingest/persists/backPressure", - new Metric( - new String[]{"dataSource"}, - new Gauge.Builder() - .namespace(namespace) - .name("ingest_persists_backpressure") - .labelNames("dataSource") - .register() - ) - ); - map.put( - "ingest/persists/failed", - new Metric( - new String[]{"dataSource"}, - new Counter.Builder() - .namespace(namespace) - .name("ingest_persists_failed_total") - .labelNames("dataSource") - .register() - ) - ); - map.put( - "ingest/handoff/failed", - new Metric( - new String[]{"dataSource"}, - new Counter.Builder() - .namespace(namespace) - .name("ingest_handoff_failed_total") - .labelNames("dataSource") - .register() - ) - ); - map.put( - "ingest/merge/time", - new Metric( - new String[]{"dataSource"}, - new Histogram.Builder() - .namespace(namespace) - .name("ingest_merge_time") - .labelNames("dataSource") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "ingest/merge/cpu", - new Metric( - new String[]{"dataSource"}, - new Histogram.Builder() - .namespace(namespace) - .name("ingest_merge_cpu_time") - .labelNames("dataSource") - .buckets(new double[]{.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300}) - .register() - ) - ); - map.put( - "ingest/kafka/lag", - new Metric( - new String[]{"dataSource"}, - new Gauge.Builder() - .namespace(namespace) - .name("ingest_kafka_lag") - .labelNames("dataSource") - .register() - ) - ); - - -// "ingest/kafka/lag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, -// "ingest/kafka/maxLag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, -// "ingest/kafka/avgLag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, -// -// "task/success/count" : { "dimensions" : ["dataSource"], "type" : "count" }, -// "task/failed/count" : { "dimensions" : ["dataSource"], "type" : "count" }, -// "task/running/count" : { "dimensions" : ["dataSource"], "type" : "count" }, -// "task/pending/count" : { "dimensions" : ["dataSource"], "type" : "count" }, -// "task/waiting/count" : { "dimensions" : ["dataSource"], "type" : "count" }, -// -// "task/run/time" : { "dimensions" : ["dataSource", "taskType"], "type" : "timer" }, -// "segment/added/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, -// "segment/moved/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, -// "segment/nuked/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, -// -// "segment/assigned/count" : { "dimensions" : ["tier"], "type" : "count" }, -// "segment/moved/count" : { "dimensions" : ["tier"], "type" : "count" }, -// "segment/dropped/count" : { "dimensions" : ["tier"], "type" : "count" }, -// "segment/deleted/count" : { "dimensions" : ["tier"], "type" : "count" }, -// "segment/unneeded/count" : { "dimensions" : ["tier"], "type" : "count" }, -// "segment/unavailable/count" : { "dimensions" : ["dataSource"], "type" : "count" }, -// "segment/underReplicated/count" : { "dimensions" : ["dataSource", "tier"], "type" : "count" }, -// "segment/cost/raw" : { "dimensions" : ["tier"], "type" : "count" }, -// "segment/cost/normalization" : { "dimensions" : ["tier"], "type" : "count" }, -// "segment/cost/normalized" : { "dimensions" : ["tier"], "type" : "count" }, -// "segment/loadQueue/size" : { "dimensions" : ["server"], "type" : "gauge" }, -// "segment/loadQueue/failed" : { "dimensions" : ["server"], "type" : "gauge" }, -// "segment/loadQueue/count" : { "dimensions" : ["server"], "type" : "gauge" }, -// "segment/dropQueue/count" : { "dimensions" : ["server"], "type" : "gauge" }, -// "segment/size" : { "dimensions" : ["dataSource"], "type" : "gauge" }, -// "segment/overShadowed/count" : { "dimensions" : [], "type" : "gauge" }, -// -// "segment/max" : { "dimensions" : [], "type" : "gauge"}, -// "segment/used" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge" }, -// "segment/usedPercent" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge", "convertRange" : true }, -// "segment/pendingDelete" : { "dimensions" : [], "type" : "gauge"}, -// -// "jvm/pool/committed" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, -// "jvm/pool/init" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, -// "jvm/pool/max" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, -// "jvm/pool/used" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, -// "jvm/bufferpool/count" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, -// "jvm/bufferpool/used" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, -// "jvm/bufferpool/capacity" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, -// "jvm/mem/init" : { "dimensions" : ["memKind"], "type" : "gauge" }, -// "jvm/mem/max" : { "dimensions" : ["memKind"], "type" : "gauge" }, -// "jvm/mem/used" : { "dimensions" : ["memKind"], "type" : "gauge" }, -// "jvm/mem/committed" : { "dimensions" : ["memKind"], "type" : "gauge" }, -// "jvm/gc/count" : { "dimensions" : ["gcName"], "type" : "count" }, -// "jvm/gc/cpu" : { "dimensions" : ["gcName"], "type" : "timer" }, -// -// "ingest/events/buffered" : { "dimensions" : ["serviceName, bufferCapacity"], "type" : "gauge"}, -// -// "sys/swap/free" : { "dimensions" : [], "type" : "gauge"}, -// "sys/swap/max" : { "dimensions" : [], "type" : "gauge"}, -// "sys/swap/pageIn" : { "dimensions" : [], "type" : "gauge"}, -// "sys/swap/pageOut" : { "dimensions" : [], "type" : "gauge"}, -// "sys/disk/write/count" : { "dimensions" : ["fsDevName"], "type" : "count"}, -// "sys/disk/read/count" : { "dimensions" : ["fsDevName"], "type" : "count"}, -// "sys/disk/write/size" : { "dimensions" : ["fsDevName"], "type" : "count"}, -// "sys/disk/read/size" : { "dimensions" : ["fsDevName"], "type" : "count"}, -// "sys/net/write/size" : { "dimensions" : [], "type" : "count"}, -// "sys/net/read/size" : { "dimensions" : [], "type" : "count"}, -// "sys/fs/used" : { "dimensions" : ["fsDevName", "fsDirName", "fsTypeName", "fsSysTypeName", "fsOptions"], "type" : "gauge"}, -// "sys/fs/max" : { "dimensions" : ["fsDevName", "fsDirName", "fsTypeName", "fsSysTypeName", "fsOptions"], "type" : "gauge"}, -// "sys/mem/used" : { "dimensions" : [], "type" : "gauge"}, -// "sys/mem/max" : { "dimensions" : [], "type" : "gauge"}, -// "sys/storage/used" : { "dimensions" : ["fsDirName"], "type" : "gauge"}, -// "sys/cpu" : { "dimensions" : ["cpuName", "cpuTime"], "type" : "gauge"}, -// -// "coordinator-segment/count" : { "dimensions" : ["dataSource"], "type" : "gauge" }, -// "historical-segment/count" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge" } + .name(formattedName) + .labelNames(dimensions) + .register(); + } else if (Metric.Type.gauge.equals(type)) { + collector = new Gauge.Builder() + .namespace(namespace) + .name(formattedName) + .labelNames(dimensions) + .register(); + } else if (Metric.Type.timer.equals(type)) { + collector = new Histogram.Builder() + .namespace(namespace) + .name(formattedName) + .labelNames(dimensions) + .buckets(.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300) + .register(); + } else { + log.error("Unrecognized metric type [%s]", type); + } + + if (collector != null) { + map.put(name, new DimensionsAndCollector(dimensions, collector)); + } + } } + private Map readMap(String path) + { + try { + InputStream is; + if (Strings.isNullOrEmpty(path)) { + log.info("Using default metric dimension and types"); + is = this.getClass().getClassLoader().getResourceAsStream("defaultMetrics.json"); + } else { + log.info("Using metric dimensions at types at [%s]", path); + is = new FileInputStream(new File(path)); + } + return mapper.readerFor(new TypeReference>() + { + }).readValue(is); + } + catch (IOException e) { + throw new ISE(e, "Failed to parse metric dimensions and types"); + } + } public static class Metric { - private final String[] dimensions; - private final SimpleCollector collector; - - Metric(String[] dimensions, SimpleCollector collector) + public final SortedSet dimensions; + public final Type type; + + @JsonCreator + public Metric( + @JsonProperty("dimensions") SortedSet dimensions, + @JsonProperty("type") Type type + ) { this.dimensions = dimensions; - this.collector = collector; + this.type = type; } - public String[] getDimensions() + public enum Type { - return dimensions; - } - - public SimpleCollector getCollector() - { - return collector; + count, gauge, timer } } - } diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index 530cd94b2c79..7748c5eb6e5c 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -45,7 +45,7 @@ static PrometheusEmitter of(PrometheusEmitterConfig config) public PrometheusEmitter(PrometheusEmitterConfig config) { - metrics = new Metrics(config.getNamespace()); + metrics = new Metrics(config.getNamespace(), config.getDimensionMapPath()); } @@ -68,7 +68,7 @@ void emitMetric(ServiceMetricEvent metricEvent) Map userDims = metricEvent.getUserDims(); Number value = metricEvent.getValue(); - Metrics.Metric metric = metrics.getByName(name); + DimensionsAndCollector metric = metrics.getByName(name); if (metric != null) { String[] labelValues = new String[metric.getDimensions().length]; String[] labelNames = metric.getDimensions(); diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java index c5065192b135..83315e79dd17 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java @@ -36,14 +36,20 @@ public class PrometheusEmitterConfig @JsonProperty private final String path; + @JsonProperty + @Nullable + private final String dimensionMapPath; + @JsonCreator public PrometheusEmitterConfig( @JsonProperty("namespace") @Nullable String namespace, - @JsonProperty("path") @Nullable String path + @JsonProperty("path") @Nullable String path, + @JsonProperty("dimensionMapPath") @Nullable String dimensionMapPath ) { this.namespace = namespace != null ? namespace : "druid"; this.path = path != null ? path : "/prometheus"; + this.dimensionMapPath = dimensionMapPath; } public String getNamespace() @@ -55,4 +61,9 @@ public String getPath() { return path; } + + public String getDimensionMapPath() + { + return dimensionMapPath; + } } diff --git a/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json new file mode 100644 index 000000000000..ac5077656cca --- /dev/null +++ b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json @@ -0,0 +1,128 @@ +{ + "query/time" : { "dimensions" : ["dataSource", "type"], "type" : "timer"}, + "query/bytes" : { "dimensions" : ["dataSource", "type"], "type" : "count"}, + "query/node/time" : { "dimensions" : ["server"], "type" : "timer"}, + "query/node/ttfb" : { "dimensions" : ["server"], "type" : "timer"}, + "query/node/bytes" : { "dimensions" : ["server"], "type" : "count"}, + "query/node/backpressure": { "dimensions" : ["server"], "type" : "timer"}, + "query/intervalChunk/time" : { "dimensions" : [], "type" : "timer"}, + + "query/segment/time" : { "dimensions" : [], "type" : "timer"}, + "query/wait/time" : { "dimensions" : [], "type" : "timer"}, + "segment/scan/pending" : { "dimensions" : [], "type" : "gauge"}, + "query/segmentAndCache/time" : { "dimensions" : [], "type" : "timer" }, + "query/cpu/time" : { "dimensions" : ["dataSource", "type"], "type" : "timer" }, + + "query/count" : { "dimensions" : [], "type" : "count" }, + "query/success/count" : { "dimensions" : [], "type" : "count" }, + "query/failed/count" : { "dimensions" : [], "type" : "count" }, + "query/interrupted/count" : { "dimensions" : [], "type" : "count" }, + + "query/cache/delta/numEntries" : { "dimensions" : [], "type" : "count" }, + "query/cache/delta/sizeBytes" : { "dimensions" : [], "type" : "count" }, + "query/cache/delta/hits" : { "dimensions" : [], "type" : "count" }, + "query/cache/delta/misses" : { "dimensions" : [], "type" : "count" }, + "query/cache/delta/evictions" : { "dimensions" : [], "type" : "count" }, + "query/cache/delta/hitRate" : { "dimensions" : [], "type" : "count"}, + "query/cache/delta/averageBytes" : { "dimensions" : [], "type" : "count" }, + "query/cache/delta/timeouts" : { "dimensions" : [], "type" : "count" }, + "query/cache/delta/errors" : { "dimensions" : [], "type" : "count" }, + + "query/cache/total/numEntries" : { "dimensions" : [], "type" : "gauge" }, + "query/cache/total/sizeBytes" : { "dimensions" : [], "type" : "gauge" }, + "query/cache/total/hits" : { "dimensions" : [], "type" : "gauge" }, + "query/cache/total/misses" : { "dimensions" : [], "type" : "gauge" }, + "query/cache/total/evictions" : { "dimensions" : [], "type" : "gauge" }, + "query/cache/total/hitRate" : { "dimensions" : [], "type" : "gauge"}, + "query/cache/total/averageBytes" : { "dimensions" : [], "type" : "gauge" }, + "query/cache/total/timeouts" : { "dimensions" : [], "type" : "gauge" }, + "query/cache/total/errors" : { "dimensions" : [], "type" : "gauge" }, + + "ingest/events/thrownAway" : { "dimensions" : ["dataSource"], "type" : "count" }, + "ingest/events/unparseable" : { "dimensions" : ["dataSource"], "type" : "count" }, + "ingest/events/duplicate" : { "dimensions" : ["dataSource"], "type" : "count" }, + "ingest/events/processed" : { "dimensions" : ["dataSource"], "type" : "count" }, + "ingest/events/messageGap" : { "dimensions" : ["dataSource"], "type" : "gauge" }, + "ingest/rows/output" : { "dimensions" : ["dataSource"], "type" : "count" }, + "ingest/persists/count" : { "dimensions" : ["dataSource"], "type" : "count" }, + "ingest/persists/time" : { "dimensions" : ["dataSource"], "type" : "timer" }, + "ingest/persists/cpu" : { "dimensions" : ["dataSource"], "type" : "timer" }, + "ingest/persists/backPressure" : { "dimensions" : ["dataSource"], "type" : "gauge" }, + "ingest/persists/failed" : { "dimensions" : ["dataSource"], "type" : "count" }, + "ingest/handoff/failed" : { "dimensions" : ["dataSource"], "type" : "count" }, + "ingest/merge/time" : { "dimensions" : ["dataSource"], "type" : "timer" }, + "ingest/merge/cpu" : { "dimensions" : ["dataSource"], "type" : "timer" }, + + "ingest/kafka/lag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, + "ingest/kafka/maxLag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, + "ingest/kafka/avgLag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, + + "task/success/count" : { "dimensions" : ["dataSource"], "type" : "count" }, + "task/failed/count" : { "dimensions" : ["dataSource"], "type" : "count" }, + "task/running/count" : { "dimensions" : ["dataSource"], "type" : "count" }, + "task/pending/count" : { "dimensions" : ["dataSource"], "type" : "count" }, + "task/waiting/count" : { "dimensions" : ["dataSource"], "type" : "count" }, + + "task/run/time" : { "dimensions" : ["dataSource", "taskType"], "type" : "timer" }, + "segment/added/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, + "segment/moved/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, + "segment/nuked/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, + + "segment/assigned/count" : { "dimensions" : ["tier"], "type" : "count" }, + "segment/moved/count" : { "dimensions" : ["tier"], "type" : "count" }, + "segment/dropped/count" : { "dimensions" : ["tier"], "type" : "count" }, + "segment/deleted/count" : { "dimensions" : ["tier"], "type" : "count" }, + "segment/unneeded/count" : { "dimensions" : ["tier"], "type" : "count" }, + "segment/unavailable/count" : { "dimensions" : ["dataSource"], "type" : "count" }, + "segment/underReplicated/count" : { "dimensions" : ["dataSource", "tier"], "type" : "count" }, + "segment/cost/raw" : { "dimensions" : ["tier"], "type" : "count" }, + "segment/cost/normalization" : { "dimensions" : ["tier"], "type" : "count" }, + "segment/cost/normalized" : { "dimensions" : ["tier"], "type" : "count" }, + "segment/loadQueue/size" : { "dimensions" : ["server"], "type" : "gauge" }, + "segment/loadQueue/failed" : { "dimensions" : ["server"], "type" : "gauge" }, + "segment/loadQueue/count" : { "dimensions" : ["server"], "type" : "gauge" }, + "segment/dropQueue/count" : { "dimensions" : ["server"], "type" : "gauge" }, + "segment/size" : { "dimensions" : ["dataSource"], "type" : "gauge" }, + "segment/overShadowed/count" : { "dimensions" : [], "type" : "gauge" }, + + "segment/max" : { "dimensions" : [], "type" : "gauge"}, + "segment/used" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge" }, + "segment/usedPercent" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge"}, + "segment/pendingDelete" : { "dimensions" : [], "type" : "gauge"}, + + "jvm/pool/committed" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, + "jvm/pool/init" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, + "jvm/pool/max" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, + "jvm/pool/used" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, + "jvm/bufferpool/count" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, + "jvm/bufferpool/used" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, + "jvm/bufferpool/capacity" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, + "jvm/mem/init" : { "dimensions" : ["memKind"], "type" : "gauge" }, + "jvm/mem/max" : { "dimensions" : ["memKind"], "type" : "gauge" }, + "jvm/mem/used" : { "dimensions" : ["memKind"], "type" : "gauge" }, + "jvm/mem/committed" : { "dimensions" : ["memKind"], "type" : "gauge" }, + "jvm/gc/count" : { "dimensions" : ["gcName"], "type" : "count" }, + "jvm/gc/cpu" : { "dimensions" : ["gcName"], "type" : "timer" }, + + "ingest/events/buffered" : { "dimensions" : ["serviceName, bufferCapacity"], "type" : "gauge"}, + + "sys/swap/free" : { "dimensions" : [], "type" : "gauge"}, + "sys/swap/max" : { "dimensions" : [], "type" : "gauge"}, + "sys/swap/pageIn" : { "dimensions" : [], "type" : "gauge"}, + "sys/swap/pageOut" : { "dimensions" : [], "type" : "gauge"}, + "sys/disk/write/count" : { "dimensions" : ["fsDevName"], "type" : "count"}, + "sys/disk/read/count" : { "dimensions" : ["fsDevName"], "type" : "count"}, + "sys/disk/write/size" : { "dimensions" : ["fsDevName"], "type" : "count"}, + "sys/disk/read/size" : { "dimensions" : ["fsDevName"], "type" : "count"}, + "sys/net/write/size" : { "dimensions" : [], "type" : "count"}, + "sys/net/read/size" : { "dimensions" : [], "type" : "count"}, + "sys/fs/used" : { "dimensions" : ["fsDevName", "fsDirName", "fsTypeName", "fsSysTypeName", "fsOptions"], "type" : "gauge"}, + "sys/fs/max" : { "dimensions" : ["fsDevName", "fsDirName", "fsTypeName", "fsSysTypeName", "fsOptions"], "type" : "gauge"}, + "sys/mem/used" : { "dimensions" : [], "type" : "gauge"}, + "sys/mem/max" : { "dimensions" : [], "type" : "gauge"}, + "sys/storage/used" : { "dimensions" : ["fsDirName"], "type" : "gauge"}, + "sys/cpu" : { "dimensions" : ["cpuName", "cpuTime"], "type" : "gauge"}, + + "coordinator-segment/count" : { "dimensions" : ["dataSource"], "type" : "gauge" }, + "historical-segment/count" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge" } +} From 8787cd6912c9cf5ee26742547b3ecc668ff1887b Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Thu, 3 Oct 2019 14:44:29 -0700 Subject: [PATCH 008/107] use prometheus HTTPServer instead of hooking into Jetty server --- extensions-contrib/prometheus-emitter/pom.xml | 6 ++++++ .../druid/emitter/prometheus/Metrics.java | 5 +++-- .../emitter/prometheus/PrometheusEmitter.java | 16 ++++++++++++++ .../prometheus/PrometheusEmitterConfig.java | 21 ++++++++++--------- .../prometheus/PrometheusEmitterModule.java | 11 ++-------- 5 files changed, 38 insertions(+), 21 deletions(-) diff --git a/extensions-contrib/prometheus-emitter/pom.xml b/extensions-contrib/prometheus-emitter/pom.xml index 9be9aba4bb45..963d2f358aea 100644 --- a/extensions-contrib/prometheus-emitter/pom.xml +++ b/extensions-contrib/prometheus-emitter/pom.xml @@ -45,6 +45,12 @@ simpleclient 0.7.0 + + io.prometheus + simpleclient_httpserver + 0.7.0 + + com.google.code.findbugs jsr305 diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java index 7d8e58688795..f6a04bc407ea 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -55,8 +55,9 @@ public DimensionsAndCollector getByName(String name) public Metrics(String namespace, String path) { Map metrics = readMap(path); - for (String name : metrics.keySet()) { - Metric metric = metrics.get(name); + for (Map.Entry entry : metrics.entrySet()) { + String name = entry.getKey(); + Metric metric = entry.getValue(); Metric.Type type = metric.type; String[] dimensions = metric.dimensions.toArray(new String[0]); String formattedName = StringUtils.replaceChar(StringUtils.toLowerCase(name), '/', "_"); diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index 7748c5eb6e5c..f37af98f80e7 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -23,10 +23,13 @@ import io.prometheus.client.Counter; import io.prometheus.client.Gauge; import io.prometheus.client.Histogram; +import io.prometheus.client.exporter.HTTPServer; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.java.util.emitter.core.Emitter; import org.apache.druid.java.util.emitter.core.Event; import org.apache.druid.java.util.emitter.service.ServiceMetricEvent; + +import java.io.IOException; import java.util.Map; /** @@ -37,6 +40,9 @@ public class PrometheusEmitter implements Emitter private static final Logger log = new Logger(PrometheusEmitter.class); private final Metrics metrics; + private final PrometheusEmitterConfig config; + + private HTTPServer server; static PrometheusEmitter of(PrometheusEmitterConfig config) { @@ -45,6 +51,7 @@ static PrometheusEmitter of(PrometheusEmitterConfig config) public PrometheusEmitter(PrometheusEmitterConfig config) { + this.config = config; metrics = new Metrics(config.getNamespace(), config.getDimensionMapPath()); } @@ -52,6 +59,12 @@ public PrometheusEmitter(PrometheusEmitterConfig config) @Override public void start() { + try { + server = new HTTPServer(config.getPort()); + } + catch (IOException e) { + log.error(e, "Unable to start prometheus HTTPServer"); + } } @Override @@ -98,5 +111,8 @@ public void flush() @Override public void close() { + if (server != null) { + server.stop(); + } } } diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java index 83315e79dd17..e988b951782d 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Preconditions; import javax.annotation.Nullable; @@ -33,23 +34,23 @@ public class PrometheusEmitterConfig @JsonProperty private final String namespace; - @JsonProperty - private final String path; - @JsonProperty @Nullable private final String dimensionMapPath; + @JsonProperty + private final int port; + @JsonCreator public PrometheusEmitterConfig( @JsonProperty("namespace") @Nullable String namespace, - @JsonProperty("path") @Nullable String path, - @JsonProperty("dimensionMapPath") @Nullable String dimensionMapPath + @JsonProperty("dimensionMapPath") @Nullable String dimensionMapPath, + @JsonProperty("port") int port ) { this.namespace = namespace != null ? namespace : "druid"; - this.path = path != null ? path : "/prometheus"; this.dimensionMapPath = dimensionMapPath; + this.port = Preconditions.checkNotNull(port, "Prometheus server port cannot be null."); } public String getNamespace() @@ -57,13 +58,13 @@ public String getNamespace() return namespace; } - public String getPath() + public String getDimensionMapPath() { - return path; + return dimensionMapPath; } - public String getDimensionMapPath() + public int getPort() { - return dimensionMapPath; + return port; } } diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java index 42ff7e7b2435..6cc2b06c9ead 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java @@ -24,15 +24,12 @@ import com.google.inject.Binder; import com.google.inject.Provides; import com.google.inject.name.Named; -import io.prometheus.client.exporter.MetricsServlet; import org.apache.druid.guice.JsonConfigProvider; import org.apache.druid.guice.ManageLifecycle; import org.apache.druid.initialization.DruidModule; import org.apache.druid.java.util.emitter.core.Emitter; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import java.io.IOException; import java.util.Collections; import java.util.List; @@ -59,12 +56,8 @@ public void configure(Binder binder) @Provides @ManageLifecycle @Named(EMITTER_TYPE) - public Emitter getEmitter(PrometheusEmitterConfig config, Server server) + public Emitter getEmitter(PrometheusEmitterConfig config) throws IOException { - ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - server.setHandler(context); - context.addServlet(new ServletHolder(new MetricsServlet()), config.getPath()); return PrometheusEmitter.of(config); } } From 31a8d3c3a9f49bea8089f282f36c435d7012dbf4 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Thu, 3 Oct 2019 15:09:20 -0700 Subject: [PATCH 009/107] temporary empty help string --- .../main/java/org/apache/druid/emitter/prometheus/Metrics.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java index f6a04bc407ea..bc987a134f71 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -67,12 +67,14 @@ public Metrics(String namespace, String path) .namespace(namespace) .name(formattedName) .labelNames(dimensions) + .help("") .register(); } else if (Metric.Type.gauge.equals(type)) { collector = new Gauge.Builder() .namespace(namespace) .name(formattedName) .labelNames(dimensions) + .help("") .register(); } else if (Metric.Type.timer.equals(type)) { collector = new Histogram.Builder() @@ -80,6 +82,7 @@ public Metrics(String namespace, String path) .name(formattedName) .labelNames(dimensions) .buckets(.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300) + .help("") .register(); } else { log.error("Unrecognized metric type [%s]", type); From 94b37ee9c163191540dec49d8c22748524e18f4a Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Thu, 3 Oct 2019 15:25:21 -0700 Subject: [PATCH 010/107] temporary non-empty help. fix incorrect dimension value in JSON (also updated statsd json) --- .../java/org/apache/druid/emitter/prometheus/Metrics.java | 6 +++--- .../src/main/resources/defaultMetrics.json | 2 +- .../src/main/resources/defaultMetricDimensions.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java index bc987a134f71..a4f0ce4e4899 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -67,14 +67,14 @@ public Metrics(String namespace, String path) .namespace(namespace) .name(formattedName) .labelNames(dimensions) - .help("") + .help("todo") .register(); } else if (Metric.Type.gauge.equals(type)) { collector = new Gauge.Builder() .namespace(namespace) .name(formattedName) .labelNames(dimensions) - .help("") + .help("todo") .register(); } else if (Metric.Type.timer.equals(type)) { collector = new Histogram.Builder() @@ -82,7 +82,7 @@ public Metrics(String namespace, String path) .name(formattedName) .labelNames(dimensions) .buckets(.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300) - .help("") + .help("todo") .register(); } else { log.error("Unrecognized metric type [%s]", type); diff --git a/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json index ac5077656cca..2697ce3359f2 100644 --- a/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json +++ b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json @@ -104,7 +104,7 @@ "jvm/gc/count" : { "dimensions" : ["gcName"], "type" : "count" }, "jvm/gc/cpu" : { "dimensions" : ["gcName"], "type" : "timer" }, - "ingest/events/buffered" : { "dimensions" : ["serviceName, bufferCapacity"], "type" : "gauge"}, + "ingest/events/buffered" : { "dimensions" : ["serviceName", "bufferCapacity"], "type" : "gauge"}, "sys/swap/free" : { "dimensions" : [], "type" : "gauge"}, "sys/swap/max" : { "dimensions" : [], "type" : "gauge"}, diff --git a/extensions-contrib/statsd-emitter/src/main/resources/defaultMetricDimensions.json b/extensions-contrib/statsd-emitter/src/main/resources/defaultMetricDimensions.json index 2c7817b516a1..8b964270dd74 100644 --- a/extensions-contrib/statsd-emitter/src/main/resources/defaultMetricDimensions.json +++ b/extensions-contrib/statsd-emitter/src/main/resources/defaultMetricDimensions.json @@ -103,7 +103,7 @@ "jvm/gc/count" : { "dimensions" : ["gcName"], "type" : "count" }, "jvm/gc/cpu" : { "dimensions" : ["gcName"], "type" : "count" }, - "ingest/events/buffered" : { "dimensions" : ["serviceName, bufferCapacity"], "type" : "gauge"}, + "ingest/events/buffered" : { "dimensions" : ["serviceName", "bufferCapacity"], "type" : "gauge"}, "sys/swap/free" : { "dimensions" : [], "type" : "gauge"}, "sys/swap/max" : { "dimensions" : [], "type" : "gauge"}, From 49c1b407e5966f9d0ec4b3725a39eac81a557887 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Thu, 3 Oct 2019 16:44:19 -0700 Subject: [PATCH 011/107] added full help text. added metric conversion factor for timers that are not using seconds. Correct metric dimension name in documentation --- docs/operations/metrics.md | 4 +- .../prometheus/DimensionsAndCollector.java | 9 +- .../druid/emitter/prometheus/Metrics.java | 26 ++- .../emitter/prometheus/PrometheusEmitter.java | 7 +- .../src/main/resources/defaultMetrics.json | 220 +++++++++--------- 5 files changed, 143 insertions(+), 123 deletions(-) diff --git a/docs/operations/metrics.md b/docs/operations/metrics.md index ac6bd095063f..cb0c93f9a4f5 100644 --- a/docs/operations/metrics.md +++ b/docs/operations/metrics.md @@ -240,8 +240,8 @@ These metrics are only available if the JVMMonitor module is included. |`jvm/pool/init`|Initial pool.|poolKind, poolName.|Varies.| |`jvm/pool/max`|Max pool.|poolKind, poolName.|Varies.| |`jvm/pool/used`|Pool used.|poolKind, poolName.|< max pool| -|`jvm/bufferpool/count`|Bufferpool count.|bufferPoolName.|Varies.| -|`jvm/bufferpool/used`|Bufferpool used.|bufferPoolName.|close to capacity| +|`jvm/bufferpool/count`|Bufferpool count.|bufferpoolName.|Varies.| +|`jvm/bufferpool/used`|Bufferpool used.|bufferpoolName.|close to capacity| |`jvm/bufferpool/capacity`|Bufferpool capacity.|bufferPoolName.|Varies.| |`jvm/mem/init`|Initial memory.|memKind.|Varies.| |`jvm/mem/max`|Max memory.|memKind.|Varies.| diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/DimensionsAndCollector.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/DimensionsAndCollector.java index 64279d9d0939..ede4977aeee6 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/DimensionsAndCollector.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/DimensionsAndCollector.java @@ -25,11 +25,13 @@ public class DimensionsAndCollector { private final String[] dimensions; private final SimpleCollector collector; + private final double conversionFactor; - DimensionsAndCollector(String[] dimensions, SimpleCollector collector) + DimensionsAndCollector(String[] dimensions, SimpleCollector collector, double conversionFactor) { this.dimensions = dimensions; this.collector = collector; + this.conversionFactor = conversionFactor; } public String[] getDimensions() @@ -41,4 +43,9 @@ public SimpleCollector getCollector() { return collector; } + + public double getConversionFactor() + { + return conversionFactor; + } } diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java index a4f0ce4e4899..61fd2e1384cf 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -47,9 +47,15 @@ public class Metrics private final Map map = new HashMap<>(); private final ObjectMapper mapper = new ObjectMapper(); - public DimensionsAndCollector getByName(String name) + public DimensionsAndCollector getByName(String name, String service) { - return map.get(name); + if (map.containsKey(name)) { + return map.get(name); + } else if (map.containsKey(service + "_" + name)) { + return map.get(service + "_" + name); + } else { + return null; + } } public Metrics(String namespace, String path) @@ -67,14 +73,14 @@ public Metrics(String namespace, String path) .namespace(namespace) .name(formattedName) .labelNames(dimensions) - .help("todo") + .help(metric.help) .register(); } else if (Metric.Type.gauge.equals(type)) { collector = new Gauge.Builder() .namespace(namespace) .name(formattedName) .labelNames(dimensions) - .help("todo") + .help(metric.help) .register(); } else if (Metric.Type.timer.equals(type)) { collector = new Histogram.Builder() @@ -82,14 +88,14 @@ public Metrics(String namespace, String path) .name(formattedName) .labelNames(dimensions) .buckets(.1, .25, .5, .75, 1, 2.5, 5, 7.5, 10, 30, 60, 120, 300) - .help("todo") + .help(metric.help) .register(); } else { log.error("Unrecognized metric type [%s]", type); } if (collector != null) { - map.put(name, new DimensionsAndCollector(dimensions, collector)); + map.put(name, new DimensionsAndCollector(dimensions, collector, metric.conversionFactor)); } } @@ -119,15 +125,21 @@ public static class Metric { public final SortedSet dimensions; public final Type type; + public final String help; + public final double conversionFactor; @JsonCreator public Metric( @JsonProperty("dimensions") SortedSet dimensions, - @JsonProperty("type") Type type + @JsonProperty("type") Type type, + @JsonProperty("help") String help, + @JsonProperty("conversionFactor") double conversionFactor ) { this.dimensions = dimensions; this.type = type; + this.help = help; + this.conversionFactor = conversionFactor; } public enum Type diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index f37af98f80e7..c7fe221a2ee6 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -78,10 +78,11 @@ public void emit(Event event) void emitMetric(ServiceMetricEvent metricEvent) { String name = metricEvent.getMetric(); + String service = metricEvent.getService(); Map userDims = metricEvent.getUserDims(); Number value = metricEvent.getValue(); - DimensionsAndCollector metric = metrics.getByName(name); + DimensionsAndCollector metric = metrics.getByName(name, service); if (metric != null) { String[] labelValues = new String[metric.getDimensions().length]; String[] labelNames = metric.getDimensions(); @@ -96,9 +97,9 @@ void emitMetric(ServiceMetricEvent metricEvent) } else if (metric.getCollector() instanceof Gauge) { ((Gauge) metric.getCollector()).labels(labelValues).set(value.doubleValue()); } else if (metric.getCollector() instanceof Histogram) { - ((Histogram) metric.getCollector()).labels(labelValues).observe(value.doubleValue()); + ((Histogram) metric.getCollector()).labels(labelValues).observe(value.doubleValue() / metric.getConversionFactor()); } else { - //TODO + log.error("Unrecognized metric type [%s]", metric.getCollector().getClass()); } } } diff --git a/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json index 2697ce3359f2..51468a0d764f 100644 --- a/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json +++ b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json @@ -1,128 +1,128 @@ { - "query/time" : { "dimensions" : ["dataSource", "type"], "type" : "timer"}, - "query/bytes" : { "dimensions" : ["dataSource", "type"], "type" : "count"}, - "query/node/time" : { "dimensions" : ["server"], "type" : "timer"}, - "query/node/ttfb" : { "dimensions" : ["server"], "type" : "timer"}, - "query/node/bytes" : { "dimensions" : ["server"], "type" : "count"}, - "query/node/backpressure": { "dimensions" : ["server"], "type" : "timer"}, - "query/intervalChunk/time" : { "dimensions" : [], "type" : "timer"}, + "query/time" : { "dimensions" : ["dataSource", "type"], "type" : "timer", "conversionFactor": 1000.0, "help": "Seconds taken to complete a query."}, + "query/bytes" : { "dimensions" : ["dataSource", "type"], "type" : "count", "help": "Number of bytes returned in query response."}, + "query/node/time" : { "dimensions" : ["server"], "type" : "timer", "conversionFactor": 1000.0, "help": "Seconds taken to query individual historical/realtime processes."}, + "query/node/ttfb" : { "dimensions" : ["server"], "type" : "timer", "help": "Time to first byte. Seconds elapsed until Broker starts receiving the response from individual historical/realtime processes."}, + "query/node/bytes" : { "dimensions" : ["server"], "type" : "count", "help": "Number of bytes returned from querying individual historical/realtime processes."}, + "query/node/backpressure": { "dimensions" : ["server"], "type" : "timer", "help": "Seconds that the channel to this process has spent suspended due to backpressure."}, + "query/intervalChunk/time" : { "dimensions" : [], "type" : "timer", "conversionFactor": 1000.0, "help": "Only emitted if interval chunking is enabled. Milliseconds required to query an interval chunk. This metric is deprecated and will be removed in the future because interval chunking is deprecated."}, - "query/segment/time" : { "dimensions" : [], "type" : "timer"}, - "query/wait/time" : { "dimensions" : [], "type" : "timer"}, - "segment/scan/pending" : { "dimensions" : [], "type" : "gauge"}, - "query/segmentAndCache/time" : { "dimensions" : [], "type" : "timer" }, - "query/cpu/time" : { "dimensions" : ["dataSource", "type"], "type" : "timer" }, + "query/segment/time" : { "dimensions" : [], "type" : "timer", "conversionFactor": 1000.0, "help": "Seconds taken to query individual segment. Includes time to page in the segment from disk."}, + "query/wait/time" : { "dimensions" : [], "type" : "timer", "conversionFactor": 1000.0, "help": "Seconds spent waiting for a segment to be scanned."}, + "segment/scan/pending" : { "dimensions" : [], "type" : "gauge", "help": "Number of segments in queue waiting to be scanned."}, + "query/segmentAndCache/time" : { "dimensions" : [], "type" : "timer", "conversionFactor": 1000.0, "help": "Seconds taken to query individual segment or hit the cache (if it is enabled on the Historical process)."}, + "query/cpu/time" : { "dimensions" : ["dataSource", "type"], "type" : "timer", "conversionFactor": "1000000", "help": "Seconds of CPU time taken to complete a query"}, - "query/count" : { "dimensions" : [], "type" : "count" }, - "query/success/count" : { "dimensions" : [], "type" : "count" }, - "query/failed/count" : { "dimensions" : [], "type" : "count" }, - "query/interrupted/count" : { "dimensions" : [], "type" : "count" }, + "query/count" : { "dimensions" : [], "type" : "count", "help": "Number of total queries" }, + "query/success/count" : { "dimensions" : [], "type" : "count", "help": "Number of queries successfully processed"}, + "query/failed/count" : { "dimensions" : [], "type" : "count", "help": "Number of failed queries"}, + "query/interrupted/count" : { "dimensions" : [], "type" : "count", "help": "Number of queries interrupted due to cancellation or timeout"}, - "query/cache/delta/numEntries" : { "dimensions" : [], "type" : "count" }, - "query/cache/delta/sizeBytes" : { "dimensions" : [], "type" : "count" }, - "query/cache/delta/hits" : { "dimensions" : [], "type" : "count" }, - "query/cache/delta/misses" : { "dimensions" : [], "type" : "count" }, - "query/cache/delta/evictions" : { "dimensions" : [], "type" : "count" }, - "query/cache/delta/hitRate" : { "dimensions" : [], "type" : "count"}, - "query/cache/delta/averageBytes" : { "dimensions" : [], "type" : "count" }, - "query/cache/delta/timeouts" : { "dimensions" : [], "type" : "count" }, - "query/cache/delta/errors" : { "dimensions" : [], "type" : "count" }, + "query/cache/delta/numEntries" : { "dimensions" : [], "type" : "count", "help": "Number of entries in cache"}, + "query/cache/delta/sizeBytes" : { "dimensions" : [], "type" : "count", "help": "Size of cache in bytes."}, + "query/cache/delta/hits" : { "dimensions" : [], "type" : "count", "help": "Number of cache hits."}, + "query/cache/delta/misses" : { "dimensions" : [], "type" : "count", "help": "Number of cache misses."}, + "query/cache/delta/evictions" : { "dimensions" : [], "type" : "count", "help": "Number of cache evictions."}, + "query/cache/delta/hitRate" : { "dimensions" : [], "type" : "count","help": "Cache hit rate."}, + "query/cache/delta/averageBytes" : { "dimensions" : [], "type" : "count", "help": "Average size of record in bytes"}, + "query/cache/delta/timeouts" : { "dimensions" : [], "type" : "count", "help": "Number of cache timeouts"}, + "query/cache/delta/errors" : { "dimensions" : [], "type" : "count", "help": "Number of cache errors."}, - "query/cache/total/numEntries" : { "dimensions" : [], "type" : "gauge" }, - "query/cache/total/sizeBytes" : { "dimensions" : [], "type" : "gauge" }, - "query/cache/total/hits" : { "dimensions" : [], "type" : "gauge" }, - "query/cache/total/misses" : { "dimensions" : [], "type" : "gauge" }, - "query/cache/total/evictions" : { "dimensions" : [], "type" : "gauge" }, - "query/cache/total/hitRate" : { "dimensions" : [], "type" : "gauge"}, - "query/cache/total/averageBytes" : { "dimensions" : [], "type" : "gauge" }, - "query/cache/total/timeouts" : { "dimensions" : [], "type" : "gauge" }, - "query/cache/total/errors" : { "dimensions" : [], "type" : "gauge" }, + "query/cache/total/numEntries" : { "dimensions" : [], "type" : "gauge","help": "Total number of entries in cache" }, + "query/cache/total/sizeBytes" : { "dimensions" : [], "type" : "gauge", "help": "Total size of cache in bytes."}, + "query/cache/total/hits" : { "dimensions" : [], "type" : "gauge", "help": "Total number of cache hits."}, + "query/cache/total/misses" : { "dimensions" : [], "type" : "gauge", "help": "Total number of cache misses." }, + "query/cache/total/evictions" : { "dimensions" : [], "type" : "gauge", "help": "Total number of cache evictions."}, + "query/cache/total/hitRate" : { "dimensions" : [], "type" : "gauge", "help": "Total cache hit rate"}, + "query/cache/total/averageBytes" : { "dimensions" : [], "type" : "gauge", "help": "Total average record size in bytes"}, + "query/cache/total/timeouts" : { "dimensions" : [], "type" : "gauge", "help": "Total number of cache timeouts"}, + "query/cache/total/errors" : { "dimensions" : [], "type" : "gauge", "help": "Total number of cache errors" }, - "ingest/events/thrownAway" : { "dimensions" : ["dataSource"], "type" : "count" }, - "ingest/events/unparseable" : { "dimensions" : ["dataSource"], "type" : "count" }, - "ingest/events/duplicate" : { "dimensions" : ["dataSource"], "type" : "count" }, - "ingest/events/processed" : { "dimensions" : ["dataSource"], "type" : "count" }, - "ingest/events/messageGap" : { "dimensions" : ["dataSource"], "type" : "gauge" }, - "ingest/rows/output" : { "dimensions" : ["dataSource"], "type" : "count" }, - "ingest/persists/count" : { "dimensions" : ["dataSource"], "type" : "count" }, - "ingest/persists/time" : { "dimensions" : ["dataSource"], "type" : "timer" }, - "ingest/persists/cpu" : { "dimensions" : ["dataSource"], "type" : "timer" }, - "ingest/persists/backPressure" : { "dimensions" : ["dataSource"], "type" : "gauge" }, - "ingest/persists/failed" : { "dimensions" : ["dataSource"], "type" : "count" }, - "ingest/handoff/failed" : { "dimensions" : ["dataSource"], "type" : "count" }, - "ingest/merge/time" : { "dimensions" : ["dataSource"], "type" : "timer" }, - "ingest/merge/cpu" : { "dimensions" : ["dataSource"], "type" : "timer" }, + "ingest/events/thrownAway" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of events rejected because they are outside the windowPeriod."}, + "ingest/events/unparseable" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of events rejected because the events are unparseable." }, + "ingest/events/duplicate" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of events rejected because the events are duplicated."}, + "ingest/events/processed" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of events successfully processed per emission period." }, + "ingest/events/messageGap" : { "dimensions" : ["dataSource"], "type" : "gauge", "help": "Time gap between the data time in event and current system time."}, + "ingest/rows/output" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of Druid rows persisted."}, + "ingest/persists/count" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of times persist occurred." }, + "ingest/persists/time" : { "dimensions" : ["dataSource"], "type" : "timer", "conversionFactor": 1000.0, "help": "Seconds spent doing intermediate persist."}, + "ingest/persists/cpu" : { "dimensions" : ["dataSource"], "type" : "timer", "conversionFactor": 1000000000.0, "help": "Cpu time in Seconds spent on doing intermediate persist." }, + "ingest/persists/backPressure" : { "dimensions" : ["dataSource"], "type" : "gauge", "help": "Seconds spent creating persist tasks and blocking waiting for them to finish." }, + "ingest/persists/failed" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of persists that failed." }, + "ingest/handoff/failed" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of handoffs that failed." }, + "ingest/merge/time" : { "dimensions" : ["dataSource"], "type" : "timer", "conversionFactor": 1000.0, "help": "Seconds spent merging intermediate segments" }, + "ingest/merge/cpu" : { "dimensions" : ["dataSource"], "type" : "timer", "conversionFactor": 1000000000.0, "help": "Cpu time in Seconds spent on merging intermediate segments."}, - "ingest/kafka/lag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, - "ingest/kafka/maxLag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, - "ingest/kafka/avgLag" : { "dimensions" : ["dataSource"], "type" : "gauge" }, + "ingest/kafka/lag" : { "dimensions" : ["dataSource"], "type" : "gauge", "help": "Total lag between the offsets consumed by the Kafka indexing tasks and latest offsets in Kafka brokers across all partitions. Minimum emission period for this metric is a minute."}, + "ingest/kafka/maxLag" : { "dimensions" : ["dataSource"], "type" : "gauge", "help": "Max lag between the offsets consumed by the Kafka indexing tasks and latest offsets in Kafka brokers across all partitions. Minimum emission period for this metric is a minute."}, + "ingest/kafka/avgLag" : { "dimensions" : ["dataSource"], "type" : "gauge", "help": "Average lag between the offsets consumed by the Kafka indexing tasks and latest offsets in Kafka brokers across all partitions. Minimum emission period for this metric is a minute."}, - "task/success/count" : { "dimensions" : ["dataSource"], "type" : "count" }, - "task/failed/count" : { "dimensions" : ["dataSource"], "type" : "count" }, - "task/running/count" : { "dimensions" : ["dataSource"], "type" : "count" }, - "task/pending/count" : { "dimensions" : ["dataSource"], "type" : "count" }, - "task/waiting/count" : { "dimensions" : ["dataSource"], "type" : "count" }, + "task/success/count" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of successful tasks per emission period. This metric is only available if the TaskCountStatsMonitor module is included."}, + "task/failed/count" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of failed tasks per emission period. This metric is only available if the TaskCountStatsMonitor module is included."}, + "task/running/count" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of current running tasks. This metric is only available if the TaskCountStatsMonitor module is included."}, + "task/pending/count" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of current pending tasks. This metric is only available if the TaskCountStatsMonitor module is included."}, + "task/waiting/count" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of current waiting tasks. This metric is only available if the TaskCountStatsMonitor module is included."}, + "task/run/time" : { "dimensions" : ["dataSource", "taskType"], "type" : "timer", "conversionFactor": 1000.0, "help": "Milliseconds taken to run a task."}, - "task/run/time" : { "dimensions" : ["dataSource", "taskType"], "type" : "timer" }, - "segment/added/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, - "segment/moved/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, - "segment/nuked/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count" }, + "segment/added/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count", "help": "Size in bytes of new segments created." }, + "segment/moved/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count", "help": "Size in bytes of segments moved/archived via the Move Task." }, + "segment/nuked/bytes" : { "dimensions" : ["dataSource", "taskType"], "type" : "count", "help": "Size in bytes of segments deleted via the Kill Task." }, - "segment/assigned/count" : { "dimensions" : ["tier"], "type" : "count" }, - "segment/moved/count" : { "dimensions" : ["tier"], "type" : "count" }, - "segment/dropped/count" : { "dimensions" : ["tier"], "type" : "count" }, - "segment/deleted/count" : { "dimensions" : ["tier"], "type" : "count" }, - "segment/unneeded/count" : { "dimensions" : ["tier"], "type" : "count" }, - "segment/unavailable/count" : { "dimensions" : ["dataSource"], "type" : "count" }, - "segment/underReplicated/count" : { "dimensions" : ["dataSource", "tier"], "type" : "count" }, - "segment/cost/raw" : { "dimensions" : ["tier"], "type" : "count" }, - "segment/cost/normalization" : { "dimensions" : ["tier"], "type" : "count" }, - "segment/cost/normalized" : { "dimensions" : ["tier"], "type" : "count" }, - "segment/loadQueue/size" : { "dimensions" : ["server"], "type" : "gauge" }, - "segment/loadQueue/failed" : { "dimensions" : ["server"], "type" : "gauge" }, - "segment/loadQueue/count" : { "dimensions" : ["server"], "type" : "gauge" }, - "segment/dropQueue/count" : { "dimensions" : ["server"], "type" : "gauge" }, - "segment/size" : { "dimensions" : ["dataSource"], "type" : "gauge" }, - "segment/overShadowed/count" : { "dimensions" : [], "type" : "gauge" }, + "segment/assigned/count" : { "dimensions" : ["tier"], "type" : "count", "help": "Number of segments assigned to be loaded in the cluster."}, + "segment/moved/count" : { "dimensions" : ["tier"], "type" : "count", "help": "Number of segments moved in the cluster." }, + "segment/dropped/count" : { "dimensions" : ["tier"], "type" : "count", "help": "Number of segments dropped due to being overshadowed." }, + "segment/deleted/count" : { "dimensions" : ["tier"], "type" : "count", "help": "Number of segments dropped due to rules."}, + "segment/unneeded/count" : { "dimensions" : ["tier"], "type" : "count", "help": "Number of segments dropped due to being marked as unused."}, + "segment/unavailable/count" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of segments (not including replicas) left to load until segments that should be loaded in the cluster are available for queries."}, + "segment/underReplicated/count" : { "dimensions" : ["dataSource", "tier"], "type" : "count", "help": "Number of segments (including replicas) left to load until segments that should be loaded in the cluster are available for queries."}, + "segment/cost/raw" : { "dimensions" : ["tier"], "type" : "count", "help": "Used in cost balancing. The raw cost of hosting segments."}, + "segment/cost/normalization" : { "dimensions" : ["tier"], "type" : "count", "help": "Used in cost balancing. The normalization of hosting segments."}, + "segment/cost/normalized" : { "dimensions" : ["tier"], "type" : "count", "help": "Used in cost balancing. The normalized cost of hosting segments."}, + "segment/loadQueue/size" : { "dimensions" : ["server"], "type" : "gauge", "help": "Size in bytes of segments to load."}, + "segment/loadQueue/failed" : { "dimensions" : ["server"], "type" : "gauge", "help": "Number of segments that failed to load."}, + "segment/loadQueue/count" : { "dimensions" : ["server"], "type" : "gauge", "help": "Number of segments to load."}, + "segment/dropQueue/count" : { "dimensions" : ["server"], "type" : "gauge", "help": "Number of segments to drop."}, + "segment/size" : { "dimensions" : ["dataSource"], "type" : "gauge", "help": "Size in bytes of available segments."}, + "segment/overShadowed/count" : { "dimensions" : [], "type" : "gauge", "help": "Number of overShadowed segments."}, - "segment/max" : { "dimensions" : [], "type" : "gauge"}, - "segment/used" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge" }, - "segment/usedPercent" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge"}, - "segment/pendingDelete" : { "dimensions" : [], "type" : "gauge"}, + "segment/max" : { "dimensions" : [], "type" : "gauge", "help": "Maximum byte limit available for segments."}, + "segment/used" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge", "help": "Bytes used for served segments."}, + "segment/usedPercent" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge", "help": "Percentage of space used by served segments."}, + "segment/pendingDelete" : { "dimensions" : [], "type" : "gauge", "help": "On-disk size in bytes of segments that are waiting to be cleared out"}, - "jvm/pool/committed" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, - "jvm/pool/init" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, - "jvm/pool/max" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, - "jvm/pool/used" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge" }, - "jvm/bufferpool/count" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, - "jvm/bufferpool/used" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, - "jvm/bufferpool/capacity" : { "dimensions" : ["bufferPoolName"], "type" : "gauge" }, - "jvm/mem/init" : { "dimensions" : ["memKind"], "type" : "gauge" }, - "jvm/mem/max" : { "dimensions" : ["memKind"], "type" : "gauge" }, - "jvm/mem/used" : { "dimensions" : ["memKind"], "type" : "gauge" }, - "jvm/mem/committed" : { "dimensions" : ["memKind"], "type" : "gauge" }, - "jvm/gc/count" : { "dimensions" : ["gcName"], "type" : "count" }, - "jvm/gc/cpu" : { "dimensions" : ["gcName"], "type" : "timer" }, + "jvm/pool/committed" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge", "help": "Committed pool."}, + "jvm/pool/init" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge", "help": "Initial pool."}, + "jvm/pool/max" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge", "help": "Max pool."}, + "jvm/pool/used" : { "dimensions" : ["poolKind", "poolName"], "type" : "gauge", "help": "Pool used."}, + "jvm/bufferpool/count" : { "dimensions" : ["bufferpoolName"], "type" : "gauge", "help": "bufferpool count"}, + "jvm/bufferpool/used" : { "dimensions" : ["bufferpoolName"], "type" : "gauge", "help": "bufferpool used"}, + "jvm/bufferpool/capacity" : { "dimensions" : ["bufferpoolName"], "type" : "gauge", "help": "bufferpool capacity"}, + "jvm/mem/init" : { "dimensions" : ["memKind"], "type" : "gauge", "help": "Initial memory"}, + "jvm/mem/max" : { "dimensions" : ["memKind"], "type" : "gauge", "help": "Max memory"}, + "jvm/mem/used" : { "dimensions" : ["memKind"], "type" : "gauge", "help": "Used memory"}, + "jvm/mem/committed" : { "dimensions" : ["memKind"], "type" : "gauge", "help": "Committed memory"}, + "jvm/gc/count" : { "dimensions" : ["gcName"], "type" : "count", "help": "Garbage collection count"}, + "jvm/gc/cpu" : { "dimensions" : ["gcName"], "type" : "timer", "help": "Cpu time in Nanoseconds spent on garbage collection."}, - "ingest/events/buffered" : { "dimensions" : ["serviceName", "bufferCapacity"], "type" : "gauge"}, + "ingest/events/buffered" : { "dimensions" : ["serviceName", "bufferCapacity"], "type" : "gauge", "help": "Number of events queued in the EventReceiverFirehose's buffer"}, - "sys/swap/free" : { "dimensions" : [], "type" : "gauge"}, - "sys/swap/max" : { "dimensions" : [], "type" : "gauge"}, - "sys/swap/pageIn" : { "dimensions" : [], "type" : "gauge"}, - "sys/swap/pageOut" : { "dimensions" : [], "type" : "gauge"}, + "sys/swap/free" : { "dimensions" : [], "type" : "gauge", "help": "Free swap"}, + "sys/swap/max" : { "dimensions" : [], "type" : "gauge", "help": "Max swap"}, + "sys/swap/pageIn" : { "dimensions" : [], "type" : "gauge", "help": "Paged in swap"}, + "sys/swap/pageOut" : { "dimensions" : [], "type" : "gauge", "help": "Paged out swap"}, "sys/disk/write/count" : { "dimensions" : ["fsDevName"], "type" : "count"}, "sys/disk/read/count" : { "dimensions" : ["fsDevName"], "type" : "count"}, - "sys/disk/write/size" : { "dimensions" : ["fsDevName"], "type" : "count"}, - "sys/disk/read/size" : { "dimensions" : ["fsDevName"], "type" : "count"}, - "sys/net/write/size" : { "dimensions" : [], "type" : "count"}, - "sys/net/read/size" : { "dimensions" : [], "type" : "count"}, - "sys/fs/used" : { "dimensions" : ["fsDevName", "fsDirName", "fsTypeName", "fsSysTypeName", "fsOptions"], "type" : "gauge"}, - "sys/fs/max" : { "dimensions" : ["fsDevName", "fsDirName", "fsTypeName", "fsSysTypeName", "fsOptions"], "type" : "gauge"}, - "sys/mem/used" : { "dimensions" : [], "type" : "gauge"}, - "sys/mem/max" : { "dimensions" : [], "type" : "gauge"}, - "sys/storage/used" : { "dimensions" : ["fsDirName"], "type" : "gauge"}, - "sys/cpu" : { "dimensions" : ["cpuName", "cpuTime"], "type" : "gauge"}, + "sys/disk/write/size" : { "dimensions" : ["fsDevName"], "type" : "count", "help": "Bytes written to disk. Can we used to determine how much paging is occurring with regards to segments."}, + "sys/disk/read/size" : { "dimensions" : ["fsDevName"], "type" : "count", "help": "Bytes read from disk. Can we used to determine how much paging is occurring with regards to segments."}, + "sys/net/write/size" : { "dimensions" : [], "type" : "count", "help": "Bytes written to the network."}, + "sys/net/read/size" : { "dimensions" : [], "type" : "count", "help": "Bytes read from the network."}, + "sys/fs/used" : { "dimensions" : ["fsDevName", "fsDirName", "fsTypeName", "fsSysTypeName", "fsOptions"], "type" : "gauge", "help": "Filesystem bytes used."}, + "sys/fs/max" : { "dimensions" : ["fsDevName", "fsDirName", "fsTypeName", "fsSysTypeName", "fsOptions"], "type" : "gauge", "help": "Filesystesm bytes max."}, + "sys/mem/used" : { "dimensions" : [], "type" : "gauge", "help": "Memory used."}, + "sys/mem/max" : { "dimensions" : [], "type" : "gauge", "help": "Memory max"}, + "sys/storage/used" : { "dimensions" : ["fsDirName"], "type" : "gauge", "help": "Disk space used."}, + "sys/cpu" : { "dimensions" : ["cpuName", "cpuTime"], "type" : "gauge", "help": "CPU used"}, - "coordinator-segment/count" : { "dimensions" : ["dataSource"], "type" : "gauge" }, - "historical-segment/count" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge" } + "coordinator_segment/count" : { "dimensions" : ["dataSource"], "type" : "gauge", "help": "Number of served segments."}, + "historical_segment/count" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge", "help": "Number of served segments."} } From edab82392f573716637ff6f4973d5184041b6b42 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Thu, 3 Oct 2019 16:53:38 -0700 Subject: [PATCH 012/107] added documentation for prometheus emitter --- .../extensions-contrib/prometheus.md | 61 +++++++++++++++++++ .../emitter/prometheus/PrometheusEmitter.java | 2 + 2 files changed, 63 insertions(+) create mode 100644 docs/development/extensions-contrib/prometheus.md diff --git a/docs/development/extensions-contrib/prometheus.md b/docs/development/extensions-contrib/prometheus.md new file mode 100644 index 000000000000..bb42ad894f9f --- /dev/null +++ b/docs/development/extensions-contrib/prometheus.md @@ -0,0 +1,61 @@ +--- +id: prometheus +title: "Prometheus Emitter" +--- + + + + +To use this Apache Druid (incubating) extension, make sure to [include](../../development/extensions.md#loading-extensions) `prometheus-emitter` extension. + +## Introduction + +This extension exposes Druid metrics for collection by a Prometheus server +(https://prometheus.io/) + +## Configuration + +All the configuration parameters for the Prometheus emitter are under `druid.emitter.prometheus`. + +|property|description|required?|default| +|--------|-----------|---------|-------| +|`druid.emitter.prometheus.port`|The port on which to expose the prometheus HTTPServer.|yes|none| +|`druid.emitter.prometheus.namespace`|Optional metric namespace.|no|"druid"| +|`druid.emitter.prometheus.dimensionMapPath`|JSON file defining the Prometheus metric type, desired dimensions, help text, and conversionFactor for every Druid metric.|no|Default mapping provided. See below.| + +### Metric mapping + +Each metric to be collected by Prometheus must specify a type, one of `[timer, counter, guage]`. Prometheus Emitter expects this mapping to +be provided as a JSON file. Additionally, this mapping specifies which dimensions should be included for each metric. Prometheus expects +histogram timers to use Seconds as the base unit. Timers which do not use seconds as a base unit can use the `conversionFactor` to set +the base time unit. If the user does not specify their own JSON file, a default mapping is used. All +metrics are expected to be mapped. Metrics which are not mapped will not be tracked. +Prometheus metric path is organized using the following schema: +` : { "dimensions" : , "type" : , conversionFactor: , "help" : ,}` +e.g. +`query/time" : { "dimensions" : ["dataSource", "type"], "conversionFactor": 1000.0, "type" : "timer", "help": "Seconds taken to complete a query."}` + +For metrics which are emitted from multiple services with different dimensions, the metric name is prefixed with +the service name. +e.g. +`"coordinator-segment/count" : { "dimensions" : ["dataSource"], "type" : "gauge" }, + "historical-segment/count" : { "dimensions" : ["dataSource", "tier", "priority"], "type" : "gauge" }` + +For most use-cases, the default mapping is sufficient. diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index c7fe221a2ee6..0b756c2e2eae 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -101,6 +101,8 @@ void emitMetric(ServiceMetricEvent metricEvent) } else { log.error("Unrecognized metric type [%s]", metric.getCollector().getClass()); } + } else { + log.debug("Unmapped metric [%s]", name); } } From 58367f45409e223806d54cd88913966c7f07cf69 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Thu, 3 Oct 2019 17:00:27 -0700 Subject: [PATCH 013/107] safety for invalid labelNames --- .../org/apache/druid/emitter/prometheus/PrometheusEmitter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index 0b756c2e2eae..9aeaf3b6b29a 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -88,8 +88,9 @@ void emitMetric(ServiceMetricEvent metricEvent) String[] labelNames = metric.getDimensions(); for (int i = 0; i < labelValues.length; i++) { String labelName = labelNames[i]; + //labelName is controlled by the user. Instead of potential NPE on invalid labelName we use "unknown" as the dimension value Object userDim = userDims.get(labelName); - labelValues[i] = userDim.toString(); + labelValues[i] = userDim != null ? userDim.toString() : "unknown"; } if (metric.getCollector() instanceof Counter) { From 6330f0a73f901d292939354b49b0e3b5f31e09db Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Fri, 4 Oct 2019 11:31:20 -0700 Subject: [PATCH 014/107] fix travis checks --- extensions-contrib/prometheus-emitter/pom.xml | 14 -------------- .../prometheus/PrometheusEmitterConfig.java | 4 ++-- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/extensions-contrib/prometheus-emitter/pom.xml b/extensions-contrib/prometheus-emitter/pom.xml index 963d2f358aea..2b83426a5066 100644 --- a/extensions-contrib/prometheus-emitter/pom.xml +++ b/extensions-contrib/prometheus-emitter/pom.xml @@ -50,7 +50,6 @@ simpleclient_httpserver 0.7.0 - com.google.code.findbugs jsr305 @@ -115,18 +114,5 @@ test-jar test - - org.eclipse.jetty - jetty-server - - - org.eclipse.jetty - jetty-servlet - - - io.prometheus - simpleclient_servlet - 0.4.0 - diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java index e988b951782d..7c81603a57ad 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java @@ -39,13 +39,13 @@ public class PrometheusEmitterConfig private final String dimensionMapPath; @JsonProperty - private final int port; + private final Integer port; @JsonCreator public PrometheusEmitterConfig( @JsonProperty("namespace") @Nullable String namespace, @JsonProperty("dimensionMapPath") @Nullable String dimensionMapPath, - @JsonProperty("port") int port + @JsonProperty("port") Integer port ) { this.namespace = namespace != null ? namespace : "druid"; From 0751f71dd569b92de083b264c4c5869d61533590 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Fri, 4 Oct 2019 13:39:08 -0700 Subject: [PATCH 015/107] Unit test and better sanitization of metrics names and label values --- .../druid/emitter/prometheus/Metrics.java | 2 +- .../emitter/prometheus/PrometheusEmitter.java | 2 +- .../src/main/resources/defaultMetrics.json | 4 ++-- ...theusEmitterTest.java => MetricsTest.java} | 21 ++++++++++++++++++- sshuttle.pid | 1 + 5 files changed, 25 insertions(+), 5 deletions(-) rename extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/{PrometheusEmitterTest.java => MetricsTest.java} (52%) create mode 100644 sshuttle.pid diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java index 61fd2e1384cf..c373f5991b50 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -66,7 +66,7 @@ public Metrics(String namespace, String path) Metric metric = entry.getValue(); Metric.Type type = metric.type; String[] dimensions = metric.dimensions.toArray(new String[0]); - String formattedName = StringUtils.replaceChar(StringUtils.toLowerCase(name), '/', "_"); + String formattedName = StringUtils.toLowerCase(name).replaceAll("[^a-zA-Z_:][^a-zA-Z0-9_:]*", "_"); SimpleCollector collector = null; if (Metric.Type.count.equals(type)) { collector = new Counter.Builder() diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index 9aeaf3b6b29a..32eb2165e484 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -90,7 +90,7 @@ void emitMetric(ServiceMetricEvent metricEvent) String labelName = labelNames[i]; //labelName is controlled by the user. Instead of potential NPE on invalid labelName we use "unknown" as the dimension value Object userDim = userDims.get(labelName); - labelValues[i] = userDim != null ? userDim.toString() : "unknown"; + labelValues[i] = userDim != null ? userDim.toString().replaceAll("[^a-zA-Z_][^a-zA-Z0-9_]*", "_") : "unknown"; } if (metric.getCollector() instanceof Counter) { diff --git a/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json index 51468a0d764f..4d1fa5d13ab5 100644 --- a/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json +++ b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json @@ -110,8 +110,8 @@ "sys/swap/max" : { "dimensions" : [], "type" : "gauge", "help": "Max swap"}, "sys/swap/pageIn" : { "dimensions" : [], "type" : "gauge", "help": "Paged in swap"}, "sys/swap/pageOut" : { "dimensions" : [], "type" : "gauge", "help": "Paged out swap"}, - "sys/disk/write/count" : { "dimensions" : ["fsDevName"], "type" : "count"}, - "sys/disk/read/count" : { "dimensions" : ["fsDevName"], "type" : "count"}, + "sys/disk/write/count" : { "dimensions" : ["fsDevName"], "type" : "count", "help": "Writes to disk."}, + "sys/disk/read/count" : { "dimensions" : ["fsDevName"], "type" : "count", "help": "Reads from disk."}, "sys/disk/write/size" : { "dimensions" : ["fsDevName"], "type" : "count", "help": "Bytes written to disk. Can we used to determine how much paging is occurring with regards to segments."}, "sys/disk/read/size" : { "dimensions" : ["fsDevName"], "type" : "count", "help": "Bytes read from disk. Can we used to determine how much paging is occurring with regards to segments."}, "sys/net/write/size" : { "dimensions" : [], "type" : "count", "help": "Bytes written to the network."}, diff --git a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java similarity index 52% rename from extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java rename to extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java index 666bd1419515..6d87780360fe 100644 --- a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java +++ b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java @@ -19,6 +19,25 @@ package org.apache.druid.emitter.prometheus; -public class PrometheusEmitterTest +import io.prometheus.client.Histogram; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class MetricsTest { + @Test + public void testMetricsConfiguration() + { + Metrics metrics = new Metrics("test", null); + DimensionsAndCollector dimensionsAndCollector = metrics.getByName("query/time", "historical"); + assertNotNull(dimensionsAndCollector); + String[] dimensions = dimensionsAndCollector.getDimensions(); + assertEquals("dataSource", dimensions[0]); + assertEquals("type", dimensions[1]); + assertEquals(1000.0, dimensionsAndCollector.getConversionFactor(), 0.0); + assertTrue(dimensionsAndCollector.getCollector() instanceof Histogram); + } } diff --git a/sshuttle.pid b/sshuttle.pid new file mode 100644 index 000000000000..9d84d6806e4d --- /dev/null +++ b/sshuttle.pid @@ -0,0 +1 @@ +77782 From 2497102fdb9d044ec34cdfaae027cbb4bd6280dd Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Fri, 4 Oct 2019 13:42:28 -0700 Subject: [PATCH 016/107] add precondition to check namespace against regex --- .../apache/druid/emitter/prometheus/PrometheusEmitterConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java index 7c81603a57ad..937bccf5e00a 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java @@ -49,6 +49,7 @@ public PrometheusEmitterConfig( ) { this.namespace = namespace != null ? namespace : "druid"; + Preconditions.checkArgument(namespace.matches("[a-zA-Z_:][a-zA-Z0-9_:]*"), "Invalid namespace " + namespace); this.dimensionMapPath = dimensionMapPath; this.port = Preconditions.checkNotNull(port, "Prometheus server port cannot be null."); } From 6a0f54e54c5b94a19c9db533f8a6fd445196364f Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Fri, 4 Oct 2019 14:19:23 -0700 Subject: [PATCH 017/107] use precompiled regex --- .../java/org/apache/druid/emitter/prometheus/Metrics.java | 4 +++- .../apache/druid/emitter/prometheus/PrometheusEmitter.java | 4 +++- .../druid/emitter/prometheus/PrometheusEmitterConfig.java | 5 ++++- sshuttle.pid | 1 - 4 files changed, 10 insertions(+), 4 deletions(-) delete mode 100644 sshuttle.pid diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java index c373f5991b50..7a3487179832 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -39,6 +39,7 @@ import java.util.HashMap; import java.util.Map; import java.util.SortedSet; +import java.util.regex.Pattern; public class Metrics { @@ -46,6 +47,7 @@ public class Metrics private static final Logger log = new Logger(Metrics.class); private final Map map = new HashMap<>(); private final ObjectMapper mapper = new ObjectMapper(); + private final Pattern pattern = Pattern.compile("[^a-zA-Z_:][^a-zA-Z0-9_:]*"); public DimensionsAndCollector getByName(String name, String service) { @@ -66,7 +68,7 @@ public Metrics(String namespace, String path) Metric metric = entry.getValue(); Metric.Type type = metric.type; String[] dimensions = metric.dimensions.toArray(new String[0]); - String formattedName = StringUtils.toLowerCase(name).replaceAll("[^a-zA-Z_:][^a-zA-Z0-9_:]*", "_"); + String formattedName = pattern.matcher(StringUtils.toLowerCase(name)).replaceAll("_"); SimpleCollector collector = null; if (Metric.Type.count.equals(type)) { collector = new Counter.Builder() diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index 32eb2165e484..5a69c8996f4c 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.util.Map; +import java.util.regex.Pattern; /** * @@ -41,6 +42,7 @@ public class PrometheusEmitter implements Emitter private static final Logger log = new Logger(PrometheusEmitter.class); private final Metrics metrics; private final PrometheusEmitterConfig config; + private final Pattern pattern = Pattern.compile("[^a-zA-Z_][^a-zA-Z0-9_]*"); private HTTPServer server; @@ -90,7 +92,7 @@ void emitMetric(ServiceMetricEvent metricEvent) String labelName = labelNames[i]; //labelName is controlled by the user. Instead of potential NPE on invalid labelName we use "unknown" as the dimension value Object userDim = userDims.get(labelName); - labelValues[i] = userDim != null ? userDim.toString().replaceAll("[^a-zA-Z_][^a-zA-Z0-9_]*", "_") : "unknown"; + labelValues[i] = userDim != null ? pattern.matcher(userDim.toString()).replaceAll("_") : "unknown"; } if (metric.getCollector() instanceof Counter) { diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java index 937bccf5e00a..6a89562e813d 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java @@ -24,6 +24,7 @@ import com.google.common.base.Preconditions; import javax.annotation.Nullable; +import java.util.regex.Pattern; /** * @@ -31,6 +32,8 @@ public class PrometheusEmitterConfig { + Pattern pattern = Pattern.compile("[a-zA-Z_:][a-zA-Z0-9_:]*"); + @JsonProperty private final String namespace; @@ -49,7 +52,7 @@ public PrometheusEmitterConfig( ) { this.namespace = namespace != null ? namespace : "druid"; - Preconditions.checkArgument(namespace.matches("[a-zA-Z_:][a-zA-Z0-9_:]*"), "Invalid namespace " + namespace); + Preconditions.checkArgument(pattern.matcher(namespace).matches(), "Invalid namespace " + namespace); this.dimensionMapPath = dimensionMapPath; this.port = Preconditions.checkNotNull(port, "Prometheus server port cannot be null."); } diff --git a/sshuttle.pid b/sshuttle.pid deleted file mode 100644 index 9d84d6806e4d..000000000000 --- a/sshuttle.pid +++ /dev/null @@ -1 +0,0 @@ -77782 From 624b0d94284525a218ff449043157aec0e9b99d2 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Mon, 7 Oct 2019 10:59:50 -0700 Subject: [PATCH 018/107] remove static imports. fix metric types --- .../src/main/resources/defaultMetrics.json | 6 +++--- .../druid/emitter/prometheus/MetricsTest.java | 15 ++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json index 4d1fa5d13ab5..90770d7b4fc9 100644 --- a/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json +++ b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json @@ -73,8 +73,8 @@ "segment/dropped/count" : { "dimensions" : ["tier"], "type" : "count", "help": "Number of segments dropped due to being overshadowed." }, "segment/deleted/count" : { "dimensions" : ["tier"], "type" : "count", "help": "Number of segments dropped due to rules."}, "segment/unneeded/count" : { "dimensions" : ["tier"], "type" : "count", "help": "Number of segments dropped due to being marked as unused."}, - "segment/unavailable/count" : { "dimensions" : ["dataSource"], "type" : "count", "help": "Number of segments (not including replicas) left to load until segments that should be loaded in the cluster are available for queries."}, - "segment/underReplicated/count" : { "dimensions" : ["dataSource", "tier"], "type" : "count", "help": "Number of segments (including replicas) left to load until segments that should be loaded in the cluster are available for queries."}, + "segment/unavailable/count" : { "dimensions" : ["dataSource"], "type" : "gauge", "help": "Number of segments (not including replicas) left to load until segments that should be loaded in the cluster are available for queries."}, + "segment/underReplicated/count" : { "dimensions" : ["dataSource", "tier"], "type" : "gauge", "help": "Number of segments (including replicas) left to load until segments that should be loaded in the cluster are available for queries."}, "segment/cost/raw" : { "dimensions" : ["tier"], "type" : "count", "help": "Used in cost balancing. The raw cost of hosting segments."}, "segment/cost/normalization" : { "dimensions" : ["tier"], "type" : "count", "help": "Used in cost balancing. The normalization of hosting segments."}, "segment/cost/normalized" : { "dimensions" : ["tier"], "type" : "count", "help": "Used in cost balancing. The normalized cost of hosting segments."}, @@ -102,7 +102,7 @@ "jvm/mem/used" : { "dimensions" : ["memKind"], "type" : "gauge", "help": "Used memory"}, "jvm/mem/committed" : { "dimensions" : ["memKind"], "type" : "gauge", "help": "Committed memory"}, "jvm/gc/count" : { "dimensions" : ["gcName"], "type" : "count", "help": "Garbage collection count"}, - "jvm/gc/cpu" : { "dimensions" : ["gcName"], "type" : "timer", "help": "Cpu time in Nanoseconds spent on garbage collection."}, + "jvm/gc/cpu" : { "dimensions" : ["gcName"], "type" : "count", "help": "Count of CPU time in Nanoseconds spent on garbage collection. Note: `jvm/gc/cpu` represents the total time over multiple GC cycles; divide by `jvm/gc/count` to get the mean GC time per cycle."}, "ingest/events/buffered" : { "dimensions" : ["serviceName", "bufferCapacity"], "type" : "gauge", "help": "Number of events queued in the EventReceiverFirehose's buffer"}, diff --git a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java index 6d87780360fe..128f9efe7f95 100644 --- a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java +++ b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java @@ -20,12 +20,9 @@ package org.apache.druid.emitter.prometheus; import io.prometheus.client.Histogram; +import org.junit.Assert; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - public class MetricsTest { @Test @@ -33,11 +30,11 @@ public void testMetricsConfiguration() { Metrics metrics = new Metrics("test", null); DimensionsAndCollector dimensionsAndCollector = metrics.getByName("query/time", "historical"); - assertNotNull(dimensionsAndCollector); + Assert.assertNotNull(dimensionsAndCollector); String[] dimensions = dimensionsAndCollector.getDimensions(); - assertEquals("dataSource", dimensions[0]); - assertEquals("type", dimensions[1]); - assertEquals(1000.0, dimensionsAndCollector.getConversionFactor(), 0.0); - assertTrue(dimensionsAndCollector.getCollector() instanceof Histogram); + Assert.assertEquals("dataSource", dimensions[0]); + Assert.assertEquals("type", dimensions[1]); + Assert.assertEquals(1000.0, dimensionsAndCollector.getConversionFactor(), 0.0); + Assert.assertTrue(dimensionsAndCollector.getCollector() instanceof Histogram); } } From 5f3ef58197cb209df6912e425c1dbeb18122a2c5 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Thu, 10 Oct 2019 01:03:34 -0700 Subject: [PATCH 019/107] better docs. fix possible NPE in PrometheusEmitterConfig. Guard against multiple calls to PrometheusEmitter.start() --- docs/development/extensions-contrib/prometheus.md | 8 +++++++- .../emitter/prometheus/PrometheusEmitter.java | 14 +++++++++----- .../prometheus/PrometheusEmitterConfig.java | 3 ++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/development/extensions-contrib/prometheus.md b/docs/development/extensions-contrib/prometheus.md index bb42ad894f9f..560941e5b6ef 100644 --- a/docs/development/extensions-contrib/prometheus.md +++ b/docs/development/extensions-contrib/prometheus.md @@ -37,9 +37,15 @@ All the configuration parameters for the Prometheus emitter are under `druid.emi |property|description|required?|default| |--------|-----------|---------|-------| |`druid.emitter.prometheus.port`|The port on which to expose the prometheus HTTPServer.|yes|none| -|`druid.emitter.prometheus.namespace`|Optional metric namespace.|no|"druid"| +|`druid.emitter.prometheus.namespace`|Optional metric namespace. Must match the regex `[a-zA-Z_:][a-zA-Z0-9_:]*`|no|"druid"| |`druid.emitter.prometheus.dimensionMapPath`|JSON file defining the Prometheus metric type, desired dimensions, help text, and conversionFactor for every Druid metric.|no|Default mapping provided. See below.| +### Metric names + +All metric names and labels are reformatted to match Prometheus standards. +- For names: all characters which are not alphanumeric, underscores, or colons (matching `[^a-zA-Z_:][^a-zA-Z0-9_:]*`) are replaced with `_` +- For labels: all characters which are not alphanumeric or underscores (matching `[^a-zA-Z_][^a-zA-Z0-9_]*`) are replaced with `_` + ### Metric mapping Each metric to be collected by Prometheus must specify a type, one of `[timer, counter, guage]`. Prometheus Emitter expects this mapping to diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index 5a69c8996f4c..518b270748d1 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -61,11 +61,15 @@ public PrometheusEmitter(PrometheusEmitterConfig config) @Override public void start() { - try { - server = new HTTPServer(config.getPort()); - } - catch (IOException e) { - log.error(e, "Unable to start prometheus HTTPServer"); + if (server == null) { + try { + server = new HTTPServer(config.getPort()); + } + catch (IOException e) { + log.error(e, "Unable to start prometheus HTTPServer"); + } + } else { + log.error("HTTPServer is already started"); } } diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java index 6a89562e813d..39c94e2af8a1 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java @@ -35,6 +35,7 @@ public class PrometheusEmitterConfig Pattern pattern = Pattern.compile("[a-zA-Z_:][a-zA-Z0-9_:]*"); @JsonProperty + @Nullable private final String namespace; @JsonProperty @@ -52,7 +53,7 @@ public PrometheusEmitterConfig( ) { this.namespace = namespace != null ? namespace : "druid"; - Preconditions.checkArgument(pattern.matcher(namespace).matches(), "Invalid namespace " + namespace); + Preconditions.checkArgument(pattern.matcher(this.namespace).matches(), "Invalid namespace " + this.namespace); this.dimensionMapPath = dimensionMapPath; this.port = Preconditions.checkNotNull(port, "Prometheus server port cannot be null."); } From 4ee7ba78f684f79e083b59d61aacda4e3f78105c Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Wed, 27 Nov 2019 10:18:06 -0800 Subject: [PATCH 020/107] Update regex for label-value replacements to allow internal numeric values. Additional tests --- .../extensions-contrib/prometheus.md | 2 +- .../emitter/prometheus/PrometheusEmitter.java | 2 +- .../druid/emitter/prometheus/MetricsTest.java | 2 ++ .../prometheus/PrometheusEmitterTest.java | 30 +++++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java diff --git a/docs/development/extensions-contrib/prometheus.md b/docs/development/extensions-contrib/prometheus.md index 560941e5b6ef..eec0bd308b05 100644 --- a/docs/development/extensions-contrib/prometheus.md +++ b/docs/development/extensions-contrib/prometheus.md @@ -44,7 +44,7 @@ All the configuration parameters for the Prometheus emitter are under `druid.emi All metric names and labels are reformatted to match Prometheus standards. - For names: all characters which are not alphanumeric, underscores, or colons (matching `[^a-zA-Z_:][^a-zA-Z0-9_:]*`) are replaced with `_` -- For labels: all characters which are not alphanumeric or underscores (matching `[^a-zA-Z_][^a-zA-Z0-9_]*`) are replaced with `_` +- For labels: all characters which are not alphanumeric or underscores (matching `[^a-zA-Z0-9_][^a-zA-Z0-9_]*`) are replaced with `_` ### Metric mapping diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index 518b270748d1..6080c871fc80 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -42,7 +42,7 @@ public class PrometheusEmitter implements Emitter private static final Logger log = new Logger(PrometheusEmitter.class); private final Metrics metrics; private final PrometheusEmitterConfig config; - private final Pattern pattern = Pattern.compile("[^a-zA-Z_][^a-zA-Z0-9_]*"); + private final Pattern pattern = Pattern.compile("[^a-zA-Z0-9_][^a-zA-Z0-9_]*"); private HTTPServer server; diff --git a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java index 128f9efe7f95..add6f72c8582 100644 --- a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java +++ b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java @@ -19,6 +19,7 @@ package org.apache.druid.emitter.prometheus; +import io.prometheus.client.Gauge; import io.prometheus.client.Histogram; import org.junit.Assert; import org.junit.Test; @@ -30,6 +31,7 @@ public void testMetricsConfiguration() { Metrics metrics = new Metrics("test", null); DimensionsAndCollector dimensionsAndCollector = metrics.getByName("query/time", "historical"); + DimensionsAndCollector d = metrics.getByName("segment/loadQueue/count", "historical"); Assert.assertNotNull(dimensionsAndCollector); String[] dimensions = dimensionsAndCollector.getDimensions(); Assert.assertEquals("dataSource", dimensions[0]); diff --git a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java new file mode 100644 index 000000000000..2d8572e5d57f --- /dev/null +++ b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java @@ -0,0 +1,30 @@ +package org.apache.druid.emitter.prometheus; + +import com.google.common.collect.ImmutableMap; +import io.prometheus.client.CollectorRegistry; +import org.apache.druid.java.util.emitter.service.ServiceMetricEvent; +import org.joda.time.DateTime; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class PrometheusEmitterTest +{ + @Test + public void testEmitter() { + PrometheusEmitterConfig config = new PrometheusEmitterConfig(null, null, 0); + PrometheusEmitter emitter = new PrometheusEmitter(config); + ServiceMetricEvent build = ServiceMetricEvent.builder() + .setDimension("server", "druid-data01.vpc.region") + .build("segment/loadQueue/count", 10) + .build(ImmutableMap.of("service", "historical")); + assertEquals("historical", build.getService()); + assertFalse(build.getUserDims().isEmpty()); + emitter.emit(build); + Double count = CollectorRegistry.defaultRegistry.getSampleValue( + "druid_segment_loadqueue_count", new String[]{"server"}, new String[]{"druid_data01_vpc_region"} + ); + assertEquals(10, count.intValue()); + } +} From 71c83305961e86bb92f470e0c05655b959703f83 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Tue, 28 Jan 2020 18:58:06 -0800 Subject: [PATCH 021/107] Adds missing license header updates website/.spelling to add words used in prometheus-emitter docs. updates docs/operations/metrics.md to correct the spelling of bufferPoolName --- docs/operations/metrics.md | 4 ++-- .../prometheus/PrometheusEmitterTest.java | 19 +++++++++++++++++++ website/.spelling | 3 +++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/operations/metrics.md b/docs/operations/metrics.md index cb0c93f9a4f5..ac6bd095063f 100644 --- a/docs/operations/metrics.md +++ b/docs/operations/metrics.md @@ -240,8 +240,8 @@ These metrics are only available if the JVMMonitor module is included. |`jvm/pool/init`|Initial pool.|poolKind, poolName.|Varies.| |`jvm/pool/max`|Max pool.|poolKind, poolName.|Varies.| |`jvm/pool/used`|Pool used.|poolKind, poolName.|< max pool| -|`jvm/bufferpool/count`|Bufferpool count.|bufferpoolName.|Varies.| -|`jvm/bufferpool/used`|Bufferpool used.|bufferpoolName.|close to capacity| +|`jvm/bufferpool/count`|Bufferpool count.|bufferPoolName.|Varies.| +|`jvm/bufferpool/used`|Bufferpool used.|bufferPoolName.|close to capacity| |`jvm/bufferpool/capacity`|Bufferpool capacity.|bufferPoolName.|Varies.| |`jvm/mem/init`|Initial memory.|memKind.|Varies.| |`jvm/mem/max`|Max memory.|memKind.|Varies.| diff --git a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java index 2d8572e5d57f..65cecdc01646 100644 --- a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java +++ b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java @@ -1,3 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package org.apache.druid.emitter.prometheus; import com.google.common.collect.ImmutableMap; diff --git a/website/.spelling b/website/.spelling index 26aac1c9f794..4a758c4788f6 100644 --- a/website/.spelling +++ b/website/.spelling @@ -539,6 +539,9 @@ com.microsoft.sqlserver.jdbc.SQLServerDriver sqljdbc - ../docs/development/extensions-contrib/statsd.md convertRange +- ../docs/development/extensions-contrib/prometheus.md +HTTPServer +conversionFactor - ../docs/development/extensions-contrib/tdigestsketch-quantiles.md postAggregator quantileFromTDigestSketch From e357befd478c61b40b5e9b3a5cde3f475b43454d Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Tue, 28 Jan 2020 19:59:28 -0800 Subject: [PATCH 022/107] fixes version in extensions-contrib/prometheus-emitter --- extensions-contrib/prometheus-emitter/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions-contrib/prometheus-emitter/pom.xml b/extensions-contrib/prometheus-emitter/pom.xml index 2b83426a5066..569a7e20df78 100644 --- a/extensions-contrib/prometheus-emitter/pom.xml +++ b/extensions-contrib/prometheus-emitter/pom.xml @@ -23,7 +23,7 @@ druid org.apache.druid - 0.17.0-incubating-SNAPSHOT + 0.18.0-SNAPSHOT ../../pom.xml 4.0.0 From 6cce1a41adea09d45ca5c2f811ca54cdc5da2bd1 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Tue, 28 Jan 2020 21:37:11 -0800 Subject: [PATCH 023/107] fix style guide errors --- .../druid/emitter/prometheus/MetricsTest.java | 1 - .../emitter/prometheus/PrometheusEmitterTest.java | 13 ++++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java index add6f72c8582..dfd71ba1c95a 100644 --- a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java +++ b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/MetricsTest.java @@ -19,7 +19,6 @@ package org.apache.druid.emitter.prometheus; -import io.prometheus.client.Gauge; import io.prometheus.client.Histogram; import org.junit.Assert; import org.junit.Test; diff --git a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java index 65cecdc01646..97d6692dfd65 100644 --- a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java +++ b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java @@ -22,28 +22,27 @@ import com.google.common.collect.ImmutableMap; import io.prometheus.client.CollectorRegistry; import org.apache.druid.java.util.emitter.service.ServiceMetricEvent; -import org.joda.time.DateTime; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import org.junit.Assert; public class PrometheusEmitterTest { @Test - public void testEmitter() { + public void testEmitter() + { PrometheusEmitterConfig config = new PrometheusEmitterConfig(null, null, 0); PrometheusEmitter emitter = new PrometheusEmitter(config); ServiceMetricEvent build = ServiceMetricEvent.builder() .setDimension("server", "druid-data01.vpc.region") .build("segment/loadQueue/count", 10) .build(ImmutableMap.of("service", "historical")); - assertEquals("historical", build.getService()); - assertFalse(build.getUserDims().isEmpty()); + Assert.assertEquals("historical", build.getService()); + Assert.assertFalse(build.getUserDims().isEmpty()); emitter.emit(build); Double count = CollectorRegistry.defaultRegistry.getSampleValue( "druid_segment_loadqueue_count", new String[]{"server"}, new String[]{"druid_data01_vpc_region"} ); - assertEquals(10, count.intValue()); + Assert.assertEquals(10, count.intValue()); } } From dbda9722d776567c5604d086f5593f553d373e1f Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Tue, 28 Jan 2020 23:25:33 -0800 Subject: [PATCH 024/107] update import ordering --- .../apache/druid/emitter/prometheus/PrometheusEmitterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java index 97d6692dfd65..560c9cfebe86 100644 --- a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java +++ b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java @@ -22,9 +22,9 @@ import com.google.common.collect.ImmutableMap; import io.prometheus.client.CollectorRegistry; import org.apache.druid.java.util.emitter.service.ServiceMetricEvent; +import org.junit.Assert; import org.junit.Test; -import org.junit.Assert; public class PrometheusEmitterTest { From bad3eaf6a6fe74380650e5c0c19cbdb9cb781139 Mon Sep 17 00:00:00 2001 From: Michael Schiff Date: Wed, 29 Jan 2020 00:35:55 -0800 Subject: [PATCH 025/107] add another word to website/.spelling --- website/.spelling | 1 + 1 file changed, 1 insertion(+) diff --git a/website/.spelling b/website/.spelling index 4a758c4788f6..309c6aa20b8d 100644 --- a/website/.spelling +++ b/website/.spelling @@ -542,6 +542,7 @@ convertRange - ../docs/development/extensions-contrib/prometheus.md HTTPServer conversionFactor +prometheus - ../docs/development/extensions-contrib/tdigestsketch-quantiles.md postAggregator quantileFromTDigestSketch From 14409955bf3062e2f6b93c92abf7a4c65110745d Mon Sep 17 00:00:00 2001 From: Tianxin Zhao Date: Sun, 29 Mar 2020 15:15:22 -0700 Subject: [PATCH 026/107] remove unthrown declared exception --- .../druid/emitter/prometheus/PrometheusEmitterModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java index 6cc2b06c9ead..b56e56c49f2a 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java @@ -56,7 +56,7 @@ public void configure(Binder binder) @Provides @ManageLifecycle @Named(EMITTER_TYPE) - public Emitter getEmitter(PrometheusEmitterConfig config) throws IOException + public Emitter getEmitter(PrometheusEmitterConfig config) { return PrometheusEmitter.of(config); } From 97fd41c0de7bb73a9f6def5a1fd75457fb833e15 Mon Sep 17 00:00:00 2001 From: Tianxin Zhao Date: Sun, 29 Mar 2020 16:56:04 -0700 Subject: [PATCH 027/107] remove unused import --- .../apache/druid/emitter/prometheus/PrometheusEmitterModule.java | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java index b56e56c49f2a..bb26c4d37d58 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterModule.java @@ -29,7 +29,6 @@ import org.apache.druid.initialization.DruidModule; import org.apache.druid.java.util.emitter.core.Emitter; -import java.io.IOException; import java.util.Collections; import java.util.List; From 62a7f16814f4c149eb89141a73e8ce44b4f94688 Mon Sep 17 00:00:00 2001 From: Tianxin Zhao Date: Sun, 29 Mar 2020 20:11:02 -0700 Subject: [PATCH 028/107] Add missing dependency in druid-processing --- processing/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/processing/pom.xml b/processing/pom.xml index 1c824166f67b..d954c76c61c5 100644 --- a/processing/pom.xml +++ b/processing/pom.xml @@ -91,6 +91,10 @@ commons-io commons-io + + org.apache.commons + commons-lang3 + commons-net commons-net From 6f524ae4585cefb123c9bceca8c7718c17f569d9 Mon Sep 17 00:00:00 2001 From: Tianxin Zhao Date: Mon, 30 Mar 2020 16:45:05 -0700 Subject: [PATCH 029/107] Update extension version --- extensions-contrib/prometheus-emitter/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions-contrib/prometheus-emitter/pom.xml b/extensions-contrib/prometheus-emitter/pom.xml index 569a7e20df78..f9c1e64cb9e5 100644 --- a/extensions-contrib/prometheus-emitter/pom.xml +++ b/extensions-contrib/prometheus-emitter/pom.xml @@ -23,7 +23,7 @@ druid org.apache.druid - 0.18.0-SNAPSHOT + 0.19.0-SNAPSHOT ../../pom.xml 4.0.0 From d5b6aa8933c01cb82a2faa9f3c97576bafeae220 Mon Sep 17 00:00:00 2001 From: Tianxin Zhao Date: Sun, 5 Apr 2020 22:15:11 -0700 Subject: [PATCH 030/107] Pushgateway strategy for metrics --- .../extensions-contrib/prometheus.md | 5 ++- extensions-contrib/prometheus-emitter/pom.xml | 5 +++ .../druid/emitter/prometheus/Metrics.java | 4 ++ .../emitter/prometheus/PrometheusEmitter.java | 44 ++++++++++++++----- .../prometheus/PrometheusEmitterConfig.java | 31 ++++++++++++- .../prometheus/PrometheusEmitterTest.java | 2 +- 6 files changed, 77 insertions(+), 14 deletions(-) diff --git a/docs/development/extensions-contrib/prometheus.md b/docs/development/extensions-contrib/prometheus.md index eec0bd308b05..6ede7923a469 100644 --- a/docs/development/extensions-contrib/prometheus.md +++ b/docs/development/extensions-contrib/prometheus.md @@ -36,9 +36,12 @@ All the configuration parameters for the Prometheus emitter are under `druid.emi |property|description|required?|default| |--------|-----------|---------|-------| -|`druid.emitter.prometheus.port`|The port on which to expose the prometheus HTTPServer.|yes|none| +|`druid.emitter.prometheus.strategy`|The strategy to expose prometheus metrics. Default strategy `exporter` would expose metrics for scraping purpose. Only peon task (short-lived jobs) need to use `pushgateway` strategy.|yes|scraping| +|`druid.emitter.prometheus.port`|The port on which to expose the prometheus HTTPServer. Required if using exporter strategy.|no|none| |`druid.emitter.prometheus.namespace`|Optional metric namespace. Must match the regex `[a-zA-Z_:][a-zA-Z0-9_:]*`|no|"druid"| |`druid.emitter.prometheus.dimensionMapPath`|JSON file defining the Prometheus metric type, desired dimensions, help text, and conversionFactor for every Druid metric.|no|Default mapping provided. See below.| +|`druid.emitter.prometheus.pushGatewayAddress`|Pushgateway address. Required if using pushgateway strategy|no|none| + ### Metric names diff --git a/extensions-contrib/prometheus-emitter/pom.xml b/extensions-contrib/prometheus-emitter/pom.xml index f9c1e64cb9e5..6a38f7e9c6c2 100644 --- a/extensions-contrib/prometheus-emitter/pom.xml +++ b/extensions-contrib/prometheus-emitter/pom.xml @@ -50,6 +50,11 @@ simpleclient_httpserver 0.7.0 + + io.prometheus + simpleclient_pushgateway + 0.7.0 + com.google.code.findbugs jsr305 diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java index 7a3487179832..a5d1861a2e16 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -123,6 +123,10 @@ private Map readMap(String path) } } + public Map getMap(){ + return map; + } + public static class Metric { public final SortedSet dimensions; diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index 6080c871fc80..1292567f1006 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -20,10 +20,12 @@ package org.apache.druid.emitter.prometheus; +import com.google.common.collect.ImmutableMap; import io.prometheus.client.Counter; import io.prometheus.client.Gauge; import io.prometheus.client.Histogram; import io.prometheus.client.exporter.HTTPServer; +import io.prometheus.client.exporter.PushGateway; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.java.util.emitter.core.Emitter; import org.apache.druid.java.util.emitter.core.Event; @@ -42,9 +44,12 @@ public class PrometheusEmitter implements Emitter private static final Logger log = new Logger(PrometheusEmitter.class); private final Metrics metrics; private final PrometheusEmitterConfig config; + private final PrometheusEmitterConfig.Strategy strategy; private final Pattern pattern = Pattern.compile("[^a-zA-Z0-9_][^a-zA-Z0-9_]*"); private HTTPServer server; + private PushGateway pushGateway; + private String identifier; static PrometheusEmitter of(PrometheusEmitterConfig config) { @@ -54,6 +59,7 @@ static PrometheusEmitter of(PrometheusEmitterConfig config) public PrometheusEmitter(PrometheusEmitterConfig config) { this.config = config; + this.strategy = config.getStrategy(); metrics = new Metrics(config.getNamespace(), config.getDimensionMapPath()); } @@ -61,16 +67,21 @@ public PrometheusEmitter(PrometheusEmitterConfig config) @Override public void start() { - if (server == null) { - try { - server = new HTTPServer(config.getPort()); - } - catch (IOException e) { - log.error(e, "Unable to start prometheus HTTPServer"); + if(strategy.equals(PrometheusEmitterConfig.Strategy.exporter)){ + if (server == null) { + try { + server = new HTTPServer(config.getPort()); + } + catch (IOException e) { + log.error(e, "Unable to start prometheus HTTPServer"); + } + } else { + log.error("HTTPServer is already started"); } - } else { - log.error("HTTPServer is already started"); + } else if (strategy.equals(PrometheusEmitterConfig.Strategy.pushgateway)){ + pushGateway = new PushGateway(config.getPushGatewayAddress()); } + } @Override @@ -86,6 +97,7 @@ void emitMetric(ServiceMetricEvent metricEvent) String name = metricEvent.getMetric(); String service = metricEvent.getService(); Map userDims = metricEvent.getUserDims(); + identifier = (userDims.get("task") == null ? metricEvent.getHost() : (String) userDims.get("task")); Number value = metricEvent.getValue(); DimensionsAndCollector metric = metrics.getByName(name, service); @@ -116,13 +128,25 @@ void emitMetric(ServiceMetricEvent metricEvent) @Override public void flush() { + Map map = metrics.getMap(); + try { + for (DimensionsAndCollector collector : map.values()) { + pushGateway.push(collector.getCollector(), config.getNamespace(), ImmutableMap.of(config.getNamespace(), identifier)); + } + } catch(IOException e){ + log.error(e, "Unable to push prometheus metrics to pushGateway"); + } } @Override public void close() { - if (server != null) { - server.stop(); + if(strategy.equals(PrometheusEmitterConfig.Strategy.exporter)){ + if (server != null) { + server.stop(); + } + } else { + flush(); } } } diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java index 39c94e2af8a1..f713e85ab7eb 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java @@ -24,6 +24,7 @@ import com.google.common.base.Preconditions; import javax.annotation.Nullable; +import javax.validation.constraints.Null; import java.util.regex.Pattern; /** @@ -34,6 +35,9 @@ public class PrometheusEmitterConfig Pattern pattern = Pattern.compile("[a-zA-Z_:][a-zA-Z0-9_:]*"); + @JsonProperty + private final Strategy strategy; + @JsonProperty @Nullable private final String namespace; @@ -43,19 +47,29 @@ public class PrometheusEmitterConfig private final String dimensionMapPath; @JsonProperty + @Nullable private final Integer port; + @JsonProperty + @Nullable + private final String pushGatewayAddress; + @JsonCreator public PrometheusEmitterConfig( + @JsonProperty("strategy") Strategy strategy, @JsonProperty("namespace") @Nullable String namespace, @JsonProperty("dimensionMapPath") @Nullable String dimensionMapPath, - @JsonProperty("port") Integer port + @JsonProperty("port") @Nullable Integer port, + @JsonProperty("pushGatewayAddress") @Nullable String pushGatewayAddress ) { + + this.strategy = Preconditions.checkNotNull(strategy, "Prometheus metrics expose strategy cannot be null"); this.namespace = namespace != null ? namespace : "druid"; Preconditions.checkArgument(pattern.matcher(this.namespace).matches(), "Invalid namespace " + this.namespace); this.dimensionMapPath = dimensionMapPath; - this.port = Preconditions.checkNotNull(port, "Prometheus server port cannot be null."); + this.port = port; + this.pushGatewayAddress = pushGatewayAddress; } public String getNamespace() @@ -72,4 +86,17 @@ public int getPort() { return port; } + + public String getPushGatewayAddress() { + return pushGatewayAddress; + } + + public Strategy getStrategy(){ + return strategy; + } + + public enum Strategy + { + exporter, pushgateway + } } diff --git a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java index 560c9cfebe86..fc2fdd5f7600 100644 --- a/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java +++ b/extensions-contrib/prometheus-emitter/src/test/java/org/apache/druid/emitter/prometheus/PrometheusEmitterTest.java @@ -31,7 +31,7 @@ public class PrometheusEmitterTest @Test public void testEmitter() { - PrometheusEmitterConfig config = new PrometheusEmitterConfig(null, null, 0); + PrometheusEmitterConfig config = new PrometheusEmitterConfig(PrometheusEmitterConfig.Strategy.exporter, null, null, 0, null); PrometheusEmitter emitter = new PrometheusEmitter(config); ServiceMetricEvent build = ServiceMetricEvent.builder() .setDimension("server", "druid-data01.vpc.region") From d662509b750fed17d14e3460cfad3c5314edcb12 Mon Sep 17 00:00:00 2001 From: Tianxin Zhao Date: Sun, 5 Apr 2020 22:18:53 -0700 Subject: [PATCH 031/107] typo --- docs/development/extensions-contrib/prometheus.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/extensions-contrib/prometheus.md b/docs/development/extensions-contrib/prometheus.md index 6ede7923a469..5f695f23c5cd 100644 --- a/docs/development/extensions-contrib/prometheus.md +++ b/docs/development/extensions-contrib/prometheus.md @@ -36,7 +36,7 @@ All the configuration parameters for the Prometheus emitter are under `druid.emi |property|description|required?|default| |--------|-----------|---------|-------| -|`druid.emitter.prometheus.strategy`|The strategy to expose prometheus metrics. Default strategy `exporter` would expose metrics for scraping purpose. Only peon task (short-lived jobs) need to use `pushgateway` strategy.|yes|scraping| +|`druid.emitter.prometheus.strategy`|The strategy to expose prometheus metrics. Default strategy `exporter` would expose metrics for scraping purpose. Only peon task (short-lived jobs) need to use `pushgateway` strategy.|yes|exporter| |`druid.emitter.prometheus.port`|The port on which to expose the prometheus HTTPServer. Required if using exporter strategy.|no|none| |`druid.emitter.prometheus.namespace`|Optional metric namespace. Must match the regex `[a-zA-Z_:][a-zA-Z0-9_:]*`|no|"druid"| |`druid.emitter.prometheus.dimensionMapPath`|JSON file defining the Prometheus metric type, desired dimensions, help text, and conversionFactor for every Druid metric.|no|Default mapping provided. See below.| From 95bef24fb9e6240fa0ba20ad2ba727a28713aac7 Mon Sep 17 00:00:00 2001 From: Tianxin Zhao Date: Mon, 8 Jun 2020 17:53:39 -0700 Subject: [PATCH 032/107] Format fix and nullable strategy --- .../apache/druid/emitter/prometheus/Metrics.java | 3 ++- .../emitter/prometheus/PrometheusEmitter.java | 11 ++++++----- .../prometheus/PrometheusEmitterConfig.java | 14 ++++++++------ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java index a5d1861a2e16..fd62a7b35ccc 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/Metrics.java @@ -123,7 +123,8 @@ private Map readMap(String path) } } - public Map getMap(){ + public Map getMap() + { return map; } diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java index 1292567f1006..945f46100ed9 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitter.java @@ -67,7 +67,7 @@ public PrometheusEmitter(PrometheusEmitterConfig config) @Override public void start() { - if(strategy.equals(PrometheusEmitterConfig.Strategy.exporter)){ + if (strategy.equals(PrometheusEmitterConfig.Strategy.exporter)) { if (server == null) { try { server = new HTTPServer(config.getPort()); @@ -78,7 +78,7 @@ public void start() } else { log.error("HTTPServer is already started"); } - } else if (strategy.equals(PrometheusEmitterConfig.Strategy.pushgateway)){ + } else if (strategy.equals(PrometheusEmitterConfig.Strategy.pushgateway)) { pushGateway = new PushGateway(config.getPushGatewayAddress()); } @@ -131,9 +131,10 @@ public void flush() Map map = metrics.getMap(); try { for (DimensionsAndCollector collector : map.values()) { - pushGateway.push(collector.getCollector(), config.getNamespace(), ImmutableMap.of(config.getNamespace(), identifier)); + pushGateway.push(collector.getCollector(), config.getNamespace(), ImmutableMap.of(config.getNamespace(), identifier)); } - } catch(IOException e){ + } + catch(IOException e){ log.error(e, "Unable to push prometheus metrics to pushGateway"); } } @@ -141,7 +142,7 @@ public void flush() @Override public void close() { - if(strategy.equals(PrometheusEmitterConfig.Strategy.exporter)){ + if (strategy.equals(PrometheusEmitterConfig.Strategy.exporter)) { if (server != null) { server.stop(); } diff --git a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java index f713e85ab7eb..32439f395cb6 100644 --- a/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java +++ b/extensions-contrib/prometheus-emitter/src/main/java/org/apache/druid/emitter/prometheus/PrometheusEmitterConfig.java @@ -24,7 +24,7 @@ import com.google.common.base.Preconditions; import javax.annotation.Nullable; -import javax.validation.constraints.Null; +import javax.xml.ws.BindingType; import java.util.regex.Pattern; /** @@ -56,15 +56,15 @@ public class PrometheusEmitterConfig @JsonCreator public PrometheusEmitterConfig( - @JsonProperty("strategy") Strategy strategy, + @JsonProperty("strategy") @Nullable Strategy strategy, @JsonProperty("namespace") @Nullable String namespace, @JsonProperty("dimensionMapPath") @Nullable String dimensionMapPath, - @JsonProperty("port") @Nullable Integer port, + @JsonProperty("port") @Nullable Integer port, @JsonProperty("pushGatewayAddress") @Nullable String pushGatewayAddress ) { - this.strategy = Preconditions.checkNotNull(strategy, "Prometheus metrics expose strategy cannot be null"); + this.strategy = strategy != null ? strategy : Strategy.exporter; this.namespace = namespace != null ? namespace : "druid"; Preconditions.checkArgument(pattern.matcher(this.namespace).matches(), "Invalid namespace " + this.namespace); this.dimensionMapPath = dimensionMapPath; @@ -87,11 +87,13 @@ public int getPort() return port; } - public String getPushGatewayAddress() { + public String getPushGatewayAddress() + { return pushGatewayAddress; } - public Strategy getStrategy(){ + public Strategy getStrategy() + { return strategy; } From 2145c72eb0632a7b4b940bea09cb2cb149743df4 Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn <52679095+maytasm@users.noreply.github.com> Date: Tue, 26 May 2020 06:37:01 -1000 Subject: [PATCH 033/107] Update doc on tmp dir (java.io.tmpdir) best practice (#9910) * Update doc on tmp dir best practice * remove local recommendation --- docs/configuration/index.md | 2 +- docs/operations/basic-cluster-tuning.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/index.md b/docs/configuration/index.md index e1a9ddeac992..0f2175b3aa1c 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -71,7 +71,7 @@ There are four JVM parameters that we set on all of our processes: 1. `-Duser.timezone=UTC` This sets the default timezone of the JVM to UTC. We always set this and do not test with other default timezones, so local timezones might work, but they also might uncover weird and interesting bugs. To issue queries in a non-UTC timezone, see [query granularities](../querying/granularities.html#period-granularities) 2. `-Dfile.encoding=UTF-8` This is similar to timezone, we test assuming UTF-8. Local encodings might work, but they also might result in weird and interesting bugs. -3. `-Djava.io.tmpdir=` Various parts of the system that interact with the file system do it via temporary files, and these files can get somewhat large. Many production systems are set up to have small (but fast) `/tmp` directories, which can be problematic with Druid so we recommend pointing the JVM’s tmp directory to something with a little more meat. +3. `-Djava.io.tmpdir=` Various parts of the system that interact with the file system do it via temporary files, and these files can get somewhat large. Many production systems are set up to have small (but fast) `/tmp` directories, which can be problematic with Druid so we recommend pointing the JVM’s tmp directory to something with a little more meat. This directory should not be volatile tmpfs. This directory should also have good read and write speed and hence NFS mount should strongly be avoided. 4. `-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager` This allows log4j2 to handle logs for non-log4j2 components (like jetty) which use standard java logging. ### Extensions diff --git a/docs/operations/basic-cluster-tuning.md b/docs/operations/basic-cluster-tuning.md index 7b62e2bf0128..6fee5ffe12a9 100644 --- a/docs/operations/basic-cluster-tuning.md +++ b/docs/operations/basic-cluster-tuning.md @@ -389,7 +389,7 @@ Enabling process termination on out-of-memory errors is useful as well, since th ``` -Duser.timezone=UTC -Dfile.encoding=UTF-8 --Djava.io.tmpdir= +-Djava.io.tmpdir= -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager -Dorg.jboss.logging.provider=slf4j -Dnet.spy.log.LoggerImpl=net.spy.memcached.compat.log.SLF4JLogger From be9bf6ead73589e4091a46fe2c6373eadc224c21 Mon Sep 17 00:00:00 2001 From: Chi Cao Minh Date: Tue, 26 May 2020 20:13:08 -0700 Subject: [PATCH 034/107] Disable function code coverage check (#9933) As observed in https://github.com/apache/druid/pull/9905 and https://github.com/apache/druid/pull/9915, the function code coverage check flags false positive issues, so it should be disabled for now. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6dd1adf6629e..401af9c99520 100644 --- a/.travis.yml +++ b/.travis.yml @@ -166,7 +166,8 @@ jobs: project_files="$(echo "${all_files}" | grep "${regex}" || [[ $? == 1 ]])"; fi - for f in ${project_files}; do echo $f; done # for debugging - # Check diff code coverage for the maven projects being tested (retry install in case of network error) + # Check diff code coverage for the maven projects being tested (retry install in case of network error). + # Currently, the function coverage check is not reliable, so it is disabled. - > if [ -n "${project_files}" ]; then travis_retry npm install @connectis/diff-test-coverage@1.5.3 @@ -176,7 +177,7 @@ jobs: --type jacoco --line-coverage 65 --branch-coverage 65 - --function-coverage 80 + --function-coverage 0 -- || { printf "\nDiff code coverage check failed. To view coverage report, run 'mvn clean test jacoco:report' and open 'target/site/jacoco/index.html'\n" && false; } fi From 1df7a62efded00edfd82f93547bcf820237e017b Mon Sep 17 00:00:00 2001 From: Suneet Saldanha <44787917+suneet-s@users.noreply.github.com> Date: Wed, 27 May 2020 13:03:53 -0700 Subject: [PATCH 035/107] Make it easier for devs to add CalciteQueryTests (#9922) * Add ingestion specs for CalciteQueryTests This PR introduces ingestion specs that can be used for local testing so that CalciteQueryTests can be built on a druid cluster. * Add README * Update sql/src/test/resources/calcite/tests/README.md --- .../druid/sql/calcite/util/CalciteTests.java | 348 +++++++++--------- .../test/resources/calcite/tests/README.md | 7 + sql/src/test/resources/calcite/tests/foo.json | 53 +++ .../test/resources/calcite/tests/foo2.json | 47 +++ .../test/resources/calcite/tests/foo4.json | 52 +++ .../resources/calcite/tests/lookyloo.json | 6 + .../test/resources/calcite/tests/numFoo.json | 79 ++++ 7 files changed, 413 insertions(+), 179 deletions(-) create mode 100644 sql/src/test/resources/calcite/tests/README.md create mode 100644 sql/src/test/resources/calcite/tests/foo.json create mode 100644 sql/src/test/resources/calcite/tests/foo2.json create mode 100644 sql/src/test/resources/calcite/tests/foo4.json create mode 100644 sql/src/test/resources/calcite/tests/lookyloo.json create mode 100644 sql/src/test/resources/calcite/tests/numFoo.json diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java index 08742812f89b..588d5adb8e37 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java @@ -124,6 +124,7 @@ import java.util.Set; import java.util.concurrent.Executor; import java.util.function.BooleanSupplier; +import java.util.stream.Collectors; /** * Utility functions for Calcite tests. @@ -309,189 +310,178 @@ public AuthenticationResult createEscalatedAuthenticationResult() .withRollup(false) .build(); - public static final List ROWS1 = ImmutableList.of( - createRow( - ImmutableMap.builder() - .put("t", "2000-01-01") - .put("m1", "1.0") - .put("m2", "1.0") - .put("dim1", "") - .put("dim2", ImmutableList.of("a")) - .put("dim3", ImmutableList.of("a", "b")) - .build() - ), - createRow( - ImmutableMap.builder() - .put("t", "2000-01-02") - .put("m1", "2.0") - .put("m2", "2.0") - .put("dim1", "10.1") - .put("dim2", ImmutableList.of()) - .put("dim3", ImmutableList.of("b", "c")) - .build() - ), - createRow( - ImmutableMap.builder() - .put("t", "2000-01-03") - .put("m1", "3.0") - .put("m2", "3.0") - .put("dim1", "2") - .put("dim2", ImmutableList.of("")) - .put("dim3", ImmutableList.of("d")) - .build() - ), - createRow( - ImmutableMap.builder() - .put("t", "2001-01-01") - .put("m1", "4.0") - .put("m2", "4.0") - .put("dim1", "1") - .put("dim2", ImmutableList.of("a")) - .put("dim3", ImmutableList.of("")) - .build() - ), - createRow( - ImmutableMap.builder() - .put("t", "2001-01-02") - .put("m1", "5.0") - .put("m2", "5.0") - .put("dim1", "def") - .put("dim2", ImmutableList.of("abc")) - .put("dim3", ImmutableList.of()) - .build() - ), - createRow( - ImmutableMap.builder() - .put("t", "2001-01-03") - .put("m1", "6.0") - .put("m2", "6.0") - .put("dim1", "abc") - .build() - ) + public static final List> RAW_ROWS1 = ImmutableList.of( + ImmutableMap.builder() + .put("t", "2000-01-01") + .put("m1", "1.0") + .put("m2", "1.0") + .put("dim1", "") + .put("dim2", ImmutableList.of("a")) + .put("dim3", ImmutableList.of("a", "b")) + .build(), + ImmutableMap.builder() + .put("t", "2000-01-02") + .put("m1", "2.0") + .put("m2", "2.0") + .put("dim1", "10.1") + .put("dim2", ImmutableList.of()) + .put("dim3", ImmutableList.of("b", "c")) + .build(), + ImmutableMap.builder() + .put("t", "2000-01-03") + .put("m1", "3.0") + .put("m2", "3.0") + .put("dim1", "2") + .put("dim2", ImmutableList.of("")) + .put("dim3", ImmutableList.of("d")) + .build(), + ImmutableMap.builder() + .put("t", "2001-01-01") + .put("m1", "4.0") + .put("m2", "4.0") + .put("dim1", "1") + .put("dim2", ImmutableList.of("a")) + .put("dim3", ImmutableList.of("")) + .build(), + ImmutableMap.builder() + .put("t", "2001-01-02") + .put("m1", "5.0") + .put("m2", "5.0") + .put("dim1", "def") + .put("dim2", ImmutableList.of("abc")) + .put("dim3", ImmutableList.of()) + .build(), + ImmutableMap.builder() + .put("t", "2001-01-03") + .put("m1", "6.0") + .put("m2", "6.0") + .put("dim1", "abc") + .build() ); - - public static final List ROWS1_WITH_NUMERIC_DIMS = ImmutableList.of( - createRow( - ImmutableMap.builder() - .put("t", "2000-01-01") - .put("m1", "1.0") - .put("m2", "1.0") - .put("d1", 1.0) - .put("f1", 1.0f) - .put("l1", 7L) - .put("dim1", "") - .put("dim2", ImmutableList.of("a")) - .put("dim3", ImmutableList.of("a", "b")) - .put("dim4", "a") - .put("dim5", "aa") - .build(), - PARSER_NUMERIC_DIMS - ), - createRow( - ImmutableMap.builder() - .put("t", "2000-01-02") - .put("m1", "2.0") - .put("m2", "2.0") - .put("d1", 1.7) - .put("d2", 1.7) - .put("f1", 0.1f) - .put("f2", 0.1f) - .put("l1", 325323L) - .put("l2", 325323L) - .put("dim1", "10.1") - .put("dim2", ImmutableList.of()) - .put("dim3", ImmutableList.of("b", "c")) - .put("dim4", "a") - .put("dim5", "ab") - .build(), - PARSER_NUMERIC_DIMS - ), - createRow( - ImmutableMap.builder() - .put("t", "2000-01-03") - .put("m1", "3.0") - .put("m2", "3.0") - .put("d1", 0.0) - .put("d2", 0.0) - .put("f1", 0.0) - .put("f2", 0.0) - .put("l1", 0) - .put("l2", 0) - .put("dim1", "2") - .put("dim2", ImmutableList.of("")) - .put("dim3", ImmutableList.of("d")) - .put("dim4", "a") - .put("dim5", "ba") - .build(), - PARSER_NUMERIC_DIMS - ), - createRow( - ImmutableMap.builder() - .put("t", "2001-01-01") - .put("m1", "4.0") - .put("m2", "4.0") - .put("dim1", "1") - .put("dim2", ImmutableList.of("a")) - .put("dim3", ImmutableList.of("")) - .put("dim4", "b") - .put("dim5", "ad") - .build(), - PARSER_NUMERIC_DIMS - ), - createRow( - ImmutableMap.builder() - .put("t", "2001-01-02") - .put("m1", "5.0") - .put("m2", "5.0") - .put("dim1", "def") - .put("dim2", ImmutableList.of("abc")) - .put("dim3", ImmutableList.of()) - .put("dim4", "b") - .put("dim5", "aa") - .build(), - PARSER_NUMERIC_DIMS - ), - createRow( - ImmutableMap.builder() - .put("t", "2001-01-03") - .put("m1", "6.0") - .put("m2", "6.0") - .put("dim1", "abc") - .put("dim4", "b") - .put("dim5", "ab") - .build(), - PARSER_NUMERIC_DIMS - ) + public static final List ROWS1 = + RAW_ROWS1.stream().map(CalciteTests::createRow).collect(Collectors.toList()); + + public static final List> RAW_ROWS1_WITH_NUMERIC_DIMS = ImmutableList.of( + ImmutableMap.builder() + .put("t", "2000-01-01") + .put("m1", "1.0") + .put("m2", "1.0") + .put("d1", 1.0) + .put("f1", 1.0f) + .put("l1", 7L) + .put("dim1", "") + .put("dim2", ImmutableList.of("a")) + .put("dim3", ImmutableList.of("a", "b")) + .put("dim4", "a") + .put("dim5", "aa") + .build(), + ImmutableMap.builder() + .put("t", "2000-01-02") + .put("m1", "2.0") + .put("m2", "2.0") + .put("d1", 1.7) + .put("d2", 1.7) + .put("f1", 0.1f) + .put("f2", 0.1f) + .put("l1", 325323L) + .put("l2", 325323L) + .put("dim1", "10.1") + .put("dim2", ImmutableList.of()) + .put("dim3", ImmutableList.of("b", "c")) + .put("dim4", "a") + .put("dim5", "ab") + .build(), + ImmutableMap.builder() + .put("t", "2000-01-03") + .put("m1", "3.0") + .put("m2", "3.0") + .put("d1", 0.0) + .put("d2", 0.0) + .put("f1", 0.0) + .put("f2", 0.0) + .put("l1", 0) + .put("l2", 0) + .put("dim1", "2") + .put("dim2", ImmutableList.of("")) + .put("dim3", ImmutableList.of("d")) + .put("dim4", "a") + .put("dim5", "ba") + .build(), + ImmutableMap.builder() + .put("t", "2001-01-01") + .put("m1", "4.0") + .put("m2", "4.0") + .put("dim1", "1") + .put("dim2", ImmutableList.of("a")) + .put("dim3", ImmutableList.of("")) + .put("dim4", "b") + .put("dim5", "ad") + .build(), + ImmutableMap.builder() + .put("t", "2001-01-02") + .put("m1", "5.0") + .put("m2", "5.0") + .put("dim1", "def") + .put("dim2", ImmutableList.of("abc")) + .put("dim3", ImmutableList.of()) + .put("dim4", "b") + .put("dim5", "aa") + .build(), + ImmutableMap.builder() + .put("t", "2001-01-03") + .put("m1", "6.0") + .put("m2", "6.0") + .put("dim1", "abc") + .put("dim4", "b") + .put("dim5", "ab") + .build() ); - - public static final List ROWS2 = ImmutableList.of( - createRow("2000-01-01", "דרואיד", "he", 1.0), - createRow("2000-01-01", "druid", "en", 1.0), - createRow("2000-01-01", "друид", "ru", 1.0) + public static final List ROWS1_WITH_NUMERIC_DIMS = + RAW_ROWS1_WITH_NUMERIC_DIMS.stream().map(raw -> createRow(raw, PARSER_NUMERIC_DIMS)).collect(Collectors.toList()); + + public static final List> RAW_ROWS2 = ImmutableList.of( + ImmutableMap.builder() + .put("t", "2000-01-01") + .put("dim1", "דרואיד") + .put("dim2", "he") + .put("m1", 1.0) + .build(), + ImmutableMap.builder() + .put("t", "2000-01-01") + .put("dim1", "druid") + .put("dim2", "en") + .put("m1", 1.0) + .build(), + ImmutableMap.builder() + .put("t", "2000-01-01") + .put("dim1", "друид") + .put("dim2", "ru") + .put("m1", 1.0) + .build() ); - - public static final List ROWS1_WITH_FULL_TIMESTAMP = ImmutableList.of( - createRow( - ImmutableMap.builder() - .put("t", "2000-01-01T10:51:45.695Z") - .put("m1", "1.0") - .put("m2", "1.0") - .put("dim1", "") - .put("dim2", ImmutableList.of("a")) - .put("dim3", ImmutableList.of("a", "b")) - .build() - ), - createRow( - ImmutableMap.builder() - .put("t", "2000-01-18T10:51:45.695Z") - .put("m1", "2.0") - .put("m2", "2.0") - .put("dim1", "10.1") - .put("dim2", ImmutableList.of()) - .put("dim3", ImmutableList.of("b", "c")) - .build() - ) + public static final List ROWS2 = + RAW_ROWS2.stream().map(CalciteTests::createRow).collect(Collectors.toList()); + + public static final List> RAW_ROWS1_WITH_FULL_TIMESTAMP = ImmutableList.of( + ImmutableMap.builder() + .put("t", "2000-01-01T10:51:45.695Z") + .put("m1", "1.0") + .put("m2", "1.0") + .put("dim1", "") + .put("dim2", ImmutableList.of("a")) + .put("dim3", ImmutableList.of("a", "b")) + .build(), + ImmutableMap.builder() + .put("t", "2000-01-18T10:51:45.695Z") + .put("m1", "2.0") + .put("m2", "2.0") + .put("dim1", "10.1") + .put("dim2", ImmutableList.of()) + .put("dim3", ImmutableList.of("b", "c")) + .build() ); + public static final List ROWS1_WITH_FULL_TIMESTAMP = + RAW_ROWS1_WITH_FULL_TIMESTAMP.stream().map(CalciteTests::createRow).collect(Collectors.toList()); public static final List FORBIDDEN_ROWS = ImmutableList.of( diff --git a/sql/src/test/resources/calcite/tests/README.md b/sql/src/test/resources/calcite/tests/README.md new file mode 100644 index 000000000000..461679c1e53a --- /dev/null +++ b/sql/src/test/resources/calcite/tests/README.md @@ -0,0 +1,7 @@ +This package contains ingestion specs for datasources used by Calcite Tests. + +The purpose of these files is to make it easier to look at and manipulate the data under test so that you can easily +validate if the results of the SQL query written are as expected. + +> NOTE: The provided specs are not guaranteed to be in sync with the datasources used by the test. The source of truth +> is org.apache.druid.sql.calcite.util.CalciteTests diff --git a/sql/src/test/resources/calcite/tests/foo.json b/sql/src/test/resources/calcite/tests/foo.json new file mode 100644 index 000000000000..4b0d65b5d215 --- /dev/null +++ b/sql/src/test/resources/calcite/tests/foo.json @@ -0,0 +1,53 @@ +{ + "type": "index_parallel", + "spec": { + "ioConfig": { + "type": "index_parallel", + "inputSource": { + "type": "inline", + "data": "{\"t\":\"2000-01-01\",\"m1\":\"1.0\",\"m2\":\"1.0\",\"dim1\":\"\",\"dim2\":[\"a\"],\"dim3\":[\"a\",\"b\"]},\n{\"t\":\"2000-01-02\",\"m1\":\"2.0\",\"m2\":\"2.0\",\"dim1\":\"10.1\",\"dim2\":[],\"dim3\":[\"b\",\"c\"]},\n{\"t\":\"2000-01-03\",\"m1\":\"3.0\",\"m2\":\"3.0\",\"dim1\":\"2\",\"dim2\":[\"\"],\"dim3\":[\"d\"]},\n{\"t\":\"2001-01-01\",\"m1\":\"4.0\",\"m2\":\"4.0\",\"dim1\":\"1\",\"dim2\":[\"a\"],\"dim3\":[\"\"]},\n{\"t\":\"2001-01-02\",\"m1\":\"5.0\",\"m2\":\"5.0\",\"dim1\":\"def\",\"dim2\":[\"abc\"],\"dim3\":[]},\n{\"t\":\"2001-01-03\",\"m1\":\"6.0\",\"m2\":\"6.0\",\"dim1\":\"abc\"}" + }, + "inputFormat": { + "type": "json" + } + }, + "tuningConfig": { + "type": "index_parallel", + "partitionsSpec": { + "type": "dynamic" + } + }, + "dataSchema": { + "dataSource": "foo", + "granularitySpec": { + "type": "uniform", + "queryGranularity": "NONE", + "rollup": false, + "segmentGranularity": "YEAR" + }, + "timestampSpec": { + "column": "t", + "format": "auto" + }, + "dimensionsSpec": { + "dimensions": [ + "dim1", + "dim2", + "dim3" + ] + }, + "metricsSpec": [ + { + "name": "m1", + "type": "floatSum", + "fieldName": "m1" + }, + { + "name": "m2", + "type": "doubleSum", + "fieldName": "m2" + } + ] + } + } +} \ No newline at end of file diff --git a/sql/src/test/resources/calcite/tests/foo2.json b/sql/src/test/resources/calcite/tests/foo2.json new file mode 100644 index 000000000000..d865c2ce549f --- /dev/null +++ b/sql/src/test/resources/calcite/tests/foo2.json @@ -0,0 +1,47 @@ +{ + "type": "index_parallel", + "spec": { + "ioConfig": { + "type": "index_parallel", + "inputSource": { + "type": "inline", + "data": "{\"t\":\"2000-01-01\",\"dim1\":\"דרואיד\",\"dim2\":\"he\",\"m1\":1.0}\n{\"t\":\"2000-01-01\",\"dim1\":\"druid\",\"dim2\":\"en\",\"m1\":1.0}\n{\"t\":\"2000-01-01\",\"dim1\":\"друид\",\"dim2\":\"ru\",\"m1\":1.0}" + }, + "inputFormat": { + "type": "json" + } + }, + "tuningConfig": { + "type": "index_parallel", + "partitionsSpec": { + "type": "dynamic" + } + }, + "dataSchema": { + "dataSource": "foo2", + "granularitySpec": { + "type": "uniform", + "queryGranularity": "NONE", + "rollup": false, + "segmentGranularity": "YEAR" + }, + "timestampSpec": { + "column": "t", + "format": "auto" + }, + "dimensionsSpec": { + "dimensions": [ + "dim1", + "dim2" + ] + }, + "metricsSpec": [ + { + "name": "m1", + "type": "longSum", + "fieldName": "m1" + } + ] + } + } +} \ No newline at end of file diff --git a/sql/src/test/resources/calcite/tests/foo4.json b/sql/src/test/resources/calcite/tests/foo4.json new file mode 100644 index 000000000000..ddf275fc3ba6 --- /dev/null +++ b/sql/src/test/resources/calcite/tests/foo4.json @@ -0,0 +1,52 @@ +{ + "type": "index_parallel", + "spec": { + "ioConfig": { + "type": "index_parallel", + "inputSource": { + "type": "inline", + "data": "{\"t\":\"2000-01-01T10:51:45.695Z\",\"m1\":\"1.0\",\"m2\":\"1.0\",\"dim1\":\"\",\"dim2\":[\"a\"],\"dim3\":[\"a\",\"b\"]}\n{\"t\":\"2000-01-18T10:51:45.695Z\",\"m1\":\"2.0\",\"m2\":\"2.0\",\"dim1\":\"10.1\",\"dim2\":[],\"dim3\":[\"b\",\"c\"]}" + }, + "inputFormat": { + "type": "json" + } + }, + "tuningConfig": { + "type": "index_parallel", + "partitionsSpec": { + "type": "dynamic" + } + }, + "dataSchema": { + "dataSource": "foo4", + "granularitySpec": { + "type": "uniform", + "queryGranularity": "HOUR", + "rollup": true, + "segmentGranularity": "YEAR" + }, + "timestampSpec": { + "column": "t", + "format": "iso" + }, + "dimensionsSpec": { + "dimensions": [ + "dim2", + "dim3" + ] + }, + "metricsSpec": [ + { + "name": "m1", + "type": "floatSum", + "fieldName": "m1" + }, + { + "name": "m2", + "type": "doubleSum", + "fieldName": "m2" + } + ] + } + } +} \ No newline at end of file diff --git a/sql/src/test/resources/calcite/tests/lookyloo.json b/sql/src/test/resources/calcite/tests/lookyloo.json new file mode 100644 index 000000000000..8514c3d38670 --- /dev/null +++ b/sql/src/test/resources/calcite/tests/lookyloo.json @@ -0,0 +1,6 @@ +{ + "a": "xa", + "abc": "xabc", + "nosuchkey": "mysteryvalue", + "6": "x6" +} \ No newline at end of file diff --git a/sql/src/test/resources/calcite/tests/numFoo.json b/sql/src/test/resources/calcite/tests/numFoo.json new file mode 100644 index 000000000000..f298b1db7fef --- /dev/null +++ b/sql/src/test/resources/calcite/tests/numFoo.json @@ -0,0 +1,79 @@ +{ + "type": "index_parallel", + "spec": { + "ioConfig": { + "type": "index_parallel", + "inputSource": { + "type": "inline", + "data": "{\"t\":\"2000-01-01\",\"m1\":\"1.0\",\"m2\":\"1.0\",\"d1\":1.0,\"f1\":1.0,\"l1\":7,\"dim1\":\"\",\"dim2\":[\"a\"],\"dim3\":[\"a\",\"b\"],\"dim4\":\"a\",\"dim5\":\"aa\"}\n{\"t\":\"2000-01-02\",\"m1\":\"2.0\",\"m2\":\"2.0\",\"d1\":1.7,\"d2\":1.7,\"f1\":0.1,\"f2\":0.1,\"l1\":325323,\"l2\":325323,\"dim1\":\"10.1\",\"dim2\":[],\"dim3\":[\"b\",\"c\"],\"dim4\":\"a\",\"dim5\":\"ab\"}\n{\"t\":\"2000-01-03\",\"m1\":\"3.0\",\"m2\":\"3.0\",\"d1\":0.0,\"d2\":0.0,\"f1\":0.0,\"f2\":0.0,\"l1\":0,\"l2\":0,\"dim1\":\"2\",\"dim2\":[\"\"],\"dim3\":[\"d\"],\"dim4\":\"a\",\"dim5\":\"ba\"}\n{\"t\":\"2001-01-01\",\"m1\":\"4.0\",\"m2\":\"4.0\",\"dim1\":\"1\",\"dim2\":[\"a\"],\"dim3\":[\"\"],\"dim4\":\"b\",\"dim5\":\"ad\"}\n{\"t\":\"2001-01-02\",\"m1\":\"5.0\",\"m2\":\"5.0\",\"dim1\":\"def\",\"dim2\":[\"abc\"],\"dim3\":[],\"dim4\":\"b\",\"dim5\":\"aa\"}\n{\"t\":\"2001-01-03\",\"m1\":\"6.0\",\"m2\":\"6.0\",\"dim1\":\"abc\",\"dim4\":\"b\",\"dim5\":\"ab\"}" + }, + "inputFormat": { + "type": "json" + } + }, + "tuningConfig": { + "type": "index_parallel", + "partitionsSpec": { + "type": "dynamic" + } + }, + "dataSchema": { + "dataSource": "numFoo", + "granularitySpec": { + "type": "uniform", + "queryGranularity": "NONE", + "rollup": false, + "segmentGranularity": "YEAR" + }, + "timestampSpec": { + "column": "t", + "format": "auto" + }, + "dimensionsSpec": { + "dimensions": [ + { + "type": "double", + "name": "d1" + }, + { + "type": "double", + "name": "d2" + }, + "dim1", + "dim2", + "dim3", + "dim4", + "dim5", + { + "type": "float", + "name": "f1" + }, + { + "type": "float", + "name": "f2" + }, + { + "type": "long", + "name": "l1" + }, + { + "type": "long", + "name": "l2" + } + ] + }, + "metricsSpec": [ + { + "name": "m1", + "type": "floatSum", + "fieldName": "m1" + }, + { + "name": "m2", + "type": "doubleSum", + "fieldName": "m2" + } + ] + } + } +} \ No newline at end of file From 7b357ab83d091008df1b45d2e0974314c2203891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20L=C3=A9aut=C3=A9?= Date: Wed, 27 May 2020 13:20:32 -0700 Subject: [PATCH 036/107] update kafka client version to 2.5.0 (#9902) - remove dependency on deprecated internal Kafka classes - keep LZ4 version in line with the version shipped with Kafka --- extensions-contrib/kafka-emitter/pom.xml | 2 +- .../kafka-extraction-namespace/pom.xml | 14 +++--- .../kafka-indexing-service/pom.xml | 9 +++- .../kafka/supervisor/KafkaSupervisorTest.java | 44 +++++-------------- .../druid/indexing/kafka/test/TestBroker.java | 28 +++++++++--- integration-tests/docker-base/setup.sh | 26 +++++++---- licenses.yaml | 6 +-- pom.xml | 7 ++- web-console/script/druid | 8 ++-- 9 files changed, 82 insertions(+), 62 deletions(-) diff --git a/extensions-contrib/kafka-emitter/pom.xml b/extensions-contrib/kafka-emitter/pom.xml index 8feed6e97121..1f69ae35b333 100644 --- a/extensions-contrib/kafka-emitter/pom.xml +++ b/extensions-contrib/kafka-emitter/pom.xml @@ -38,7 +38,7 @@ org.apache.kafka kafka-clients - 0.10.2.2 + ${apache.kafka.version} org.apache.druid diff --git a/extensions-core/kafka-extraction-namespace/pom.xml b/extensions-core/kafka-extraction-namespace/pom.xml index a33468a1bff1..0b5f269af427 100644 --- a/extensions-core/kafka-extraction-namespace/pom.xml +++ b/extensions-core/kafka-extraction-namespace/pom.xml @@ -97,11 +97,6 @@ jackson-databind provided - - org.apache.zookeeper - zookeeper - provided - com.google.guava guava @@ -135,6 +130,13 @@ + + org.apache.zookeeper + zookeeper + + 3.5.8 + test + org.apache.kafka kafka_2.12 @@ -176,7 +178,7 @@ org.scala-lang scala-library - 2.12.7 + 2.12.10 test diff --git a/extensions-core/kafka-indexing-service/pom.xml b/extensions-core/kafka-indexing-service/pom.xml index cfc50ff212d7..1f6a64d6e059 100644 --- a/extensions-core/kafka-indexing-service/pom.xml +++ b/extensions-core/kafka-indexing-service/pom.xml @@ -141,6 +141,13 @@ junit test + + org.apache.zookeeper + zookeeper + + 3.5.8 + test + org.apache.kafka kafka_2.12 @@ -188,7 +195,7 @@ org.scala-lang scala-library - 2.12.7 + 2.12.10 test diff --git a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/supervisor/KafkaSupervisorTest.java b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/supervisor/KafkaSupervisorTest.java index 79514d268158..086a9e9d10a0 100644 --- a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/supervisor/KafkaSupervisorTest.java +++ b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/supervisor/KafkaSupervisorTest.java @@ -27,10 +27,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import kafka.admin.AdminUtils; -import kafka.admin.BrokerMetadata; -import kafka.admin.RackAwareMode; -import kafka.utils.ZkUtils; import org.apache.curator.test.TestingCluster; import org.apache.druid.data.input.InputFormat; import org.apache.druid.data.input.impl.DimensionSchema; @@ -89,10 +85,12 @@ import org.apache.druid.server.metrics.DruidMonitorSchedulerConfig; import org.apache.druid.server.metrics.ExceptionCapturingServiceEmitter; import org.apache.druid.server.metrics.NoopServiceEmitter; +import org.apache.kafka.clients.admin.Admin; +import org.apache.kafka.clients.admin.NewPartitions; +import org.apache.kafka.clients.admin.NewTopic; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.common.security.JaasUtils; import org.apache.kafka.common.serialization.ByteArrayDeserializer; import org.apache.kafka.common.serialization.Deserializer; import org.easymock.Capture; @@ -111,8 +109,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import scala.Option; -import scala.collection.Seq; import java.io.File; import java.io.IOException; @@ -148,7 +144,6 @@ public class KafkaSupervisorTest extends EasyMockSupport private static String kafkaHost; private static DataSchema dataSchema; private static int topicPostfix; - private static ZkUtils zkUtils; private final int numThreads; @@ -202,8 +197,6 @@ public static void setupClass() throws Exception kafkaHost = StringUtils.format("localhost:%d", kafkaServer.getPort()); dataSchema = getDataSchema(DATASOURCE); - - zkUtils = ZkUtils.apply(zkServer.getConnectString(), 30000, 30000, JaasUtils.isZkSecurityEnabled()); } @Before @@ -237,9 +230,6 @@ public static void tearDownClass() throws IOException zkServer.stop(); zkServer = null; - - zkUtils.close(); - zkUtils = null; } @Test @@ -3242,8 +3232,12 @@ public void testIsTaskCurrent() private void addSomeEvents(int numEventsPerPartition) throws Exception { - //create topic manually - AdminUtils.createTopic(zkUtils, topic, NUM_PARTITIONS, 1, new Properties(), RackAwareMode.Enforced$.MODULE$); + // create topic manually + try (Admin admin = kafkaServer.newAdminClient()) { + admin.createTopics( + Collections.singletonList(new NewTopic(topic, NUM_PARTITIONS, (short) 1)) + ).all().get(); + } try (final KafkaProducer kafkaProducer = kafkaServer.newProducer()) { kafkaProducer.initTransactions(); @@ -3266,23 +3260,9 @@ private void addSomeEvents(int numEventsPerPartition) throws Exception private void addMoreEvents(int numEventsPerPartition, int num_partitions) throws Exception { - Seq brokerList = AdminUtils.getBrokerMetadatas( - zkUtils, - RackAwareMode.Enforced$.MODULE$, - Option.apply(zkUtils.getSortedBrokerList()) - ); - scala.collection.Map> replicaAssignment = AdminUtils.assignReplicasToBrokers( - brokerList, - num_partitions, - 1, 0, 0 - ); - AdminUtils.createOrUpdateTopicPartitionAssignmentPathInZK( - zkUtils, - topic, - replicaAssignment, - new Properties(), - true - ); + try (Admin admin = kafkaServer.newAdminClient()) { + admin.createPartitions(Collections.singletonMap(topic, NewPartitions.increaseTo(num_partitions))).all().get(); + } try (final KafkaProducer kafkaProducer = kafkaServer.newProducer()) { kafkaProducer.initTransactions(); diff --git a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/test/TestBroker.java b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/test/TestBroker.java index 0fee75ec9355..9d3f7c500c44 100644 --- a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/test/TestBroker.java +++ b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/test/TestBroker.java @@ -25,6 +25,7 @@ import org.apache.druid.indexing.kafka.KafkaConsumerConfigs; import org.apache.druid.java.util.common.FileUtils; import org.apache.druid.java.util.common.StringUtils; +import org.apache.kafka.clients.admin.Admin; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.common.serialization.ByteArraySerializer; @@ -100,18 +101,30 @@ public int getPort() public KafkaProducer newProducer() { - return new KafkaProducer(producerProperties()); + return new KafkaProducer<>(producerProperties()); + } + + public Admin newAdminClient() + { + return Admin.create(adminClientProperties()); + } + + Map adminClientProperties() + { + final Map props = new HashMap<>(); + commonClientProperties(props); + return props; } public KafkaConsumer newConsumer() { - return new KafkaConsumer(consumerProperties()); + return new KafkaConsumer<>(consumerProperties()); } - public Map producerProperties() + public Map producerProperties() { - final Map props = new HashMap<>(); - props.put("bootstrap.servers", StringUtils.format("localhost:%d", getPort())); + final Map props = new HashMap<>(); + commonClientProperties(props); props.put("key.serializer", ByteArraySerializer.class.getName()); props.put("value.serializer", ByteArraySerializer.class.getName()); props.put("acks", "all"); @@ -120,6 +133,11 @@ public Map producerProperties() return props; } + void commonClientProperties(Map props) + { + props.put("bootstrap.servers", StringUtils.format("localhost:%d", getPort())); + } + public Map consumerProperties() { final Map props = KafkaConsumerConfigs.getConsumerProperties(); diff --git a/integration-tests/docker-base/setup.sh b/integration-tests/docker-base/setup.sh index a6dc55283a94..7a066ec93c97 100644 --- a/integration-tests/docker-base/setup.sh +++ b/integration-tests/docker-base/setup.sh @@ -31,18 +31,26 @@ apt-get install -y mysql-server apt-get install -y supervisor # Zookeeper -wget -q -O /tmp/zookeeper-3.4.14.tar.gz "https://apache.org/dist/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz" -tar -xzf /tmp/zookeeper-3.4.14.tar.gz -C /usr/local -cp /usr/local/zookeeper-3.4.14/conf/zoo_sample.cfg /usr/local/zookeeper-3.4.14/conf/zoo.cfg -ln -s /usr/local/zookeeper-3.4.14 /usr/local/zookeeper -rm /tmp/zookeeper-3.4.14.tar.gz + +#ZK_VERSION=3.5.8 +#ZK_TAR=apache-zookeeper-$ZK_VERSION-bin + +ZK_VERISON=3.4.14 +ZK_TAR=zookeeper-$ZK_VERSION + +wget -q -O /tmp/$ZK_TAR.tar.gz "https://apache.org/dist/zookeeper/zookeeper-$ZK_VERSION/$ZK_TAR.tar.gz" +tar -xzf /tmp/$ZK_TAR.tar.gz -C /usr/local +cp /usr/local/$ZK_TAR/conf/zoo_sample.cfg /usr/local/$ZK_TAR/conf/zoo.cfg +ln -s /usr/local/$ZK_TAR /usr/local/zookeeper +rm /tmp/$ZK_TAR.tar.gz # Kafka # Match the version to the Kafka client used by KafkaSupervisor -wget -q -O /tmp/kafka_2.12-2.1.1.tgz "https://apache.org/dist/kafka/2.1.1/kafka_2.12-2.1.1.tgz" -tar -xzf /tmp/kafka_2.12-2.1.1.tgz -C /usr/local -ln -s /usr/local/kafka_2.12-2.1.1 /usr/local/kafka -rm /tmp/kafka_2.12-2.1.1.tgz +KAFKA_VERSION=2.5.0 +wget -q -O /tmp/kafka_2.12-$KAFKA_VERSION.tgz "https://apache.org/dist/kafka/$KAFKA_VERSION/kafka_2.12-$KAFKA_VERSION.tgz" +tar -xzf /tmp/kafka_2.12-$KAFKA_VERSION.tgz -C /usr/local +ln -s /usr/local/kafka_2.12-$KAFKA_VERSION /usr/local/kafka +rm /tmp/kafka_2.12-$KAFKA_VERSION.tgz # Druid system user adduser --system --group --no-create-home druid \ diff --git a/licenses.yaml b/licenses.yaml index 6680ade81495..0dd9acd048da 100644 --- a/licenses.yaml +++ b/licenses.yaml @@ -1975,7 +1975,7 @@ name: LZ4 Java license_category: binary module: java-core license_name: Apache License version 2.0 -version: 1.6.0 +version: 1.7.1 libraries: - org.lz4: lz4-java @@ -3251,7 +3251,7 @@ libraries: --- name: Apache Kafka -version: 2.2.2 +version: 2.5.0 license_category: binary module: extensions/druid-kafka-indexing-service license_name: Apache License version 2.0 @@ -4159,7 +4159,7 @@ name: Apache Kafka license_category: binary module: extensions/kafka-extraction-namespace license_name: Apache License version 2.0 -version: 2.2.2 +version: 2.5.0 libraries: - org.apache.kafka: kafka_2.12 - org.apache.kafka: kafka-clients diff --git a/pom.xml b/pom.xml index 351bb91cb231..1b04a8ac461f 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ 0.9.0.M2 4.3.0 2.12.0 - 2.2.2 + 2.5.0 2.0.0 2.2.4 1.15.0 @@ -111,6 +111,9 @@ 2.0.2 1.11.199 2.8.0 + 3.4.14 2.5.7 @@ -772,7 +775,7 @@ org.lz4 lz4-java - 1.6.0 + 1.7.1 com.google.protobuf diff --git a/web-console/script/druid b/web-console/script/druid index 96f67b856a89..767c3a70b9fe 100755 --- a/web-console/script/druid +++ b/web-console/script/druid @@ -58,13 +58,15 @@ function _download_zookeeper() { local dest="$1" local zk_version zk_version="$(_get_zookeeper_version)" + #zk_tar=apache-zookeeper-${zk_version}-bin # for zk 3.5.x + zk_tar=zookeeper-${zk_version} # for zk 3.4.x _log "Downloading zookeeper" - curl -s "https://archive.apache.org/dist/zookeeper/zookeeper-${zk_version}/zookeeper-${zk_version}.tar.gz" \ + curl -s "https://archive.apache.org/dist/zookeeper/zookeeper-${zk_version}/$zk_tar.tar.gz" \ | tar xz \ && rm -rf "$dest" \ - && mv "zookeeper-${zk_version}" "$dest" \ - && rm -f "zookeeper-${zk_version}" + && mv "$zk_tar" "$dest" \ + && rm -f "$zk_tar" } function _build_distribution() { From 4e05823850529ca451bdca7879babcc41c01ec6d Mon Sep 17 00:00:00 2001 From: Suneet Saldanha <44787917+suneet-s@users.noreply.github.com> Date: Thu, 28 May 2020 19:10:26 -0700 Subject: [PATCH 037/107] Add parameterized Calcite tests for join queries (#9923) * Add parameterized Calcite tests for join queries * new tests * review comments --- sql/pom.xml | 5 + .../sql/calcite/BaseCalciteQueryTest.java | 14 +- .../druid/sql/calcite/CalciteQueryTest.java | 369 +++++++++++++----- 3 files changed, 296 insertions(+), 92 deletions(-) diff --git a/sql/pom.xml b/sql/pom.xml index ef8d3f79cf64..cd1c12e9ef97 100644 --- a/sql/pom.xml +++ b/sql/pom.xml @@ -184,6 +184,11 @@ junit test + + pl.pragmatists + JUnitParams + test + org.apache.calcite calcite-core diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java index 4fc1e8e92c87..39800c1911d6 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java @@ -102,11 +102,19 @@ public class BaseCalciteQueryTest extends CalciteTestBase { - public static final String NULL_STRING = NullHandling.defaultStringValue(); - public static final Float NULL_FLOAT = NullHandling.defaultFloatValue(); - public static final Long NULL_LONG = NullHandling.defaultLongValue(); + public static String NULL_STRING; + public static Float NULL_FLOAT; + public static Long NULL_LONG; public static final String HLLC_STRING = VersionOneHyperLogLogCollector.class.getName(); + @BeforeClass + public static void setupNullValues() + { + NULL_STRING = NullHandling.defaultStringValue(); + NULL_FLOAT = NullHandling.defaultFloatValue(); + NULL_LONG = NullHandling.defaultLongValue(); + } + public static final Logger log = new Logger(BaseCalciteQueryTest.class); public static final PlannerConfig PLANNER_CONFIG_DEFAULT = new PlannerConfig(); diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index 81cf68d68927..80330453aa84 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -22,8 +22,11 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; import org.apache.calcite.runtime.CalciteContextException; import org.apache.calcite.tools.ValidationException; +import org.apache.druid.annotations.UsedByJUnitParamsRunner; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.IAE; @@ -105,6 +108,7 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.internal.matchers.ThrowableMessageMatcher; +import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Collections; @@ -112,6 +116,7 @@ import java.util.List; import java.util.Map; +@RunWith(JUnitParamsRunner.class) public class CalciteQueryTest extends BaseCalciteQueryTest { private final boolean useDefault = NullHandling.replaceWithDefault(); @@ -6510,7 +6515,8 @@ public void testAvgDailyCountDistinct() throws Exception } @Test - public void testTopNFilterJoin() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testTopNFilterJoin(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); @@ -6530,6 +6536,7 @@ public void testTopNFilterJoin() throws Exception + ") t2 ON (t1.dim2 = t2.dim2)\n" + "GROUP BY t1.dim1\n" + "ORDER BY 1\n", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -6571,7 +6578,7 @@ public void testTopNFilterJoin() throws Exception Integer.MAX_VALUE ) ) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -6582,7 +6589,8 @@ public void testTopNFilterJoin() throws Exception } @Test - public void testTopNFilterJoinWithProjection() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testTopNFilterJoinWithProjection(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); @@ -6602,6 +6610,7 @@ public void testTopNFilterJoinWithProjection() throws Exception + " LIMIT 2\n" + ") t2 ON (t1.dim2 = t2.dim2)\n" + "GROUP BY SUBSTRING(t1.dim1, 1, 10)", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -6640,7 +6649,7 @@ public void testTopNFilterJoinWithProjection() throws Exception ) ) .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -6651,8 +6660,9 @@ public void testTopNFilterJoinWithProjection() throws Exception } @Test + @Parameters(source = QueryContextForJoinProvider.class) @Ignore("Stopped working after the ability to join on subqueries was added to DruidJoinRule") - public void testRemovableLeftJoin() throws Exception + public void testRemovableLeftJoin(Map queryContext) throws Exception { // LEFT JOIN where the right-hand side can be ignored. @@ -6670,6 +6680,7 @@ public void testRemovableLeftJoin() throws Exception + ") t2 ON (t1.dim2 = t2.dim2)\n" + "GROUP BY t1.dim1\n" + "ORDER BY 1\n", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource(CalciteTests.DATASOURCE1) @@ -6689,7 +6700,7 @@ public void testRemovableLeftJoin() throws Exception Integer.MAX_VALUE ) ) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -8075,7 +8086,8 @@ public void testFilterAndGroupByLookup() throws Exception } @Test - public void testFilterAndGroupByLookupUsingJoinOperatorAllowNulls() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testFilterAndGroupByLookupUsingJoinOperatorAllowNulls(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); @@ -8085,6 +8097,7 @@ public void testFilterAndGroupByLookupUsingJoinOperatorAllowNulls() throws Excep + "FROM foo LEFT JOIN lookup.lookyloo ON foo.dim2 = lookyloo.k\n" + "WHERE lookyloo.v <> 'xa' OR lookyloo.v IS NULL\n" + "GROUP BY lookyloo.v", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -8101,7 +8114,7 @@ public void testFilterAndGroupByLookupUsingJoinOperatorAllowNulls() throws Excep .setDimFilter(or(not(selector("j0.v", "xa", null)), selector("j0.v", null, null))) .setDimensions(dimensions(new DefaultDimensionSpec("j0.v", "d0"))) .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -8112,7 +8125,8 @@ public void testFilterAndGroupByLookupUsingJoinOperatorAllowNulls() throws Excep } @Test - public void testFilterAndGroupByLookupUsingJoinOperatorBackwards() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testFilterAndGroupByLookupUsingJoinOperatorBackwards(Map queryContext) throws Exception { // Like "testFilterAndGroupByLookupUsingJoinOperator", but with the table and lookup reversed. @@ -8124,6 +8138,7 @@ public void testFilterAndGroupByLookupUsingJoinOperatorBackwards() throws Except + "FROM lookup.lookyloo RIGHT JOIN foo ON foo.dim2 = lookyloo.k\n" + "WHERE lookyloo.v <> 'xa'\n" + "GROUP BY lookyloo.v", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -8147,7 +8162,7 @@ public void testFilterAndGroupByLookupUsingJoinOperatorBackwards() throws Except .setGranularity(Granularities.ALL) .setDimensions(dimensions(new DefaultDimensionSpec("v", "d0"))) .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -8158,7 +8173,8 @@ public void testFilterAndGroupByLookupUsingJoinOperatorBackwards() throws Except } @Test - public void testFilterAndGroupByLookupUsingJoinOperator() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testFilterAndGroupByLookupUsingJoinOperatorWithNotFilter(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); @@ -8168,6 +8184,7 @@ public void testFilterAndGroupByLookupUsingJoinOperator() throws Exception + "FROM foo LEFT JOIN lookup.lookyloo ON foo.dim2 = lookyloo.k\n" + "WHERE lookyloo.v <> 'xa'\n" + "GROUP BY lookyloo.v", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -8184,7 +8201,7 @@ public void testFilterAndGroupByLookupUsingJoinOperator() throws Exception .setGranularity(Granularities.ALL) .setDimensions(dimensions(new DefaultDimensionSpec("j0.v", "d0"))) .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -8195,19 +8212,17 @@ public void testFilterAndGroupByLookupUsingJoinOperator() throws Exception } @Test - public void testFilterAndGroupByLookupUsingJoinOperatorWithValueFilterPushdown() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testFilterAndGroupByLookupUsingJoinOperator(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); - Map queryRewriteValueColumnFiltersContext = DEFAULT_QUERY_CONTEXT_BUILDER - .put(QueryContexts.JOIN_FILTER_REWRITE_VALUE_COLUMN_FILTERS_ENABLE_KEY, true) - .build(); testQuery( "SELECT lookyloo.k, COUNT(*)\n" + "FROM foo LEFT JOIN lookup.lookyloo ON foo.dim2 = lookyloo.k\n" + "WHERE lookyloo.v = 'xa'\n" + "GROUP BY lookyloo.k", - queryRewriteValueColumnFiltersContext, + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -8224,7 +8239,7 @@ public void testFilterAndGroupByLookupUsingJoinOperatorWithValueFilterPushdown() .setGranularity(Granularities.ALL) .setDimensions(dimensions(new DefaultDimensionSpec("j0.k", "d0"))) .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) - .setContext(queryRewriteValueColumnFiltersContext) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -8234,7 +8249,8 @@ public void testFilterAndGroupByLookupUsingJoinOperatorWithValueFilterPushdown() } @Test - public void testFilterAndGroupByLookupUsingPostAggregationJoinOperator() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testFilterAndGroupByLookupUsingPostAggregationJoinOperator(Map queryContext) throws Exception { testQuery( "SELECT base.dim2, lookyloo.v, base.cnt FROM (\n" @@ -8242,6 +8258,7 @@ public void testFilterAndGroupByLookupUsingPostAggregationJoinOperator() throws + ") base\n" + "LEFT JOIN lookup.lookyloo ON base.dim2 = lookyloo.k\n" + "WHERE lookyloo.v <> 'xa' OR lookyloo.v IS NULL", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -8254,7 +8271,7 @@ public void testFilterAndGroupByLookupUsingPostAggregationJoinOperator() throws .setGranularity(Granularities.ALL) .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0"))) .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), new LookupDataSource("lookyloo"), @@ -8282,7 +8299,8 @@ public void testFilterAndGroupByLookupUsingPostAggregationJoinOperator() throws } @Test - public void testGroupByInnerJoinOnLookupUsingJoinOperator() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testGroupByInnerJoinOnLookupUsingJoinOperator(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); @@ -8291,6 +8309,7 @@ public void testGroupByInnerJoinOnLookupUsingJoinOperator() throws Exception "SELECT lookyloo.v, COUNT(*)\n" + "FROM foo INNER JOIN lookup.lookyloo ON foo.dim1 = lookyloo.k\n" + "GROUP BY lookyloo.v", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -8306,7 +8325,7 @@ public void testGroupByInnerJoinOnLookupUsingJoinOperator() throws Exception .setGranularity(Granularities.ALL) .setDimensions(dimensions(new DefaultDimensionSpec("j0.v", "d0"))) .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -8316,11 +8335,13 @@ public void testGroupByInnerJoinOnLookupUsingJoinOperator() throws Exception } @Test - public void testSelectOnLookupUsingInnerJoinOperator() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testSelectOnLookupUsingInnerJoinOperator(Map queryContext) throws Exception { testQuery( "SELECT dim2, lookyloo.*\n" + "FROM foo INNER JOIN lookup.lookyloo ON foo.dim2 = lookyloo.k\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -8334,7 +8355,7 @@ public void testSelectOnLookupUsingInnerJoinOperator() throws Exception ) .intervals(querySegmentSpec(Filtration.eternity())) .columns("dim2", "j0.k", "j0.v") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -8346,13 +8367,15 @@ public void testSelectOnLookupUsingInnerJoinOperator() throws Exception } @Test - public void testLeftJoinTwoLookupsUsingJoinOperator() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testLeftJoinTwoLookupsUsingJoinOperator(Map queryContext) throws Exception { testQuery( "SELECT dim1, dim2, l1.v, l2.v\n" + "FROM foo\n" + "LEFT JOIN lookup.lookyloo l1 ON foo.dim1 = l1.k\n" + "LEFT JOIN lookup.lookyloo l2 ON foo.dim2 = l2.k\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -8372,7 +8395,7 @@ public void testLeftJoinTwoLookupsUsingJoinOperator() throws Exception ) .intervals(querySegmentSpec(Filtration.eternity())) .columns("_j0.v", "dim1", "dim2", "j0.v") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -8387,7 +8410,8 @@ public void testLeftJoinTwoLookupsUsingJoinOperator() throws Exception } @Test - public void testInnerJoinTableLookupLookupWithFilterWithOuterLimit() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinTableLookupLookupWithFilterWithOuterLimit(Map queryContext) throws Exception { testQuery( "SELECT dim1\n" @@ -8396,6 +8420,7 @@ public void testInnerJoinTableLookupLookupWithFilterWithOuterLimit() throws Exce + "INNER JOIN lookup.lookyloo l2 ON foo.dim2 = l2.k\n" + "WHERE l.v = 'xa'\n" + "LIMIT 100\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -8417,7 +8442,7 @@ public void testInnerJoinTableLookupLookupWithFilterWithOuterLimit() throws Exce .limit(100) .filters(selector("j0.v", "xa", null)) .columns("dim1") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -8428,7 +8453,8 @@ public void testInnerJoinTableLookupLookupWithFilterWithOuterLimit() throws Exce } @Test - public void testInnerJoinTableLookupLookupWithFilterWithoutLimit() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinTableLookupLookupWithFilterWithoutLimit(Map queryContext) throws Exception { testQuery( "SELECT dim1\n" @@ -8436,6 +8462,7 @@ public void testInnerJoinTableLookupLookupWithFilterWithoutLimit() throws Except + "INNER JOIN lookup.lookyloo l ON foo.dim2 = l.k\n" + "INNER JOIN lookup.lookyloo l2 ON foo.dim2 = l2.k\n" + "WHERE l.v = 'xa'\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -8456,7 +8483,7 @@ public void testInnerJoinTableLookupLookupWithFilterWithoutLimit() throws Except .intervals(querySegmentSpec(Filtration.eternity())) .filters(selector("j0.v", "xa", null)) .columns("dim1") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -8467,7 +8494,8 @@ public void testInnerJoinTableLookupLookupWithFilterWithoutLimit() throws Except } @Test - public void testInnerJoinTableLookupLookupWithFilterWithOuterLimitWithAllColumns() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinTableLookupLookupWithFilterWithOuterLimitWithAllColumns(Map queryContext) throws Exception { testQuery( "SELECT __time, cnt, dim1, dim2, dim3, m1, m2, unique_dim1\n" @@ -8476,6 +8504,7 @@ public void testInnerJoinTableLookupLookupWithFilterWithOuterLimitWithAllColumns + "INNER JOIN lookup.lookyloo l2 ON foo.dim2 = l2.k\n" + "WHERE l.v = 'xa'\n" + "LIMIT 100\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -8497,7 +8526,7 @@ public void testInnerJoinTableLookupLookupWithFilterWithOuterLimitWithAllColumns .limit(100) .filters(selector("j0.v", "xa", null)) .columns("__time", "cnt", "dim1", "dim2", "dim3", "m1", "m2", "unique_dim1") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -8508,7 +8537,8 @@ public void testInnerJoinTableLookupLookupWithFilterWithOuterLimitWithAllColumns } @Test - public void testInnerJoinTableLookupLookupWithFilterWithoutLimitWithAllColumns() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinTableLookupLookupWithFilterWithoutLimitWithAllColumns(Map queryContext) throws Exception { testQuery( "SELECT __time, cnt, dim1, dim2, dim3, m1, m2, unique_dim1\n" @@ -8516,6 +8546,7 @@ public void testInnerJoinTableLookupLookupWithFilterWithoutLimitWithAllColumns() + "INNER JOIN lookup.lookyloo l ON foo.dim2 = l.k\n" + "INNER JOIN lookup.lookyloo l2 ON foo.dim2 = l2.k\n" + "WHERE l.v = 'xa'\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -8536,7 +8567,7 @@ public void testInnerJoinTableLookupLookupWithFilterWithoutLimitWithAllColumns() .intervals(querySegmentSpec(Filtration.eternity())) .filters(selector("j0.v", "xa", null)) .columns("__time", "cnt", "dim1", "dim2", "dim3", "m1", "m2", "unique_dim1") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -8547,7 +8578,8 @@ public void testInnerJoinTableLookupLookupWithFilterWithoutLimitWithAllColumns() } @Test - public void testManyManyInnerJoinOnManyManyLookup() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testManyManyInnerJoinOnManyManyLookup(Map queryContext) throws Exception { testQuery( "SELECT dim1\n" @@ -8572,6 +8604,7 @@ public void testManyManyInnerJoinOnManyManyLookup() throws Exception + "INNER JOIN lookup.lookyloo l18 ON foo.dim2 = l18.k\n" + "INNER JOIN lookup.lookyloo l19 ON foo.dim2 = l19.k\n" + "WHERE l.v = 'xa'\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -8694,7 +8727,7 @@ public void testManyManyInnerJoinOnManyManyLookup() throws Exception .intervals(querySegmentSpec(Filtration.eternity())) .filters(selector("j0.v", "xa", null)) .columns("dim1") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -8705,7 +8738,8 @@ public void testManyManyInnerJoinOnManyManyLookup() throws Exception } @Test - public void testInnerJoinQueryOfLookup() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinQueryOfLookup(Map queryContext) throws Exception { // Cannot vectorize the subquery. cannotVectorize(); @@ -8716,6 +8750,7 @@ public void testInnerJoinQueryOfLookup() throws Exception + "INNER JOIN \n" + " (SELECT SUBSTRING(k, 1, 1) k, LATEST(v, 10) v FROM lookup.lookyloo GROUP BY 1) t1\n" + " ON foo.dim2 = t1.k", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -8744,7 +8779,7 @@ public void testInnerJoinQueryOfLookup() throws Exception ) .intervals(querySegmentSpec(Filtration.eternity())) .columns("dim1", "dim2", "j0.a0") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -8755,7 +8790,8 @@ public void testInnerJoinQueryOfLookup() throws Exception } @Test - public void testInnerJoinQueryOfLookupRemovable() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinQueryOfLookupRemovable(Map queryContext) throws Exception { // Like "testInnerJoinQueryOfLookup", but the subquery is removable. @@ -8765,6 +8801,7 @@ public void testInnerJoinQueryOfLookupRemovable() throws Exception + "INNER JOIN \n" + " (SELECT k, SUBSTRING(v, 1, 3) sk FROM lookup.lookyloo) t1\n" + " ON foo.dim2 = t1.k", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -8779,7 +8816,7 @@ public void testInnerJoinQueryOfLookupRemovable() throws Exception .intervals(querySegmentSpec(Filtration.eternity())) .virtualColumns(expressionVirtualColumn("v0", "substring(\"j0.v\", 0, 3)", ValueType.STRING)) .columns("dim1", "dim2", "v0") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -8791,7 +8828,8 @@ public void testInnerJoinQueryOfLookupRemovable() throws Exception } @Test - public void testInnerJoinTwoLookupsToTableUsingNumericColumn() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinTwoLookupsToTableUsingNumericColumn(Map queryContext) throws Exception { // Regression test for https://github.com/apache/druid/issues/9646. @@ -8803,6 +8841,7 @@ public void testInnerJoinTwoLookupsToTableUsingNumericColumn() throws Exception + "FROM foo\n" + "INNER JOIN lookup.lookyloo l1 ON l1.k = foo.m1\n" + "INNER JOIN lookup.lookyloo l2 ON l2.k = l1.k", + queryContext, ImmutableList.of( Druids.newTimeseriesQueryBuilder() .dataSource( @@ -8850,7 +8889,8 @@ public void testInnerJoinTwoLookupsToTableUsingNumericColumn() throws Exception } @Test - public void testInnerJoinTwoLookupsToTableUsingNumericColumnInReverse() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinTwoLookupsToTableUsingNumericColumnInReverse(Map queryContext) throws Exception { // Like "testInnerJoinTwoLookupsToTableUsingNumericColumn", but the tables are specified backwards. @@ -8861,6 +8901,7 @@ public void testInnerJoinTwoLookupsToTableUsingNumericColumnInReverse() throws E + "FROM lookup.lookyloo l1\n" + "INNER JOIN lookup.lookyloo l2 ON l1.k = l2.k\n" + "INNER JOIN foo on l2.k = foo.m1", + queryContext, ImmutableList.of( Druids.newTimeseriesQueryBuilder() .dataSource( @@ -8904,7 +8945,8 @@ public void testInnerJoinTwoLookupsToTableUsingNumericColumnInReverse() throws E } @Test - public void testInnerJoinLookupTableTable() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinLookupTableTable(Map queryContext) throws Exception { // Regression test for https://github.com/apache/druid/issues/9646. @@ -8917,6 +8959,7 @@ public void testInnerJoinLookupTableTable() throws Exception + "INNER JOIN druid.foo f on f.dim1 = l.k\n" + "INNER JOIN druid.numfoo nf on nf.dim1 = l.k\n" + "GROUP BY 1, 2 ORDER BY 2", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -8974,7 +9017,7 @@ public void testInnerJoinLookupTableTable() throws Exception null ) ) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -8984,7 +9027,8 @@ public void testInnerJoinLookupTableTable() throws Exception } @Test - public void testInnerJoinLookupTableTableChained() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinLookupTableTableChained(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); @@ -8995,6 +9039,7 @@ public void testInnerJoinLookupTableTableChained() throws Exception + "INNER JOIN druid.foo f on f.dim1 = l.k\n" + "INNER JOIN druid.numfoo nf on nf.dim1 = f.dim1\n" + "GROUP BY 1, 2 ORDER BY 2", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -9052,7 +9097,7 @@ public void testInnerJoinLookupTableTableChained() throws Exception null ) ) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -9141,7 +9186,8 @@ public void testCommaJoinLeftFunction() throws Exception // Unfortunately, we have disabled pushing down predicates (conditions and filters) due to https://github.com/apache/druid/pull/9773 // Hence, comma join will result in a cross join with filter on outermost @Test - public void testCommaJoinTableLookupTableMismatchedTypes() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testCommaJoinTableLookupTableMismatchedTypes(Map queryContext) throws Exception { // Regression test for https://github.com/apache/druid/issues/9646. @@ -9152,6 +9198,7 @@ public void testCommaJoinTableLookupTableMismatchedTypes() throws Exception "SELECT COUNT(*)\n" + "FROM foo, lookup.lookyloo l, numfoo\n" + "WHERE foo.cnt = l.k AND l.k = numfoo.cnt\n", + queryContext, ImmutableList.of( Druids.newTimeseriesQueryBuilder() .dataSource( @@ -9192,7 +9239,8 @@ public void testCommaJoinTableLookupTableMismatchedTypes() throws Exception } @Test - public void testJoinTableLookupTableMismatchedTypesWithoutComma() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testJoinTableLookupTableMismatchedTypesWithoutComma(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); @@ -9202,6 +9250,7 @@ public void testJoinTableLookupTableMismatchedTypesWithoutComma() throws Excepti + "FROM foo\n" + "INNER JOIN lookup.lookyloo l ON foo.cnt = l.k\n" + "INNER JOIN numfoo ON l.k = numfoo.cnt\n", + queryContext, ImmutableList.of( Druids.newTimeseriesQueryBuilder() .dataSource( @@ -9217,7 +9266,7 @@ public void testJoinTableLookupTableMismatchedTypesWithoutComma() throws Excepti ) .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) .columns("k", "v0") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), "j0.", @@ -9233,7 +9282,7 @@ public void testJoinTableLookupTableMismatchedTypesWithoutComma() throws Excepti .intervals(querySegmentSpec(Filtration.eternity())) .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) .columns("cnt") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), "_j0.", @@ -9255,7 +9304,8 @@ public void testJoinTableLookupTableMismatchedTypesWithoutComma() throws Excepti } @Test - public void testInnerJoinCastLeft() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinCastLeft(Map queryContext) throws Exception { // foo.m1 is FLOAT, l.k is STRING. @@ -9263,6 +9313,7 @@ public void testInnerJoinCastLeft() throws Exception "SELECT foo.m1, l.k, l.v\n" + "FROM foo\n" + "INNER JOIN lookup.lookyloo l ON CAST(foo.m1 AS VARCHAR) = l.k\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -9279,7 +9330,7 @@ public void testInnerJoinCastLeft() throws Exception ) .intervals(querySegmentSpec(Filtration.eternity())) .columns("j0.k", "j0.v", "m1") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of() @@ -9287,7 +9338,8 @@ public void testInnerJoinCastLeft() throws Exception } @Test - public void testInnerJoinCastRight() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinCastRight(Map queryContext) throws Exception { // foo.m1 is FLOAT, l.k is STRING. @@ -9295,6 +9347,7 @@ public void testInnerJoinCastRight() throws Exception "SELECT foo.m1, l.k, l.v\n" + "FROM foo\n" + "INNER JOIN lookup.lookyloo l ON foo.m1 = CAST(l.k AS FLOAT)\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -9309,7 +9362,7 @@ public void testInnerJoinCastRight() throws Exception ) .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) .columns("k", "v", "v0") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), "j0.", @@ -9319,7 +9372,7 @@ public void testInnerJoinCastRight() throws Exception ) .intervals(querySegmentSpec(Filtration.eternity())) .columns("j0.k", "j0.v", "m1") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -9329,7 +9382,8 @@ public void testInnerJoinCastRight() throws Exception } @Test - public void testInnerJoinMismatchedTypes() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinMismatchedTypes(Map queryContext) throws Exception { // foo.m1 is FLOAT, l.k is STRING. Comparing them generates a CAST. @@ -9337,6 +9391,7 @@ public void testInnerJoinMismatchedTypes() throws Exception "SELECT foo.m1, l.k, l.v\n" + "FROM foo\n" + "INNER JOIN lookup.lookyloo l ON foo.m1 = l.k\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -9351,7 +9406,7 @@ public void testInnerJoinMismatchedTypes() throws Exception ) .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) .columns("k", "v", "v0") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), "j0.", @@ -9361,7 +9416,7 @@ public void testInnerJoinMismatchedTypes() throws Exception ) .intervals(querySegmentSpec(Filtration.eternity())) .columns("j0.k", "j0.v", "m1") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -9371,12 +9426,14 @@ public void testInnerJoinMismatchedTypes() throws Exception } @Test - public void testInnerJoinLeftFunction() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinLeftFunction(Map queryContext) throws Exception { testQuery( "SELECT foo.dim1, foo.dim2, l.k, l.v\n" + "FROM foo\n" + "INNER JOIN lookup.lookyloo l ON SUBSTRING(foo.dim2, 1, 1) = l.k\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -9393,7 +9450,7 @@ public void testInnerJoinLeftFunction() throws Exception ) .intervals(querySegmentSpec(Filtration.eternity())) .columns("dim1", "dim2", "j0.k", "j0.v") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -9405,12 +9462,14 @@ public void testInnerJoinLeftFunction() throws Exception } @Test - public void testInnerJoinRightFunction() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinRightFunction(Map queryContext) throws Exception { testQuery( "SELECT foo.dim1, foo.dim2, l.k, l.v\n" + "FROM foo\n" + "INNER JOIN lookup.lookyloo l ON foo.dim2 = SUBSTRING(l.k, 1, 2)\n", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -9425,7 +9484,7 @@ public void testInnerJoinRightFunction() throws Exception ) .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) .columns("k", "v", "v0") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), "j0.", @@ -9435,7 +9494,7 @@ public void testInnerJoinRightFunction() throws Exception ) .intervals(querySegmentSpec(Filtration.eternity())) .columns("dim1", "dim2", "j0.k", "j0.v") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -9446,13 +9505,15 @@ public void testInnerJoinRightFunction() throws Exception } @Test - public void testLeftJoinLookupOntoLookupUsingJoinOperator() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testLeftJoinLookupOntoLookupUsingJoinOperator(Map queryContext) throws Exception { testQuery( "SELECT dim2, l1.v, l2.v\n" + "FROM foo\n" + "LEFT JOIN lookup.lookyloo l1 ON foo.dim2 = l1.k\n" + "LEFT JOIN lookup.lookyloo l2 ON l1.k = l2.k", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -9472,7 +9533,7 @@ public void testLeftJoinLookupOntoLookupUsingJoinOperator() throws Exception ) .intervals(querySegmentSpec(Filtration.eternity())) .columns("_j0.v", "dim2", "j0.v") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -9487,7 +9548,8 @@ public void testLeftJoinLookupOntoLookupUsingJoinOperator() throws Exception } @Test - public void testLeftJoinThreeLookupsUsingJoinOperator() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testLeftJoinThreeLookupsUsingJoinOperator(Map queryContext) throws Exception { testQuery( "SELECT dim1, dim2, l1.v, l2.v, l3.v\n" @@ -9495,6 +9557,7 @@ public void testLeftJoinThreeLookupsUsingJoinOperator() throws Exception + "LEFT JOIN lookup.lookyloo l1 ON foo.dim1 = l1.k\n" + "LEFT JOIN lookup.lookyloo l2 ON foo.dim2 = l2.k\n" + "LEFT JOIN lookup.lookyloo l3 ON l2.k = l3.k", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -9520,7 +9583,7 @@ public void testLeftJoinThreeLookupsUsingJoinOperator() throws Exception ) .intervals(querySegmentSpec(Filtration.eternity())) .columns("__j0.v", "_j0.v", "dim1", "dim2", "j0.v") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -9535,12 +9598,14 @@ public void testLeftJoinThreeLookupsUsingJoinOperator() throws Exception } @Test - public void testSelectOnLookupUsingLeftJoinOperator() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testSelectOnLookupUsingLeftJoinOperator(Map queryContext) throws Exception { testQuery( "SELECT dim1, lookyloo.*\n" + "FROM foo LEFT JOIN lookup.lookyloo ON foo.dim1 = lookyloo.k\n" + "WHERE lookyloo.v <> 'xxx' OR lookyloo.v IS NULL", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -9555,7 +9620,7 @@ public void testSelectOnLookupUsingLeftJoinOperator() throws Exception .intervals(querySegmentSpec(Filtration.eternity())) .filters(or(not(selector("j0.v", "xxx", null)), selector("j0.v", null, null))) .columns("dim1", "j0.k", "j0.v") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -9570,12 +9635,14 @@ public void testSelectOnLookupUsingLeftJoinOperator() throws Exception } @Test - public void testSelectOnLookupUsingRightJoinOperator() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testSelectOnLookupUsingRightJoinOperator(Map queryContext) throws Exception { testQuery( "SELECT dim1, lookyloo.*\n" + "FROM foo RIGHT JOIN lookup.lookyloo ON foo.dim1 = lookyloo.k\n" + "WHERE lookyloo.v <> 'xxx' OR lookyloo.v IS NULL", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -9590,7 +9657,7 @@ public void testSelectOnLookupUsingRightJoinOperator() throws Exception .intervals(querySegmentSpec(Filtration.eternity())) .filters(or(not(selector("j0.v", "xxx", null)), selector("j0.v", null, null))) .columns("dim1", "j0.k", "j0.v") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -9603,12 +9670,14 @@ public void testSelectOnLookupUsingRightJoinOperator() throws Exception } @Test - public void testSelectOnLookupUsingFullJoinOperator() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testSelectOnLookupUsingFullJoinOperator(Map queryContext) throws Exception { testQuery( "SELECT dim1, m1, cnt, lookyloo.*\n" + "FROM foo FULL JOIN lookup.lookyloo ON foo.dim1 = lookyloo.k\n" + "WHERE lookyloo.v <> 'xxx' OR lookyloo.v IS NULL", + queryContext, ImmutableList.of( newScanQueryBuilder() .dataSource( @@ -9623,7 +9692,7 @@ public void testSelectOnLookupUsingFullJoinOperator() throws Exception .intervals(querySegmentSpec(Filtration.eternity())) .filters(or(not(selector("j0.v", "xxx", null)), selector("j0.v", null, null))) .columns("cnt", "dim1", "j0.k", "j0.v", "m1") - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), ImmutableList.of( @@ -9681,7 +9750,8 @@ public void testCountDistinctOfLookup() throws Exception } @Test - public void testCountDistinctOfLookupUsingJoinOperator() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testCountDistinctOfLookupUsingJoinOperator(Map queryContext) throws Exception { // Cannot yet vectorize the JOIN operator. cannotVectorize(); @@ -9689,6 +9759,7 @@ public void testCountDistinctOfLookupUsingJoinOperator() throws Exception testQuery( "SELECT COUNT(DISTINCT lookyloo.v)\n" + "FROM foo LEFT JOIN lookup.lookyloo ON foo.dim1 = lookyloo.k", + queryContext, ImmutableList.of( Druids.newTimeseriesQueryBuilder() .dataSource( @@ -11206,7 +11277,8 @@ public void testGroupingSetsWithOrderByAggregatorWithLimit() throws Exception } @Test - public void testUsingSubqueryAsPartOfAndFilter() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testUsingSubqueryAsPartOfAndFilter(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); @@ -11216,6 +11288,7 @@ public void testUsingSubqueryAsPartOfAndFilter() throws Exception + "WHERE dim2 IN (SELECT dim1 FROM druid.foo WHERE dim1 <> '')\n" + "AND dim1 <> 'xxx'\n" + "group by dim1, dim2 ORDER BY dim2", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -11255,7 +11328,7 @@ public void testUsingSubqueryAsPartOfAndFilter() throws Exception Integer.MAX_VALUE ) ) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -11265,7 +11338,8 @@ public void testUsingSubqueryAsPartOfAndFilter() throws Exception } @Test - public void testUsingSubqueryAsPartOfOrFilter() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testUsingSubqueryAsPartOfOrFilter(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); @@ -11274,6 +11348,7 @@ public void testUsingSubqueryAsPartOfOrFilter() throws Exception "SELECT dim1, dim2, COUNT(*) FROM druid.foo\n" + "WHERE dim1 = 'xxx' OR dim2 IN (SELECT dim1 FROM druid.foo WHERE dim1 LIKE '%bc')\n" + "group by dim1, dim2 ORDER BY dim2", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -11307,7 +11382,7 @@ public void testUsingSubqueryAsPartOfOrFilter() throws Exception new DefaultDimensionSpec("v0", "d1", ValueType.LONG) ) ) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), "_j0.", @@ -11343,7 +11418,7 @@ public void testUsingSubqueryAsPartOfOrFilter() throws Exception Integer.MAX_VALUE ) ) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -11369,7 +11444,8 @@ public void testTimeExtractWithTooFewArguments() throws Exception } @Test - public void testNestedGroupByOnInlineDataSourceWithFilterIsNotSupported() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testNestedGroupByOnInlineDataSourceWithFilterIsNotSupported(Map queryContext) throws Exception { try { testQuery( @@ -11385,6 +11461,7 @@ public void testNestedGroupByOnInlineDataSourceWithFilterIsNotSupported() throws + " group by 1" + ")" + "SELECT count(*) from def", + queryContext, ImmutableList.of( GroupByQuery .builder() @@ -11399,7 +11476,7 @@ public void testNestedGroupByOnInlineDataSourceWithFilterIsNotSupported() throws .intervals(querySegmentSpec(Intervals.of("2001-01-02T00:00:00.000Z/146140482-04-24T15:36:27.903Z"))) .columns("dim1", "m2") .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), new QueryDataSource( @@ -11408,7 +11485,7 @@ public void testNestedGroupByOnInlineDataSourceWithFilterIsNotSupported() throws .intervals(querySegmentSpec(Intervals.of("2001-01-02T00:00:00.000Z/146140482-04-24T15:36:27.903Z"))) .columns("dim1", "m2") .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) - .context(QUERY_CONTEXT_DEFAULT) + .context(queryContext) .build() ), "j0", @@ -11680,13 +11757,15 @@ public void testSemiJoinWithOuterTimeExtractAggregateWithOrderBy() throws Except } @Test - public void testInAggregationSubquery() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testInAggregationSubquery(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); testQuery( "SELECT DISTINCT __time FROM druid.foo WHERE __time IN (SELECT MAX(__time) FROM druid.foo)", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -11712,7 +11791,7 @@ public void testInAggregationSubquery() throws Exception .setInterval(querySegmentSpec(Filtration.eternity())) .setGranularity(Granularities.ALL) .setDimensions(dimensions(new DefaultDimensionSpec("__time", "d0", ValueType.LONG))) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -11722,13 +11801,15 @@ public void testInAggregationSubquery() throws Exception } @Test - public void testNotInAggregationSubquery() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testNotInAggregationSubquery(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); testQuery( "SELECT DISTINCT __time FROM druid.foo WHERE __time NOT IN (SELECT MAX(__time) FROM druid.foo)", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -11759,7 +11840,7 @@ public void testNotInAggregationSubquery() throws Exception ) : new CountAggregatorFactory("_a1") ) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), "j0.", @@ -11790,7 +11871,7 @@ public void testNotInAggregationSubquery() throws Exception ) ) .setDimensions(dimensions(new DefaultDimensionSpec("__time", "d0", ValueType.LONG))) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -11804,7 +11885,8 @@ public void testNotInAggregationSubquery() throws Exception } @Test - public void testUsingSubqueryWithExtractionFns() throws Exception + @Parameters(source = QueryContextForJoinProvider.class) + public void testUsingSubqueryWithExtractionFns(Map queryContext) throws Exception { // Cannot vectorize JOIN operator. cannotVectorize(); @@ -11813,6 +11895,7 @@ public void testUsingSubqueryWithExtractionFns() throws Exception "SELECT dim2, COUNT(*) FROM druid.foo " + "WHERE substring(dim2, 1, 1) IN (SELECT substring(dim1, 1, 1) FROM druid.foo WHERE dim1 <> '')" + "group by dim2", + queryContext, ImmutableList.of( GroupByQuery.builder() .setDataSource( @@ -11849,7 +11932,7 @@ public void testUsingSubqueryWithExtractionFns() throws Exception .setGranularity(Granularities.ALL) .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0"))) .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) - .setContext(QUERY_CONTEXT_DEFAULT) + .setContext(queryContext) .build() ), ImmutableList.of( @@ -11859,6 +11942,79 @@ public void testUsingSubqueryWithExtractionFns() throws Exception ); } + @Test + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinWithIsNullFilter(Map queryContext) throws Exception + { + testQuery( + "SELECT dim1, l.v from druid.foo f inner join lookup.lookyloo l on f.dim1 = l.k where f.dim2 is null", + queryContext, + ImmutableList.of( + newScanQueryBuilder() + .dataSource( + join( + new TableDataSource(CalciteTests.DATASOURCE1), + new LookupDataSource("lookyloo"), + "j0.", + equalsCondition( + DruidExpression.fromColumn("dim1"), + DruidExpression.fromColumn("j0.k") + ), + JoinType.INNER + ) + ) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(selector("dim2", null, null)) + .columns("dim1", "j0.v") + .build() + ), + ImmutableList.of( + new Object[]{"abc", "xabc"} + ) + ); + } + + @Test + @Parameters(source = QueryContextForJoinProvider.class) + @Ignore // regression test for https://github.com/apache/druid/issues/9924 + public void testInnerJoinOnMultiValueColumn(Map queryContext) throws Exception + { + cannotVectorize(); + testQuery( + "SELECT dim3, l.v, count(*) from druid.foo f inner join lookup.lookyloo l on f.dim3 = l.k " + + "group by 1, 2", + queryContext, + ImmutableList.of( + GroupByQuery.builder() + .setDataSource( + join( + new TableDataSource(CalciteTests.DATASOURCE1), + new LookupDataSource("lookyloo"), + "j0.", + equalsCondition( + DruidExpression.fromColumn("dim3"), + DruidExpression.fromColumn("j0.k") + ), + JoinType.INNER + ) + ) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setGranularity(Granularities.ALL) + .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) + .setDimensions( + dimensions( + new DefaultDimensionSpec("dim3", "d0"), + new DefaultDimensionSpec("j0.v", "d1") + ) + ) + .build() + ), + ImmutableList.of( + new Object[]{"2", "x2", 1L} + ) + ); + } + @Test public void testUsingSubqueryWithLimit() throws Exception { @@ -13917,4 +14073,39 @@ public void testRepeatedIdenticalVirtualExpressionGrouping() throws Exception ) ); } + + /** + * This is a provider of query contexts that should be used by join tests. + * It tests various configs that can be passed to join queries. All the configs provided by this provider should + * have the join query engine return the same results. + */ + public static class QueryContextForJoinProvider + { + @UsedByJUnitParamsRunner + public static Object[] provideQueryContexts() + { + return new Object[] { + // default behavior + QUERY_CONTEXT_DEFAULT, + // filter value re-writes enabled + new ImmutableMap.Builder() + .putAll(QUERY_CONTEXT_DEFAULT) + .put(QueryContexts.JOIN_FILTER_REWRITE_VALUE_COLUMN_FILTERS_ENABLE_KEY, true) + .put(QueryContexts.JOIN_FILTER_REWRITE_ENABLE_KEY, true) + .build(), + // rewrite values enabled but filter re-writes disabled. + // This should be drive the same behavior as the previous config + new ImmutableMap.Builder() + .putAll(QUERY_CONTEXT_DEFAULT) + .put(QueryContexts.JOIN_FILTER_REWRITE_VALUE_COLUMN_FILTERS_ENABLE_KEY, true) + .put(QueryContexts.JOIN_FILTER_REWRITE_ENABLE_KEY, false) + .build(), + // filter re-writes disabled + new ImmutableMap.Builder() + .putAll(QUERY_CONTEXT_DEFAULT) + .put(QueryContexts.JOIN_FILTER_REWRITE_ENABLE_KEY, false) + .build(), + }; + } + } } From 84c414a7d95046d64d9ee34038ec1b3bbda5d3c3 Mon Sep 17 00:00:00 2001 From: Jonathan Wei Date: Thu, 28 May 2020 22:26:32 -0700 Subject: [PATCH 038/107] Fix type restriction for Pattern hashcode inspection (#9947) --- .idea/inspectionProfiles/Druid.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.idea/inspectionProfiles/Druid.xml b/.idea/inspectionProfiles/Druid.xml index cd32fb67f732..cf4e53758479 100644 --- a/.idea/inspectionProfiles/Druid.xml +++ b/.idea/inspectionProfiles/Druid.xml @@ -387,8 +387,8 @@ - - + + From f199ddb93cbaa3033f2c7e012073ccb73071573c Mon Sep 17 00:00:00 2001 From: Suneet Saldanha <44787917+suneet-s@users.noreply.github.com> Date: Thu, 28 May 2020 22:32:09 -0700 Subject: [PATCH 039/107] Refactor JoinFilterAnalyzer (#9921) * Refactor JoinFilterAnalyzer This patch attempts to make it easier to follow the join filter analysis code with the hope of making it easier to add rewrite optimizations in the future. To keep the patch small and easy to review, this is the first of at least 2 patches that are planned. This patch adds a builder to the Pre-Analysis, so that it is easier to instantiate the preAnalysis. It also moves some of the filter normalization code out to Fitlers with associated tests. * fix tests --- .../apache/druid/segment/filter/Filters.java | 22 +++ .../join/filter/JoinFilterAnalyzer.java | 178 +++++++----------- .../join/filter/JoinFilterPreAnalysis.java | 108 ++++++++++- .../filter/FilterCnfConversionTest.java | 163 ++++++++++++++++ 4 files changed, 362 insertions(+), 109 deletions(-) diff --git a/processing/src/main/java/org/apache/druid/segment/filter/Filters.java b/processing/src/main/java/org/apache/druid/segment/filter/Filters.java index a1f536d22b3b..69775a318212 100644 --- a/processing/src/main/java/org/apache/druid/segment/filter/Filters.java +++ b/processing/src/main/java/org/apache/druid/segment/filter/Filters.java @@ -49,6 +49,7 @@ import javax.annotation.Nullable; import java.io.IOException; import java.io.UncheckedIOException; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; @@ -521,4 +522,25 @@ public static Filter or(Set filterSet) return new OrFilter(filterSet); } + + /** + * @param filter the filter. + * @return The normalized or clauses for the provided filter. + */ + public static Set toNormalizedOrClauses(Filter filter) + { + Filter normalizedFilter = Filters.toCnf(filter); + + // List of candidates for pushdown + // CNF normalization will generate either + // - an AND filter with multiple subfilters + // - or a single non-AND subfilter which cannot be split further + Set normalizedOrClauses; + if (normalizedFilter instanceof AndFilter) { + normalizedOrClauses = ((AndFilter) normalizedFilter).getFilters(); + } else { + normalizedOrClauses = Collections.singleton(normalizedFilter); + } + return normalizedOrClauses; + } } diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java index 77f4c9d987f1..6d1da8f20f98 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java @@ -30,7 +30,6 @@ import org.apache.druid.segment.VirtualColumn; import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.column.ValueType; -import org.apache.druid.segment.filter.AndFilter; import org.apache.druid.segment.filter.Filters; import org.apache.druid.segment.filter.OrFilter; import org.apache.druid.segment.filter.SelectorFilter; @@ -42,7 +41,6 @@ import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -76,7 +74,7 @@ * * The result of this pre-analysis method should be passed into the next step of join filter analysis, described below. * - * The {@link #splitFilter(JoinFilterPreAnalysis)} method takes the pre-analysis result and optionally applies the\ + * The {@link #splitFilter(JoinFilterPreAnalysis)} method takes the pre-analysis result and optionally applies the * filter rewrite and push down operations on a per-segment level. */ public class JoinFilterAnalyzer @@ -118,34 +116,15 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( final List postJoinVirtualColumns = new ArrayList<>(); splitVirtualColumns(joinableClauses, virtualColumns, preJoinVirtualColumns, postJoinVirtualColumns); - + JoinFilterPreAnalysis.Builder preAnalysisBuilder = + new JoinFilterPreAnalysis.Builder(joinableClauses, originalFilter, postJoinVirtualColumns) + .withEnableFilterPushDown(enableFilterPushDown) + .withEnableFilterRewrite(enableFilterRewrite); if (originalFilter == null || !enableFilterPushDown) { - return new JoinFilterPreAnalysis( - joinableClauses, - originalFilter, - postJoinVirtualColumns, - null, - null, - null, - null, - enableFilterPushDown, - enableFilterRewrite, - Collections.emptyMap() - ); + return preAnalysisBuilder.build(); } - Filter normalizedFilter = Filters.toCnf(originalFilter); - - // List of candidates for pushdown - // CNF normalization will generate either - // - an AND filter with multiple subfilters - // - or a single non-AND subfilter which cannot be split further - Set normalizedOrClauses; - if (normalizedFilter instanceof AndFilter) { - normalizedOrClauses = ((AndFilter) normalizedFilter).getFilters(); - } else { - normalizedOrClauses = Collections.singleton(normalizedFilter); - } + Set normalizedOrClauses = Filters.toNormalizedOrClauses(originalFilter); List normalizedBaseTableClauses = new ArrayList<>(); List normalizedJoinTableClauses = new ArrayList<>(); @@ -161,68 +140,17 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( normalizedBaseTableClauses.add(orClause); } } - + preAnalysisBuilder + .withNormalizedBaseTableClauses(normalizedBaseTableClauses) + .withNormalizedJoinTableClauses(normalizedJoinTableClauses); if (!enableFilterRewrite) { - return new JoinFilterPreAnalysis( - joinableClauses, - originalFilter, - postJoinVirtualColumns, - normalizedBaseTableClauses, - normalizedJoinTableClauses, - null, - null, - enableFilterPushDown, - enableFilterRewrite, - Collections.emptyMap() - ); + return preAnalysisBuilder.build(); } // build the equicondition map, used for determining how the tables are connected through joins - Map> equiconditions = new HashMap<>(); - for (JoinableClause clause : joinableClauses) { - for (Equality equality : clause.getCondition().getEquiConditions()) { - Set exprsForRhs = equiconditions.computeIfAbsent( - clause.getPrefix() + equality.getRightColumn(), - (rhs) -> new HashSet<>() - ); - exprsForRhs.add(equality.getLeftExpr()); - } - } - - // Determine candidates for filter rewrites. - // A candidate is an RHS column that appears in a filter, along with the value being filtered on, plus - // the joinable clause associated with the table that the RHS column is from. - Set rhsRewriteCandidates = new LinkedHashSet<>(); - for (Filter orClause : normalizedJoinTableClauses) { - if (filterMatchesNull(orClause)) { - continue; - } - - if (orClause instanceof OrFilter) { - for (Filter subFilter : ((OrFilter) orClause).getFilters()) { - Optional rhsRewriteCandidate = determineRhsRewriteCandidatesForSingleFilter( - subFilter, - equiconditions, - joinableClauses - ); + Map> equiconditions = preAnalysisBuilder.computeEquiconditionsFromJoinableClauses(); - if (rhsRewriteCandidate.isPresent()) { - rhsRewriteCandidates.add(rhsRewriteCandidate.get()); - } - } - continue; - } - - Optional rhsRewriteCandidate = determineRhsRewriteCandidatesForSingleFilter( - orClause, - equiconditions, - joinableClauses - ); - - if (rhsRewriteCandidate.isPresent()) { - rhsRewriteCandidates.add(rhsRewriteCandidate.get()); - } - } + Set rhsRewriteCandidates = getRhsRewriteCandidates(normalizedJoinTableClauses, equiconditions, joinableClauses); // Build a map of RHS table prefix -> JoinFilterColumnCorrelationAnalysis based on the RHS rewrite candidates Map>> correlationsByPrefix = new HashMap<>(); @@ -248,7 +176,6 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( assert (baseColumnAnalysis != null); return Optional.of(correlatedBaseTableColumns.get().get(c)); } - } ); } else { @@ -280,9 +207,7 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( List perColumnCorrelations = correlationsByDirectFilteringColumn.computeIfAbsent( rhsRewriteCandidate.getRhsColumn(), - (rhsCol) -> { - return new ArrayList<>(); - } + (rhsCol) -> new ArrayList<>() ); perColumnCorrelations.add( directRewriteCorrelations.get(rhsRewriteCandidate.getRhsColumn()).get() @@ -299,9 +224,7 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( List perColumnCorrelations = correlationsByFilteringColumn.computeIfAbsent( rhsRewriteCandidate.getRhsColumn(), - (rhsCol) -> { - return new ArrayList<>(); - } + (rhsCol) -> new ArrayList<>() ); perColumnCorrelations.add(correlationForPrefix.getValue()); correlationForPrefix.getValue().getCorrelatedValuesMap().computeIfAbsent( @@ -348,20 +271,10 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( correlationsByDirectFilteringColumn.put(correlation.getKey(), dedupList); } } + preAnalysisBuilder.withCorrelationsByFilteringColumn(correlationsByFilteringColumn) + .withCorrelationsByDirectFilteringColumn(correlationsByDirectFilteringColumn); - - return new JoinFilterPreAnalysis( - joinableClauses, - originalFilter, - postJoinVirtualColumns, - normalizedBaseTableClauses, - normalizedJoinTableClauses, - correlationsByFilteringColumn, - correlationsByDirectFilteringColumn, - enableFilterPushDown, - enableFilterRewrite, - equiconditions - ); + return preAnalysisBuilder.build(); } private static Optional determineRhsRewriteCandidatesForSingleFilter( @@ -414,9 +327,7 @@ private static boolean doesRequiredColumnSetSupportDirectJoinFilterRewrite( { if (requiredColumns.size() == 1) { String reqColumn = requiredColumns.iterator().next(); - if (equiconditions.containsKey(reqColumn)) { - return true; - } + return equiconditions.containsKey(reqColumn); } return false; } @@ -1052,6 +963,57 @@ private static void splitVirtualColumns( } } + /** + * Determine candidates for filter rewrites. + * A candidate is an RHS column that appears in a filter, along with the value being filtered on, plus + * the joinable clause associated with the table that the RHS column is from. + * + * These candidates are redued to filter rewrite correlations. + * + * @param normalizedJoinTableClauses + * @param equiconditions + * @param joinableClauses + * @return A set of candidates for filter rewrites. + */ + private static Set getRhsRewriteCandidates( + List normalizedJoinTableClauses, + Map> equiconditions, + List joinableClauses) + { + Set rhsRewriteCandidates = new LinkedHashSet<>(); + for (Filter orClause : normalizedJoinTableClauses) { + if (filterMatchesNull(orClause)) { + continue; + } + + if (orClause instanceof OrFilter) { + for (Filter subFilter : ((OrFilter) orClause).getFilters()) { + Optional rhsRewriteCandidate = determineRhsRewriteCandidatesForSingleFilter( + subFilter, + equiconditions, + joinableClauses + ); + + rhsRewriteCandidate.ifPresent(rhsRewriteCandidates::add); + } + continue; + } + + Optional rhsRewriteCandidate = determineRhsRewriteCandidatesForSingleFilter( + orClause, + equiconditions, + joinableClauses + ); + + rhsRewriteCandidate.ifPresent(rhsRewriteCandidates::add); + } + return rhsRewriteCandidates; + } + + /** + * A candidate is an RHS column that appears in a filter, along with the value being filtered on, plus + * the joinable clause associated with the table that the RHS column is from. + */ private static class RhsRewriteCandidate { private final boolean isDirectRewrite; diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java index 991147b37f58..3ed12fae1c7b 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java @@ -22,8 +22,14 @@ import org.apache.druid.math.expr.Expr; import org.apache.druid.query.filter.Filter; import org.apache.druid.segment.VirtualColumn; +import org.apache.druid.segment.join.Equality; import org.apache.druid.segment.join.JoinableClause; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -53,7 +59,7 @@ public class JoinFilterPreAnalysis private final List postJoinVirtualColumns; private final Map> equiconditions; - public JoinFilterPreAnalysis( + private JoinFilterPreAnalysis( final List joinableClauses, final Filter originalFilter, final List postJoinVirtualColumns, @@ -127,5 +133,105 @@ public Map> getEquiconditions() { return equiconditions; } + + /** + * A Builder class to build {@link JoinFilterPreAnalysis} + */ + public static class Builder + { + @Nonnull private final List joinableClauses; + @Nullable private final Filter originalFilter; + @Nullable private List normalizedBaseTableClauses; + @Nullable private List normalizedJoinTableClauses; + @Nullable private Map> correlationsByFilteringColumn; + @Nullable private Map> correlationsByDirectFilteringColumn; + private boolean enableFilterPushDown = false; + private boolean enableFilterRewrite = false; + @Nonnull private final List postJoinVirtualColumns; + @Nonnull private Map> equiconditions = Collections.emptyMap(); + + public Builder( + @Nonnull List joinableClauses, + @Nullable Filter originalFilter, + @Nonnull List postJoinVirtualColumns + ) + { + this.joinableClauses = joinableClauses; + this.originalFilter = originalFilter; + this.postJoinVirtualColumns = postJoinVirtualColumns; + } + + public Builder withNormalizedBaseTableClauses(List normalizedBaseTableClauses) + { + this.normalizedBaseTableClauses = normalizedBaseTableClauses; + return this; + } + + public Builder withNormalizedJoinTableClauses(List normalizedJoinTableClauses) + { + this.normalizedJoinTableClauses = normalizedJoinTableClauses; + return this; + } + + public Builder withCorrelationsByFilteringColumn( + Map> correlationsByFilteringColumn + ) + { + this.correlationsByFilteringColumn = correlationsByFilteringColumn; + return this; + } + + public Builder withCorrelationsByDirectFilteringColumn( + Map> correlationsByDirectFilteringColumn + ) + { + this.correlationsByDirectFilteringColumn = correlationsByDirectFilteringColumn; + return this; + } + + public Builder withEnableFilterPushDown(boolean enableFilterPushDown) + { + this.enableFilterPushDown = enableFilterPushDown; + return this; + } + + public Builder withEnableFilterRewrite(boolean enableFilterRewrite) + { + this.enableFilterRewrite = enableFilterRewrite; + return this; + } + + public Map> computeEquiconditionsFromJoinableClauses() + { + this.equiconditions = new HashMap<>(); + for (JoinableClause clause : joinableClauses) { + for (Equality equality : clause.getCondition().getEquiConditions()) { + Set exprsForRhs = equiconditions.computeIfAbsent( + clause.getPrefix() + equality.getRightColumn(), + (rhs) -> new HashSet<>() + ); + exprsForRhs.add(equality.getLeftExpr()); + } + } + return equiconditions; + } + + public JoinFilterPreAnalysis build() + { + return new JoinFilterPreAnalysis( + joinableClauses, + originalFilter, + postJoinVirtualColumns, + normalizedBaseTableClauses, + normalizedJoinTableClauses, + correlationsByFilteringColumn, + correlationsByDirectFilteringColumn, + enableFilterPushDown, + enableFilterRewrite, + equiconditions + ); + } + + } } diff --git a/processing/src/test/java/org/apache/druid/segment/filter/FilterCnfConversionTest.java b/processing/src/test/java/org/apache/druid/segment/filter/FilterCnfConversionTest.java index a1692854328e..c12c012b5e05 100644 --- a/processing/src/test/java/org/apache/druid/segment/filter/FilterCnfConversionTest.java +++ b/processing/src/test/java/org/apache/druid/segment/filter/FilterCnfConversionTest.java @@ -20,6 +20,7 @@ package org.apache.druid.segment.filter; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.query.dimension.DimensionSpec; @@ -35,6 +36,7 @@ import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -155,6 +157,51 @@ public void testToCnfWithMuchReducibleFilter() assertFilter(muchReducible, expected, cnf); } + @Test + public void testToNormalizedOrClausesWithMuchReducibleFilter() + { + final Filter muchReducible = FilterTestUtils.and( + // should be flattened + FilterTestUtils.and( + FilterTestUtils.and( + FilterTestUtils.and(FilterTestUtils.selector("col1", "val1")) + ) + ), + // should be flattened + FilterTestUtils.and( + FilterTestUtils.or( + FilterTestUtils.and(FilterTestUtils.selector("col1", "val1")) + ) + ), + // should be flattened + FilterTestUtils.or( + FilterTestUtils.and( + FilterTestUtils.or(FilterTestUtils.selector("col1", "val1")) + ) + ), + // should eliminate duplicate filters + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.selector("col2", "val2"), + FilterTestUtils.and( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.selector("col2", "val2") + ), + FilterTestUtils.and( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.and( + FilterTestUtils.selector("col2", "val2"), + FilterTestUtils.selector("col1", "val1") + ) + ) + ); + final Set expected = ImmutableSet.of( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.selector("col2", "val2") + ); + final Set normalizedOrClauses = Filters.toNormalizedOrClauses(muchReducible); + Assert.assertEquals(expected, normalizedOrClauses); + } + @Test public void testToCnfWithComplexFilterIncludingNotAndOr() { @@ -259,6 +306,110 @@ public void testToCnfWithComplexFilterIncludingNotAndOr() assertFilter(filter, expected, cnf); } + @Test + public void testToNormalizedOrClausesWithComplexFilterIncludingNotAndOr() + { + final Filter filter = FilterTestUtils.and( + FilterTestUtils.or( + FilterTestUtils.and( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.selector("col2", "val2") + ), + FilterTestUtils.not( + FilterTestUtils.and( + FilterTestUtils.selector("col4", "val4"), + FilterTestUtils.selector("col5", "val5") + ) + ) + ), + FilterTestUtils.or( + FilterTestUtils.not( + FilterTestUtils.or( + FilterTestUtils.selector("col2", "val2"), + FilterTestUtils.selector("col4", "val4"), + FilterTestUtils.selector("col5", "val5") + ) + ), + FilterTestUtils.and( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.selector("col3", "val3") + ) + ), + FilterTestUtils.and( + FilterTestUtils.or( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.selector("col2", "val22"), // selecting different value + FilterTestUtils.selector("col3", "val3") + ), + FilterTestUtils.not( + FilterTestUtils.selector("col1", "val11") + ) + ), + FilterTestUtils.and( + FilterTestUtils.or( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.selector("col2", "val22"), + FilterTestUtils.selector("col3", "val3") + ), + FilterTestUtils.not( + FilterTestUtils.selector("col1", "val11") // selecting different value + ) + ) + ); + final Set expected = ImmutableSet.of( + FilterTestUtils.or( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.selector("col2", "val22"), + FilterTestUtils.selector("col3", "val3") + ), + FilterTestUtils.or( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.not(FilterTestUtils.selector("col2", "val2")) + ), + FilterTestUtils.or( + FilterTestUtils.not(FilterTestUtils.selector("col2", "val2")), + FilterTestUtils.selector("col3", "val3") + ), + FilterTestUtils.or( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.not(FilterTestUtils.selector("col4", "val4")) + ), + FilterTestUtils.or( + FilterTestUtils.selector("col3", "val3"), + FilterTestUtils.not(FilterTestUtils.selector("col4", "val4")) + ), + FilterTestUtils.or( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.not(FilterTestUtils.selector("col5", "val5")) + ), + FilterTestUtils.or( + FilterTestUtils.selector("col3", "val3"), + FilterTestUtils.not(FilterTestUtils.selector("col5", "val5")) + ), + FilterTestUtils.not(FilterTestUtils.selector("col1", "val11")), + // The below OR filter could be eliminated because this filter also has + // (col1 = val1 || ~(col4 = val4)) && (col1 = val1 || ~(col5 = val5)). + // The reduction process would be + // (col1 = val1 || ~(col4 = val4)) && (col1 = val1 || ~(col5 = val5)) && (col1 = val1 || ~(col4 = val4) || ~(col5 = val5)) + // => (col1 = val1 && ~(col4 = val4) || ~(col5 = val5)) && (col1 = val1 || ~(col4 = val4) || ~(col5 = val5)) + // => (col1 = val1 && ~(col4 = val4) || ~(col5 = val5)) + // => (col1 = val1 || ~(col4 = val4)) && (col1 = val1 || ~(col5 = val5)). + // However, we don't have this reduction now, so we have a filter in a suboptimized CNF. + FilterTestUtils.or( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.not(FilterTestUtils.selector("col4", "val4")), + FilterTestUtils.not(FilterTestUtils.selector("col5", "val5")) + ), + FilterTestUtils.or( + FilterTestUtils.selector("col2", "val2"), + FilterTestUtils.not(FilterTestUtils.selector("col4", "val4")), + FilterTestUtils.not(FilterTestUtils.selector("col5", "val5")) + ) + ); + final Set normalizedOrClauses = Filters.toNormalizedOrClauses(filter); + Assert.assertEquals(expected, normalizedOrClauses); + } + @Test public void testToCnfCollapsibleBigFilter() { @@ -355,6 +506,18 @@ public void testToCnfFilterThatPullCannotConvertToCnfProperly() assertFilter(filter, expectedCnf, Filters.toCnf(filter)); } + @Test + public void testToNormalizedOrClausesNonAndFilterShouldReturnSingleton() + { + Filter filter = FilterTestUtils.or( + FilterTestUtils.selector("col1", "val1"), + FilterTestUtils.selector("col2", "val2") + ); + Set expected = Collections.singleton(filter); + Set normalizedOrClauses = Filters.toNormalizedOrClauses(filter); + Assert.assertEquals(expected, normalizedOrClauses); + } + @Test public void testTrueFalseFilterRequiredColumnRewrite() { From 3ec1f34e98e36324fc7ee3a31ff21a96c0e2a4b5 Mon Sep 17 00:00:00 2001 From: Surekha Date: Fri, 29 May 2020 10:10:28 -0700 Subject: [PATCH 040/107] Modify information schema doc to specify correct value of TABLE_CATALOG (#9950) --- docs/querying/sql.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/querying/sql.md b/docs/querying/sql.md index 80f8a399bf33..9a49009a9bcd 100644 --- a/docs/querying/sql.md +++ b/docs/querying/sql.md @@ -933,7 +933,7 @@ SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'druid' AND TABLE_ |Column|Notes| |------|-----| -|TABLE_CATALOG|Unused| +|TABLE_CATALOG|Always set as 'druid'| |TABLE_SCHEMA|| |TABLE_NAME|| |TABLE_TYPE|"TABLE" or "SYSTEM_TABLE"| @@ -942,7 +942,7 @@ SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'druid' AND TABLE_ |Column|Notes| |------|-----| -|TABLE_CATALOG|Unused| +|TABLE_CATALOG|Always set as 'druid'| |TABLE_SCHEMA|| |TABLE_NAME|| |COLUMN_NAME|| From 3e3555816f946ff49fca59275daebeead69ca603 Mon Sep 17 00:00:00 2001 From: sthetland Date: Fri, 29 May 2020 14:32:21 -0700 Subject: [PATCH 041/107] Querying doc refresh tutorial (#9879) * Update tutorial-query.md * First full pass complete * Smoothing over, a bit * link and spell checking * Update querying.md * Review comments; screenshot fixes * Making ports consistent, pending confirmation Switching to the Router port, to make this be consistent with the tutorial ports, but can switch back here and there if it should be 8082 instead. * Resizing screenshot * Update querying.md * Review feedback incorporated. --- docs/assets/native-queries-01.png | Bin 0 -> 259507 bytes docs/assets/tutorial-query-01.png | Bin 152011 -> 81402 bytes docs/assets/tutorial-query-02.png | Bin 144750 -> 155423 bytes docs/assets/tutorial-query-03.png | Bin 141293 -> 197392 bytes docs/assets/tutorial-query-035.png | Bin 0 -> 256043 bytes docs/assets/tutorial-query-04.png | Bin 66423 -> 250861 bytes docs/assets/tutorial-query-06.png | Bin 82211 -> 208088 bytes docs/assets/tutorial-query-07.png | Bin 78633 -> 260071 bytes docs/assets/tutorial-query-08.png | Bin 0 -> 297253 bytes docs/querying/querying.md | 9 +- docs/tutorials/tutorial-query.md | 311 +++++++++++++---------------- 11 files changed, 147 insertions(+), 173 deletions(-) create mode 100644 docs/assets/native-queries-01.png create mode 100644 docs/assets/tutorial-query-035.png create mode 100644 docs/assets/tutorial-query-08.png diff --git a/docs/assets/native-queries-01.png b/docs/assets/native-queries-01.png new file mode 100644 index 0000000000000000000000000000000000000000..27fd29b632cb17fbebc39817ce5a6dba10eadcb7 GIT binary patch literal 259507 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 VvaDa=?aI2ej@ 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�*eK32 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∨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ⅈ#dtT<)gvJ7E3H_AfW%|~w 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%0DA5d^F(Huc=Nj79fz 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 literal 0 HcmV?d00001 diff --git a/docs/assets/tutorial-query-01.png b/docs/assets/tutorial-query-01.png index 8884d9bc8c47fbc5dc8db1f95b2fe04cf7721a06..371d35dec20000acb62eae5b87da0931ed44ad8b 100644 GIT binary patch literal 81402 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 literal 152011 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

9(i&=NSqjAsP-j!anIaKuZ& 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

0fA(=7G?uPkx|XXZX{Ybl_5RlZwM4hK*fV{q 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�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

5l+((HzQwx5ASbtd9pI| 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%0A;+&+rdl_K49%lzvxEwLUrxC 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 z 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 literal 66423 zcma&O1z6Kj`#+2ws0b3$f`p86q_l$c0NofJN{4`SgQ!S1gV8E5*eK}+m2RaQX&ED= z8TB5X-~a!-bzSfEPA@$h-)-M>&VApXxQ~XsP*=QtlkO%73CV3`rRQ)GlFM@>Bo`O1 zUj(o4+T9ibe_ghGruK}4q&SN7`1KX={DG+wT#bapi-UyZ-A57#SwYo-uEi56vlnL+y96w`Xj}I z_{*};8<($toDTmXZ`x7gzi={d`RQ4Y)0aU0W#=bbR-qUxiv}X zJ5R?y`$zjE9end$1UcdYiP11x{8BM3TfQD)RH93F_swap<+h^p7S>C|-{nucqgw0n zTII}FL7{^049R6Tug#EtTq7gdJk}@(#*&b1-fY#}cx=1zIBz_1jC1INVpJazW{4Pj zF*X^34n3b>F!ubqB5Rm=EDmXX62AO)i%?x1iluWRjW`3JY@`J1%(GcP{2O+{*}Ep= zxL)74M>XOueywe;T>*B5#CGG`$H|h`Lf&rW*uUT^FuGBlJu3a5&n*hsm!9ZrxEnpO zH1fJ-8<*TcEaWkg(caz(RVtv^tKT1!woui&aa6|&H%>s?a1rz;OHr>_vyo~Nf^St4 zeoE)nIb{nPyO@1nm|Gib9xQtwy6ky5i%PRD8a;mWo3FZvq54u&$6v8F%@*gGRW}Ns zfJBBjuhGM_6qa3yA{f7eMV8&8u(c0UL3a@sUL{o8G>)KL??jsWh4R|}W#iVL{G~jd zY@U?xN-hH@=#2WdksXXx5LHC`dGWffBN%d7!1g4B=EWNqz7DW?;iDjrP~2Z4s}=Qc zAFO7)HvogFapyj0;!cqOFOAm4nAmJX)+{=Bh5XdaFV*%J%~}1XwQZ+J5rtnqQ!_Y8?82 z=vph=$QI+*6=|r;-Z#%6Yz|vHv7%{Eum#O2cAeAM>`O0NH!pu-A67+Fv z{Do7ajbCWjH-zyy28M~z^1%EzrX$ov!J1pc-Wh8tx^VNH6Fcw7ipJW4V(+dBDa8!= zf8C@7JM^)y(u^DawtxQ_bXMxoW$Cn$Y*5#eWsIy?KQMv8{EL@KRs>fT#++qt9Y4p< z2*kLqM)HGD+-XU_lvrrMJ|8`LIN{L9#p`G{_B>7UXdxFx&-+MvJ}4k#eUhH7l%4sr z=R@S!WJfywu$fKR{ngrr{eamWGR5Y$zV3h8E*|02{tF+8j=iakE|!~LlF4PY%=`}y zM+#Z~4HqfFkQtfr4U5dBu?d~`qgwx#%A8Roq)IGG&dgLxu;kp=PEKkW{I)Q;-Dp6i z%3+0t@gyA_Zk7M-exGO=;ncNSGktWR%N#+l?RMek)OMIQXRf@}x5zD&5uzhdY&Wuf zy_=kyl)|ZtuC}s0jn`Xdd}y?uou!h;WRcSQEU0uj$(2%-V_n)KtK*K%^p2agoP-HJ zEEM}JB+t#?`J+!y2&r~q+hRN`=3-sCJf^lmiYh+3r`Ms|O2xAOnO%0az(#w#c4c`O zN6s(Wcm-Mxj-)3wG(?9FjM}O_(+Xma(owW+KyB6^$eBuJ1Eg-Ryi%gdgW0Rhn ze;wUUCgLpnrm95`z+yYao$XCHs4%n0edX~M;`mn+*cuhm;#6QXB|Fb=SWRsDuF)rh zf%x5U_w^1v+>Vn{9G^LS?koIH(R3#yAQ6g55GfmgCvhaLwX_a$trBr!H?Fpt|ALQ2 z^6%$RYigFiIX>;ycv$wFCkZX^z8|i;pP=!U3m%S59=y=ty7A*WFBkTnmm-F8s4OzL zjX5&dI9+)BXl=jAbZ0vPdz3?S+`ok&g}_N6S9^^qR5{vL2jAbM&)7_Sj9Hg)EIdim zaN8ItZn!REIKf7`X76Ggsb!Bwm+hHbF$M#@4h|&AM0TD*OD1pJCeJ! z%+G>p$VGb*2BXR}D>mBy5lVy4%OmXj9_sOP{EjC(Yjd=zp{(n(ZJ}i>53tdFLy% zfD{%C>KZWajkCE!iG-Hd6xeS5&eak4XBB?BvwErf_FtN#J5%&@)T$hO2GtF2zTC8b z=}}%Q_}cPzt(y8X!5?N$9?PmKSa*iEl0^;G8aIx9kkIld7a%~KkzQ%2-C|cd%8lokS${XXVUUW4Z_m*qQ@F@<` zZQd(t@Dx%AW54$yO2JlS=kcOxZKGH>pD9iK_Lp+^(L>`xCTTz7NQ!%NYzVPe8bEJOVHj%FGimm72h98dP@cj;@4KmA0~XS^*o zd!NQUR}!XIGgH1B@on!;SeKEi^k%TfaY`m9;e@s0$&|h{i>>e4UVX%dU;CRw@_z@z z8jWo9XNk6=zM1eoLn4Z@s@$b9&tiTdVI<-g#cv7bJGY}k*zZ-YO9jJ>?baCGh&f>k z?aW`;C*0Q3w(Xu&Ql;MU^|$$$@U76OvoT9WJ0Jwhr#fFa)!=?~8a+{R8F_83^yrgK z$usrSgY93N_r2SLGW^w4%@=AhE(KtM>VC@B9r#X#Y)i2~RXE4t7aNc8Q;;y~&!A=p zy+sYQOqngQArc?R1Vb;IJva&*8{b~vynoW$rh$7nMeF0SS<5_{^DpS(+#MTk&;3mP zJ%d``jar0EDEqx2jnv|QH)hJu9@DNZca2yWtC^~s;5dG0^q`YgZLG0`Q7rS=rPb$@ zyeQwDmg(;w39Ihw{?0qsmI@RzcWNgZ9uzf7?@WF3NM-oAN+;g_T~*tGNyd4dxOQaH z(L55=K%dRtt>0k_QD}~xE|37gXoN%KFibohx%i?GoCyv`pH6TUc4wAk&56WU6q5gY zVttCicixZF^;p}JDmIhlNqTa$&Cm2Y1XN{^@7@y`WpdS??wgzc21>E}T?-v`I17+{ z;J2Fs`dNgr+ePAJN1ZriRi=HvN!5CHO~RRco{vmVoYg*561liMGJ=)gW5vq zTbngP9|E+{B z7cwWr?>)62$}|=1ABj2~r6zRV@!M~bVD|TNutIZvsPQ{u;zgZ!%x=xzvuszZFo8{# zZ|LhmYC$b4JPA_%#R_#bf6dN|GOY2+bnoJnF|JlnHUDu#_l8)@QB>;Z?2>{EKi%ok z*K3Rl_C_*9LLP|q*w06?+Qc+nSoz+a3HJlsTejc0 zK^4Q}wJfIxIhrLL_EuKn#<`CFjDj$px)$}ZeuMGJ-X&gC%)lb2G2u&gNs9Zm^SNX~ zlM_0%P{Xmc_)M8w-&AWV2&3{hnZW)}|JnpaS~0tb#eKPQgUoklOxyh`Gl-mpTc=&+ zDPWUl=X1yyjshlw1-pP@S^0`AMn@m)y&Ilq>>2nLu-eRi zr@MbLUve~ZO+7J7&n#Q`Ud#*@6q;1LT#>Q1|$^0jX~@NPJM`E(Y;oVK_a`=zwB)JO(r|_3Sul`r2TOlL05m)R-zafh4~lR3#6Fre z^<%1u9>n(UZr>|XfIc)M?gDEslbm?ouZ&=dZB-q8E$z*>T{eDD#ys!jvrWsR#__Ad z3@5h(3`QELy(6*MojhD8A9g=2E%ChJkh zWe~&!3eYMlAJNn10EyT{GabillZ-TO=A~F;d3^1V25#iMs5LQ{@LNTze-U&DMe7^R z4;QO_uG~Xhn(Gg<(Niu)Azp;*2s~4Hxd1sSgcL+{@T9`(j>zI3>|Zp+ExGy*Y2q~L z5Z;ycV9!1uz}nvZ1rcY4J0JixU zXs+JB%bNg8JzK;P(c&tqXxSD3hexM(OYiX;R2z-_rb*L%isKVB7cSFGh(^HF|0#QB zvt39Sc0k98FbTG2JFyOE8&^A~WcAEKy(S9l?_7wuR^1@Du#Gg4S*q}F zSK*Gt^2Cb|=okd(|^7UOQRWK&`=1+a6{_tvt*wQ`Y4Rt(`tXCv>()5seuoAqlY2Bl%O8 ze>a`!Dt9C9s&XjxP*NZe%*OXL0EAuVWeKbV)hv6L-tSsA7T~z7s`(c#Pp77hTheu| z8v)7Zs|N5-@hbI5B_LgUMR%faD7Iy*#yNH9FY_7tK4Il3a+vmhd$Q-NNxb6GVTzxh zsoED5?Z8`p6~f?ZFpZ7QqM(e`2o6ubzSbzw(X5$0a^0!%$Byx@TGyi}8a_eJ?eA5$ z;4dS6H=rq`83tIa0XlVN$YsQ;9sYlhcQsehp3$$En6} z&Np{=!A$6$-9y+Rp_m$8)z8@iaOv-ws-}KWz4YY$p@7U~z7yhVj|6NE6^5;7Aiw?f zG*0H!X9XYeWfsc8S(l3zPpN6Zo8lESeC1Wmvo;<@eQWNSke2bD6$pXBdJ}C^a8(GU zeD;s6a3zA0^C zci+;F&gY5l(L;#uZLV9I<&H`)wQk;Gze!GNK@B$;AU6jv?i0vKEKcvrR+Atvs$9B| zv0&$OTK(AmPM#1(;L0DIx|)+60+qf=C^cXzTec^Enqpq+{e*ERz@q1!`@WbG>J{$D117ms9p(%dJkyQGl#spz z1v7&%om2MF*~*MSOgqAv*gW^TFm@%eXol4aOE~!MNr3>X&>KE8xXZ_jT11M-TT=ao zk4a?sM51qQZnbH;DWq+|bN>@iaTb(WyLYkoTmBPC@ckFi_pxz@wHZG4AQv61JUZ3y z8pWbj#!n!=2kjf|m``kv-1cmp{_O!5CO(omtyn<~c4E?Z0n@~WH0_kvt#;Dmcw3@~ z5WHcXLX%Nisu-B6IpN~R)hKR}d~%@}I@CEP47vlkpuGH5+kR5oXYN+D zw+F2Wz8g=(MPWgxsoeZ$kkA^Vf0)5%Bz7>>ajhv+#T3_{ChPWmW_T?tSc502ox6xL zaFn@t@1mG>tdt+XW?L;9H5g0^L|pAHgW`#lgmri2g5IV!4MC-c;(+`c9+Ay7$bbZR zIOmv{?buhdyW-LpE+RcEV^jsZv$3IZrUY)Z{E5$T*+j|5ABveH_A{jxu8z%-*n0g+ z?pDpM54NveOK}l$B|o+XB?Y9h)piB>q}kO|pYWo+F5DztEt*JUcy=BDpYZ^42m4mRacisEQ559>Zj0yc>6EgZ$&(`i@TrI zBX|_nF<8~$H1GCYPa2_11R7-XeowmFk@gI%b+9cWFKKD4O+5`%8VkP?&mmQWevRk= zTvxTv8(=P|Ljz$WiWZc7YMnsaqg5I?{p-GmYp9w_G)O-cSYG?_W!|zE>NP#1Zi4Si zEGn)gXUb;xCS#td^GyVB+)#2CO7&;aAw|5v_zkL66FIU2XjQZgkC~9uHB}Q_zwPCa za+M>-4`AoX_`xr4davMwoEvNAD$3 zXQuNt#d9u)!)8dk#~kxzg4%U*LGUQuZwxv#%^xdNa$8L=duh-GfIIeO(GpMF zX*C-wZ^q!4x7UN~ytetUmjN$(EF7SmNT1bNW&$hNAY^8jfGiOt-vNp-DNxTlC@{aB zgHx&+O_v(oG8wcs?yqBAJC^vx4rda8OGeWry?m4an{1Gw1kRd|`RwNC0U(E8HZ(w` z`gB?VqOtN?kkZ-=kX|;g$pF&p>~=;-bo)4U(zgq~Sun8yjPPH;_C9l@LAB#@|4_=` z6`DVwG~xW-vyia zgn0`$fK|F1s$}jZne0oWf*{tre2K46D1PMnFQIR6C*yf2%>Vv@^2+9n-%$i30!1AV z`vfvlGsG)&I=7fd=-1wQuBAnS(HD6cDqriqejCR%<*Q(L@HmhJrgnAdo2lr{jI976 zP!Vib{1VAaSoMEZgd}tpPS;IT#fU@SMgkQD=)6!rJ$Y23NSV9Cq1kiEULQ_n{+Nt4 zu~C5QoA(QZFtP6&n0wP5CY54NC6!?8LyV$3cG$6BayX4>`X^he{rfro;az+s`(X^( z?7((2d}uX(I3!Ateq%I_#@(PmDBn=t7+#ynI1F*IlT{Dv$m;@N0i*4@@Gd@rfmMKA2`Vq6g?bibf17`x^ zQj+vtH?cnXB)H7~8&2yv_ZJ8*V6AvhxKn9~8rOTee@yU@XLA@oCdb~RPc+==`F*cU zQ%iF|+}-fe+Q@>2{P7i*Wo{9P*VHajL?We=Pi5>wK}p zU+1-6@vAI9no2YF6+C$RF`?7VZ#N>jykhT;gzx&?BVO_{J16~w&v>4;$c6>?T%ck+ zl_*vcKCtCEM5do3MMPSAr4jU8HN~?2{VH>X{l`>CZR}Si12iJKs9#^(yabf#y#j*K- z9;DuXqDb6e2iYwAQ$iq6lF@WvDN1C=jy9X!l4+#jwffQwlZHB)c;dM? zncODxGIwSF@1NiEl^fH>Cm!9MOTA%oDrVx67HR3Ki(c7G>iAw&T(1sUg43NDpSh^~gft(DV{*ywMp4vg5h8saY) zeV)kH@R)8JG`t^l1112y`!$U-ml&7U`3jzZ1@@B9UuKJo$e2utD3+7XSc^rr55Rxw z&%>)uiy-e^a9knqp1pm3+%&drMBdST9oE+YY!z|$6>ZFZCZLg=_3XfO+t`>?^AotA z%?_Bq()i^Xo(Qn}th%W>0zWiK?x^;f@AU%DLc#h*2v&6CPWs8A^4SFNnb;l!a{FQS zx4ZOdyme@YOMv(OkB3WNo^;P6nIeNT`s4(JWAu&O7hCO?Hko{clIv>1v98K@1OaEm z_tb)Wi%QAdnQoBTu?lB;;vcdTPGrMK7y1^I+2H^efG|FG9iKT_aK@}M0*hQJU~Sr= zF#}j<1)Oko-Ie`6?wn&X`#54O#PAAm@K&DT`-kX(wyLXvtvoyWX$r8(-=ZNaE4;8U z)1kARFW;VH$X%6wd?;g%lT6@4A;PR1}ycfuP5EwzC9 z?znpOTd5D(hk&-}jKl^w(q<_?R(33PNGHZNYxc{c4wLVFb(djv$fhZ3^s;q|VqktE zBhJST6=tn}w{uxt)x61cexl*{Q@FqP+rP*B+KWpXH5V<2NLdiJQZ0jbZ1N`8pq{bs zf!kF@_{uG6PZssNak>sM#$y6G0d+awa7LxjJ6L|^N^TRhehT7TghhglG2p+Lv5(63^an-u8I9KV`eWd(WQ<}aGy%L-SX z{YwHi@N4yob2GU9M8nf?J4zSpG2;r*sb5;85p|C5awrC%yP2sv}*Tk z+9pHG;OMQ)l!v6ur%_AY>Srd!&KV}Fh`fFx*sM1AlL*c$STBJdLE)~z1`6n|J3&`Q zlqx-NrFl?HLOxcJ&#=*VW1xsEI>^Y6xO;&RrrkpDCI&9kB4m!YqL*lQoA}AB(G)d?R2GchrE15{>%kR2k!KF7wzS*K?nP98Tn)7)qGlBq)G zUAems05(8*K|?{>`4s*k*WaQK`hi9s5b{xJek9D7-KSw=V*b){SMu{@jrW%>{mCZE zd-rZ6%$J1iF`l08oqPeF3TQz}mjr#`pJZ!1;pzT?$6pUE8}xLh^ls>OU9!Iq&=$yLh~NMU*QU zEl#9WsyRIWFc{yo(d70ITX-<&Q@{J4b~Qf-hHw4w)$f=K?>#T#TGCC;fF+u*#w`NN1pj}(^w zKjZT!#IUsg^NE3~<^Mc+mw5SqpI-Ryafbg6{`XJ*dj{gaKl$(A|IZ~jx*CfL{=03_ zU@y)eMGVcF|1)|D^CgG629Wo4wv0*z+s@l(IW+tovqRXKl5%DQir6cRw^BPxJD+Wi zva2>356)5~#j1PohaF4#L&K?UC$89db68368NRX+<=i$P5Rzhz|4`o*FTnn!K%~}v zpbX;IrCVoak|@V3y+F=z)uCg`vCq>6O>qim?n=-?u4osS+f zrl3)#{gE4zJ`{;Kez(~)vYx>d+pP@i z;X6usnniWDAk)(3b%*Gl9;Po6%e{3bv;T?@Jhw2xC)1&z!VmOxQ|nO3J@)`5 z+ZKFfctN+FRQR0pY4P{qW!kTeLII^h0diXq&w8N%(^t?qiYXd;L`Y{s#f&3tsK-aK z((kG0E2_3;ivYWKks7!ybn>7&MZW!%^%U`fooZi4ye)HI2<0@S7+tRQ0f{SCXBTV{&`{t#vPWk1}&O>e-?!q)4&F;tQA<<7w_+wn&3(`-Xil} zD7{?l;k_#9Z4sMY%&mf_OZN7n7jYeO4W*XwwxQP_b`8Dwa6W;ZEsj`28^~_9&^{R6 znIK|nyZkk|rsDLpMm?sWmR~TBO6(8>Q4H=2^__KRCPbX`2<6aV(pXLWpm21yeyvxY zF1f(u;HQjO;};WLHtQ<|GS{}BYszShDt5Q>GB4CP67H*1=5bF#=& zX^lMMlb@Ls;S*H+#qkEbYo<#17l+xjlMcl<8$(Z1Z5nKHmznBbP37)>sVNTUsx8dD zSNLXiV+30K6MOf3Z|)*;UW0C-WpKmOEaVt8%32YSr!U-3IEF1gRun5hhlZPi!yU}b zd)2>qEl~sLK1pc1&ZY8;<5r|{IIQOp*7hyg&4!MjK8l@{6 zs;513KfNrLa&F9X zHH}pu4|PReHnfwED?+8mYGuv*Q{QVC@7;WdwN+a@zL3VQn>X(P)r+D0>uu3F4)$`f zZYoC4o^N%w7V>f6uxH-~zRsgScz)L2{t#*W2?H&6$*@0pS5aPBF%JhTSw9C3hTIMg zz6X!*myTAUrwgP+Ar0u#<2mlyTdi(zHWe7wPY{pjYimpdwd8Zmp<_;0R|{>lLW5{$ zXr{RSlbI%X=O`?L+;r@Vlg>!bi!oZy)>2@_I1?HbEk3$(HFHh3Zqu7@lG2kbm=XG3 z-mCKTu4)BT+$T4=Ag-<~ht9s<*SLc92?VaJ+^@;v0+SCPg)<@oDdmaRhD9nU;hH9H zO!p91e%>B$O@NKh-rou@NDHJyKG5mA6G3=C431mLhC*!LaQGJ*8gpkoLeMwhsK8YM zVZ+6OREU4Bon(@0S@^WCUp*>DK+U>Dhw88hjnnL!3M!?deQDF#{^SF6{X?Tb3 zgf6V`>&5FmF@Dewnc2K%%Iyr`R^!mY*&FfRGc=rt#{V%_Y8CZP^$=7&Q?J=e^Mwgl zy5ECq!q;tDDs9BYZFS`X&DDB_=*N>5nsx>r(jWv!62475!Yt7UKv65J!hC{3&6@D5 zqu;Q3CD{cf*%9&0u@^_h_|+Eql6@vs-?Tzls>H$N%FK!aQV?41CFk9Q;cH2v(HgnVWixAXts32F zXvV3njP6JnFmx9#jU5;t(KgVI`tOv{XqAs_PM28=HV4m^x~(S$&6OEI5g8xy*n^^$ z7g(}#%Z6-$S?ETil6_l^tuWfF6t=>x%nyTsNPg8uC9u5nb8fgn+TsaU@cjKP+4(pS zdqz48F?F#&oVU>1=+4NuEv~y@N1(;IkeGlLClpt+AOUT)%^< zcH$CLxHd~u>1Dz(JHyu9_;e!{c`=?F=w~o?mL#bV;9v~3_lIb*q-(*3gv%|6m*M5C z4022>tbZNr$23{wW0Wz+-@TSZ2$2}fk7k8)e5w8bkkZiKK-6+I&U49OpvxyQ1WQUS zwsNy!pyvo}+HPtB@%$TRvODkfyES3D)=G3m+SnO)5 z9c0hShHhQI+4SF{X0b9ypQz7}sPA)tKQR>zYPVITnE{8r%Dx^pkb5a4EO}w5|B0R5 z4KAxoT!lh~%ilI=R58u|HEnMmbS>~V`E>MgHRfhL8)y=|MbAoFA_SCW*JZ%Y2bcNN z>F~^nCGn}w@jOlj9XRA$(mdOPh94E{4dfjwZcKhQCN?iz-ba7!Oiq@YkE0h++ausB zB`vN(QKs#AR`%Ab8)U)3f+a3aJ2%fO*|V4sYz+pc#xLT%iq>XD_7+0PEL61>tRV*$z=JaCh|3{?7~4g zB|7}(nUTh=7fGetniWJVjKE^IxwC^(nOH9m#uI|+re!*9oWS~eDw-7?f+Zw9hN3j5 zdm(z9mGO03DlGB^JdZ-Vx>-(?Zg7Rxw)cMj*-+5fSe@f;O$ePECniiAapXn^f+VyX2?`{odxkS2r zd4ByPJ-Rg)rv+G9>Hd!G+A63$w#LS zbDR>Mdar(r@E$F7=&$%PG_^#T;yZIpY)XPX?QNFEDOx_2aEKg#QZro0MpVdP%zGV9DtCUn#w1oiUQ zsx;fUb=!nvUDBKH6qCL=E|a=Pqq8YI`G7Q>I?gh#HNRWHXVQ`a1c}`n>&3b3(Kx}j z=*qXnANoI)ch}f9lxp^LHh;>ff*2188vX|X?;V=k6b1 zz?sYQulnDr^Z!12fPpMCr^UY1qK`AQTKeHWDE(H%?f#A zyhW6COhPlYKjW0&f4wKd6Z*T&1pH;gEB65yCT%p_?1QG>SB&vTShDU(;*AZ@^za?z9H2SH&2>8&(ImaDMkZ1R<#xIsUob3m>rJOP+hcxDa>abAH?6 z@^%P(k)xr&wVdliZO1SR;S=XHTy`EY7B{&;vN_&5`0?0PW|Ce?ij776c4S*2B-s%( zp^t)|X0=ckZ)GsZ89DvX?~VpcO4r`&nAzHs!p6Xhtt-)e|XnA zX(Gw-mS?vGXXp5-QmgI=F^5wk?FGF)%ZAm({A`VxSE#DXi;4;nX7fvOpC-JCakJr| z+&aDJxXl@AgQu&ldGe|5Kk6w)I&P%WJ!^p~`VvY?fHuywo!izXAtkC9b+0X2d#rzW zd?d%xUQS_~EQ$&~$m%VMnu$D?&CY7F-J6+$~bIgsbK68G$9cQS2K{hUf7Qv&*D_ZM(;CD}vG3YqQA1v-{bLw2-f z4h8sh%)0Cfgg-yORsZ)s2RojI>T%O}ZxPQw{Fw=rOADdH+zRq_PTvF(a8cWx0 zfxoM0hDO!g`Nv7@Worjle-b$}AUp<5wF>sC5f|9||M_gSo7ba9Im13bpB&b+8;%P? zA?nw?UjEk5`qp5B->AjwV)fizm#J7gg`T_voR)e{5DuL9aslojs-?FTZ&|og`)hop4Z1)zGTe2$52CmHjqRz@OQ1Bm z8NgZqX;G95cMkn?e~Y5TX{@P<0x`s&rqrw76b|z{LzeC=r5jmrs2NUmi+Y7#8XEgR zz+MSk6Bc_h!~ju=@|=42s@mb}GySRVsKcx*&J0|(#gF;YbxLQ%6L~9r390h;^mOF~ zX_&}z$gtrFRHi@|D=~s4mMiV{52dokue+0Y6qwo&Ymy7ojBEKLhKluGuO zZA-(k=`6(c9|k5dAGq406X6F5PqP-8x_83{JNV9k`bWVJsLxf8#mgbFBG|z-X9+=W znt6~pKv92(E(GSBRDz2d);GFSo%2q`yU;jWi#0HN`_DwQjY(6+wv#$wxzGhMuPb=8 zPLD-;psT|Y0A~}r-Z|of)cdd1wpdnG%Jq(+3A=Wo;452PfnuM+?+|^)W zAwF{)XYcW+2HffA4jPSfp>Tx?a}T_u02wn$fPH9)9P^8z%d1{w$Jzg&*=#(u1it-sLDL-`Nvrs+W7%QLH)E4~Xu^ z{K*b99XSw2q}SYqU2nIywHM`5z%>8EVr*)`-;#7XF;R+P3;lJjvSR7(u%{tU%B{`2a3NX=7X_{u`9;+>!DPfB`~mYP+M zgZnR7*K59BSrVyHO{4|m@gcz!RI;nKXIz2T%H;0i)p#ZMkeT=L6_c$tAeH$#UQTWx zSUFoIe;AKJQHQz-rtRA|2Oe7LQ6QWlXHA?2{6HK4s|;+uma)SlN$aiDU;PskqeJpW z{1JvfpbKN32cAI+7#hkOTo69NHVL8r&<*(p`<^K&C+(ym)J5T(>s#A#pVh27j7?s> zZZxy5qMA`X=??)mN%U70`s7hgaiprl`ikMq^lYUhhk)=0*nP^h$Gg-b#R8w7JB^Sc zui=vAkuvAH`4J<@if>jtL)4@x}#S6V#E)cj@ z6Z89OeEa8Tk3FwVvJdK0_fo}kwQ8QkTBq7^$Y?y>T)3T@JM@lX#p}>kq6S20{bqYd z_R&?k7p(SfOO)5#Y#-tbI=w5uiVj&`kMgd=| z@;xM>&~E+>0IkwerjZRDTQC|J5SrPzDPx=ry^+%~WQKv# zpRPBD5fRpZ6^dxqY^z%;g_o{FoU8EJ8lIsq8fPy2DUZaS{^kNk_69-h7-kCmOzpwV zL}B5xR_KB`>ZAbY%p}+l(83BtI&_1X4RiCk9+T>Bl2D#BMoXyjV=E_c(?C&Oosdwv z!((UV^iQ8Iy{x1V@%*opAxjV=;^d*ps<^zeSf1FYy1FfhfZuD^+up|ulfv@Cu0Ut7 zyWR%%82E2{YYtDJr+!v@l$8ra4%0J@WMP06zzNf;C==P=i4|bBGgS>@ztEk-Dl9q1 z!!R{K8j6>v2O$Ff6db{ISpa37OF&y%*eaCxnoP?aXSzTpW*&%Yl!jn|?!Fj9jo5I% z3Pp}gWMoMLIJJK^-A#W*SIhgM9QIvm8(Hcs}SWWh~WljWg{_!duQ$*$kX~k%KF^D)-8jCSOHAJ zfr}~RhTd@DG$=)2l0a??>eH~u>ux1>^I&4Ves*};rdz`Rnflps2iSUel>iPX^1`jNTlQcf8Gr%xbq?M(=;V_&{MMep(2Ag}(;0=X z<^n;>8-*75hSdd_!Lp5rfA-;331yee-wtXWZL?qOW^)0_=ftQQ@)^`~DWoCjMkxQF zq(zKyzQd+sXZ^Ixgp5E%>VzCgI0v#?>TPkqh7J2;Lx53fB!5H?xV1ku6lK5x{ z-V~?*G&lOIRsna)aDP*r)=e(lx0+8ORrti-$n$rcw5pZk{Ik6vSx6L?E7X!X*X9lt zvhCQ#6jx(vq|;ZRv%esblG4NQ#wJ-KPh7qo=|9xhkv_|1(rG$9l`=8V%cd-R9^>8k zw-WkQJ;E?8?b01C4RZXx1W8>36xFtWxW1P;@BYctCquVY6QM77Z&f)II%HX$4meM# zK@{L5`TnI@hkY7TM(*8RkRyGMasNbz13Bcfq?1@*(J42!7sr7qbl7+>(lUMyA(Izn zU19kjDa{w>776zlQqm0#wE0KS;Gk1tD_d+REK64Nm6^dCFE_)ST!8*kegUt1fgOO* zcCWWGwbu;<*-s9LNCyLO^haD=ZgO>wjwcyX$7>tWrpGIEc9uo(_N*D04wMcI=TpJs zXSVL|!Eez>07+W5x65AG83j`Y=*4UMM$3I7{yU3n0k_0bfzjEBaJhqm`EhC03je|q z)~AV?AQ^tW*i(k zWUm$zDWAet8@WBBL!xeh)x_Lc{9!|{lFi1I_GtMS3d zxmoK$oXA!C4U?xKRoU|0zBnV7aFA6I!6ux_fCtRe2({!B&J~ZVcKsgqk4>4*Q2#C* zaqCe`qxf!(Lh8r4y#lnGsNXc8#>&Ke(&DD5iM^vKt(ll9?#5^At zXaG6Eh=?diThuGYX?z+N>Fu2HB0f#qdhwD9ahoV!MNhXIMhd1QD7gd#64Lx?O8Ogi zd&{s3>?UWgaO+<&Z$p1oI1cx9vK>a~Yl|%+m4|9HJiFs$U%I_T3g>b4xr*-<#wE_T zE$g6EwNKcG%o;C!U!_N=txb}hc1wu` ztr%EOWZblw@xenz_u4>ge#$v<*PUPn9=*|!Sl?);!GnIigl2HqQd|4xaHaeGO+p~$ z(C|z~bw%^|P>NxF!ekvJ0=rNES{Kz1Ug{WT-Uq#lmXEFFtF}o;ORa^4n8Nid3Sr8m ztiZ1|daqns1NvVo+$XWC^`LrI&cjMach?v}1@eBZ{h(ikH`N$WATC`s?&L;ZFjnfNsYQd=_s*CFgl+VUF{oc$%;S<#xdYPWRw!iQt>x+Yy;|dR%%n9L^ zTa+TGro9x#aIMeu3@Rn85^Im9Kj0MEGRUZg2hFr(+HB%x=D$xBcPmB}*qZ zwC?ridRbjHzyW7PX`#_Jjm^;?;ljZCKXyPdZZAn#y-KUxv9U2-RQN#sx zqvry|7r%V<=w2PI#pmR#EHA&cg>W1l+_;|J0o|CD>vXMN4c$rLT?Vsv`0Cf%N!zNa zC~B|PVzw;zRm(8r9oN!llc^l(r3hZvOYh4I7;;ik9LwU2WXz0-ob?{LdNs-XT(TEQ zOmHfsylIWqF582DJlaFMb)`j7&6=c&2sv?U#P^f@Ak$3kvTKpCVwBt#QEfXTe+iPrX9}|w%Dr^N` zle9_373km@v;xd6Ah3*L&Zw(BA+|@EKv)z@E;f!qU6C#RkG(@}8-hcyRBH~+1w@Vh zT2x`k6$uO^ZQ;;2Mzc~4{A+>^DQIPmp$s&J(sCsm!}Dh>MwZuJ?aCMBU5GGHD#-iy zX%kax!4U+1(1on+&;mU)pp#{NtszNBfP?_|7vAF5D4{{mfXa3>loI5|yoshnJ#GS) zCcvpdZwJsMMee4{gM+&lK#${!oh&>Z^rVGXdkxJLFM2w=lOa}~4)p`fuoV|isx|9p zLiY4`h^FNN?`cKRrgaAn2eo**WKqLj-}j=Dq41@bd4A@=?#M}u1q>byS;X7!IEGWN z7WKXkC0CkIU0i^jZ z7KL4a!^eNIWjz8`gc}U$73u*$5`9a%5+w?{@+bYu$m>PXAU*imi{6zfA{$*WIWqu1 zLYJu1i*y5N%~WRFUmb6S7H-*_Iw-9$>33nWlv zHsef&)b1-uGx_0Lm^AwdO?|S50ZMt_{7Nnh=WNNK{}1-wGb*aAYZs;MR$DQ&AfQNS ziAuD{nN}nhAX&2HC?GjwKole=i>v|#RR~DVAd<5{k+USpNph$&3;X@Pe(!mIoN@2> z=Zu-W#_ zGhT2SMpq4>gxt%=dlZH0m=V!VYoD_orc;o$KX4}H&MLyBwq(0nOkWs6M^Gz-7|w2o z2tGCc$_MNE20v~0!~N^^YOEBTvzFuz2=(?dQpgM33k+<1m}WMg`pgz)xK@1{yi%`e zbdE?7=H@6XgOQ>TOAj%z=F(sU?hXA;c&ftG^k&R}Yp2zLwALd-0v-xR(>jr{$|f}| zOh?1!Yri2_&*b=a$bt)$K+L+KeeBuQ)A8d89Y3qfvw&`$?bB$Dk&D6bq7;>pST1w) z9J&Z0OMS<5GpQ9#S&FR&Wsa~X{VlzH6W5&hp^~h^)X#(O1rq7kErYEa? zq>(g{Gd-v2f-H1E0OrSZ6$0PG zH&b0#$nuvYRqb$fT{#lVg@t=3(QB5CrFvTfH|9agw-b+qRuvxpQH6Ty$57lYpM}M# z>FmiJ?+vFb)U8qNR>MN#Tc&Cm53`T2zNerNH$~)Nos(-2IaV5RIl|J}s!vSilUHt^ zBGTVM#i>2;@sLp&Ep#`7DiGqdX{^N8R{5*HgJ|LJ@3UZw&x(8|A*CHa+ zyQh2mM&Ekf;YCE3H-$8_Bs7K=8GG=kzf@~<{=sLW&v~vl*%Ybwh-Yu)bW-GeE5MDW z*s@=6B4|aI4-Yj&m}vv3RV&|0!Zt2)`>`3MK0E0X-NO3^8PfoLMN)5oyW5->SMGU% zZ7KbRYIDy6M|oC2{OAM|quyp`+UaVJ%V_uPv4<+>JCzn;^2@mOO{@v-s@l8MrEK}8 zY}SrtoA7dCHQ&qFsoDCdGNgO4w8xi(r9jomy;sv>Dx8ujfsTR2OQ|0p9@Vn&=(^gv zpHn5OM@)yu`3yOP{$+x9dFP=)nN_ZPr7|#0Y0O-ePK7fZ$E}v^)|+ADn*#BJ11ml; z1I&+9Do^GiQ4F!~bbLv6 zRW4+6^$V1bx@XEB#CfNkssGivMZ&ZN)sn>#|G`P`#j3FI5@vN&y9X5)0Y0`<<)In) zqu)`!`->?K+*WxjO8xg$^PPefuK;`?5Taph)gwXXdHa!yZ>v3gedsQ_d`p=O6of6NoB0GOO(EZJHb812_x`;!= zD+m0{>43M86>6?@Q?rtix8uCc82JVR$z`wcM`&8&janCAxXcU2h~%goUD4>pQWPk6n>X z5~2~Cem@OPtk1Xe-3(I0Ey|5G>YReH^I?$bFU(78ueY=G(zt~A%hq931aFI#b+QNP=DPAW#R%&af9#rBa&LR{Kg{^Cz| zdBIr;G8C#-F=DS^hBx+hw)g3k1>Lp90xEX0`PzpBr0F|w^|Uqt{tNd4uhakvcLgDe zE`!9Bsrme;=IP~ioqyr12rgpgwyggwA8fZHeM6_oU4~q|UMM_Hy+IOkQ>k%Or*Smi z*aoxwy=hmT*CLJz8=;!1^^@H-TquvhSpBAxlDHR4CTCs$-D%0;lwXyiM+IP;6v$|+*uqZSpwrjIYgR&%N%g6^t%!;$>)A0sj-Xw~`Xvvml%BCB6jZa8!+ae#B zgf@p@n}(2?8Y=R7Y|ZSTaC3{&%)ZSrh}~J@O94AI1xjm?k-=i)ML1T~FXUC|af=!? zKW9ilw5|R3=}7rrjdRNR05rSnaf-t|tauu*z7;oLrz{ZHpfa!!x`bN!r zQo#Kj$T?i%hEZ|4Yq(!6n>briDb53Z7TG8QzV}DnV6d8N1$eVptdJyWa#%oV58pPT zOo_O6P582SDN1gj?10M^4|9Jo@(!c%lF_Z%+w`f#jQ9Q=nII*vKPCcxB2tt8)a03N z|HjW>l78Hk6A}zkY=!K@Z}ScT#!Ze}ml-!L=a;uG=qG9FH+vDO6S8dUCHp*sL&WZH z)Ed-;x$BR^Akl_EM33f>-9Fk5{;JH~To8Y4a2?#2nLz>0JH^UeBh88PwQg zfoB2(;(|0SX6i0-dPFm{&VVk$eNRJ*-O&?gjhxpRe^({f$Z6kz*pii(>5`(TZZ=q7 z7Co9NyTW4aft_^;xrbEHjaJfjM{K<38&UOu|HF~Q|cMHQtl_5Nt_Ljo}_&KVMjHziH4#$;%fWn*|Z-x0g0E&~UPSDty? zNr5Ix$QgO@yz-KPM;_^)#`3mW2<`{sb{l73HY?swmP#u5ylm;qWv_bU;Jdok?)-c! zRT?B>W~CxUYMdS1%!&6b%>v*2F>j6gUmilt;AinvKQoud?(geV|H_uVI z5WG#5{tn1)lBmWK>w*(NehE#!0G}BFrtq#|_ol_0$mhZQ1B=lyf%mYzZKAbrILS^rEfX=2}`WXL=c zD=yh|agMLwVxv-{kdPW_PD)NXMrR&2V2&do3o%r5*qr2}W#Gaqujv~|L{x_>_06BkEx=}`@QE^KhKz}`vgNzuaUW&p*Q2{0m0SQrlpr=5h9H6H1 z{a1OyN*OGomKz2inYP=ycr8`T!GVtfd&V z+DN^4LP3GUsEvWUM|Pl$ICBx8MHp=hqlc4m73KdJ0#cNld@{E9N$J zBySQj1J~VZ(#x!<`LtcM-R`fz#lDu=p41x`=gXa&M0ep8gMHJkdg3O_(6~(7 z!P)Ej-G_4k?MTH$Mmn}rgg54NK03Qdpa`?D@J_v?(ubY?C+i;g!gWg`UgIa@GC>xC zSubT|Uwyozoiy>id})e4UGcJP#Q7F;DpmG9LKwa0FJ5xSfntOF^!-IQ|L>3zfv6h% zP{5C>WfGS*s^NEd2U6AhX5?@|Eq=atin;S05ZOy8>AVahq{3~l%nwHwA!NHn$nTw3 znBJ9KV94^32ac=t!PHd*Z#eKnPB&Tt4`uq?F=Xxbq`uoBe@q49{4Csh{2)ZDm|7d*#1dln| zG&n)&8R(;tni{oQZQp{jn&G<6q)I$Td|x}pYYM{Kmnje~o10j2K+aG!1Pw`iuo`AX zWQO5y&Nv^px{3g$Grasf6i2n1(<0vH)M5)7om5!y^x)IXxY@ zUkNo5#AkOP^ahSCd|~$Sd)U-~wU}u)&5>t3rbk)+AvOCwB=`+gDfSLcN@^pXsxP(x zpBtP=NPs~wXtE1=9`K#oMJpj21ne@PrV3D>OtC-FiZ@F?IV{#pHLhNOx(gdow}e*5 z1TE;a5{+zCQvZ+niPNJY&2$PEjag#3`@O=Mpq63CYir*(bIGln2B;OSS%*w%hm3(Hg<{P~bG^%nm1Kx0jgvWzmA`AJ#J zQ`L^zMTy%bii@-b;0wgLI^KKRo{f&SCpY^^a;30%0z56t~GL|AQ@>__plDWt z*28&C8D;qxe^}%|l)o1!*P&Mn92iFQ?}XScI4HKr1H$5>?XB3MvM>^Dpe!H!7Kvg> z&A|1(G>HHRI+{I;N+Kz@FhA_W`QH`?-*@$mx^@_2!#;fD(m?p~IXlBQgEtA(d!U2( zd;azkJfAvk@6G0^_gU4+bw5)rFB_P-X9`S4Pl$yu9Q3SxQ800+s$6`{5WOQENPxKY zH-WWN99EnGHw#7zsKh7~W$}V|^XK}GNYnR{!M@>~3Xgq_0pUYlR}BFCHBjbYE=mW^ z@%iC$+WL^S)v>~#-5ZyBJh5?^_p~+YN5&Lnl)=ZH@sC8sl@|G3@9rB{axgP=4i5VO zZsY?>LX685ZT9l&40%aV9Zp*VVX39+xaIExmhDeEpzU6aFJsXef|tx>mfgc235{wI zfy{5m21bB7HT^jG)2*L$_|%Lq3zXYsa28eVyK+g*XKhzjuBcosKgIp8^)k8WSe--@ z!RD|LXE=-taZj~lfViN)?j8pkT!*3)Cb{8}2~!*9FtlZxAJyE<0J{WOU@%qPfk}0^ z==x0p^{!5B=g(~5S^DcFkMa>1NtVa?x_o^i=#2yszpK$j9$DR}mX&)L82X^TfnEYn z_&UHFH6a*C?3$D$zDfqB!}BjPNOp|1+r~O&Tocv*&aj8Os5u-21e&r3K>gou2i}%q ziX9y5>14N$h4i0)Aw*J0Bx7{)U@{*hwc^rGXrf>hYM-?hD_xcidaMiF=FLnz82;D+ z6ebW%D9!xGTTpdn+(fKbbLDlxlE}qo9r=4(fF9W0YF?#ie&Em?9@C4hr$#f?4Y_#V2+g7G`m37S& zPco~;VINA~ESdd5zu#)WnPEw=J&OPYaOK-rZ9hFwQ2gvFRw+JKX z9m@k`B*^^Q*vw3mQI=*`T}l7h3A#?rd2G~in&92+VTj*Qy=@a9%*~K7|I}ZsAf3#F zW}*<;`ioMkCuG3=)&Q5W-OdU*6+S)Vwf{Q-CDu_7!t?D1WUoH0xOYh2;x z49kTa>qIGnvkqKB*=zKYDKw$b;KfS_9Z`h&NfS3|^ZTevvJH+Ul!olQy90*Y+fy2l zbz#CWV1TMhAGw-|pwlZ(J@B5SDT&EUYQA?_!&YPB# zIeFGk$yHmO)yB$rHdoCzH$Rt&UB(&7PE~nv`Rz59kTyp?=gYI;J68J%JLl%7$C~k4 z76!9t=~he&olWXpFKBWpXd%C~pruXb8YlCx8L;~!}rI6PEg+Qr{>c$0q{h_bZ)RdM0mF!&1 zp8sw$zsy@WX`FxZ^ay0_=MJZzZ^4c&CofF~0H-NgR=ftrK~_%!c(s^8onDqG>+8fP zCm}SFUOTmxAOckMPe1qcpPjK!{)*^r)97#O3tTYVsq2Xr=y@w7OlZ_{L`sQjT` zakcd-hwT>|BtjMaY%x&}*s-7v4bo~0MVrnT^i$2QkBqq6eVtC8|cG&FY+F)Po! ze%8-E?dL&rXpy@v)a;HW=d?4S0r!qBJhFq7)WaByyZ+|c2Nc|&u$#dZ*y5#8(X3tl zn%tPSwU!WmE_r-?U9=z7ue0n%?i+MKx1eY0*Icl{LN-y&UB<~WF=ON2F4$U{*@{zR|`V` zq+s@79jb7v7=t6A%SLPML)AA^PS+bn{+qSjasnnA0i|bP*ENZo?=GqTZM;#v{Uh#m zI}bRqA9@tNsJ0PX;lYu9nU2y~uY7LAHYkt0{T1>P75AucoeDX#e){d_^kvGTGif;m zqA4N_>vz7LPM`0){P6x&F)~%hae7EVdk0;&P5K12HlN*kg{M1DI2gRn&If+?6?ZZ` zq!7JcrnE4qcP&9}_#EgT2tKy>f!tA1F{mix_fk1ZeIt?oC1ZHhm1 zg<>CVp0*1=vYwlrFU5uLN!B*B=YKfz1=n18o(X1={1kbk>5ZE|OBpI$5QRzm6Facw1g}%#d zmt^Q_FZOx2*QvqNMxRszcTU9OK6$$!qBE=>ld9mlQDkv-YUm&#Hp_4*V2(6wtHZrk zr(8^ zv@Bn({tbMSsXS|yrtD_C^&(yZAQ6)riMAgMk*1w;J*cueJs>cIH!|AoaTJrjE^uHu z<~F8&b^AcQu~!K${+&&7bmn-05sPdQ!weUKmxWP6Mma#sJ&<_g_Y1A;nKlCj7((>W zgFXyonaz4V{R1joAdwA)PcB72bZAund^nC9?HTC-_5dtakJC_vISjGO37f`zZiRp5 zUsTSEh-f$@!0I0dlVIl(cnAn#h7-4ZIjn=-Utf^ixp# z?P>AME5Ymzx?HU&IhOXkgD+Z@K0c<6Hyj2X+`EDLXAM8=QC7*wAwEbW9tJR&WY6_$azjc9uZX)iB_F zUTnGiJqteu6Eo>yx~69Y%;7rRy}Jrfq+@iT#GLbaHfgM{>bR|t5h#OD#G0u+%+${) z3PtL@UoOI1nwr0(Z01Qx2K&ZgI3kNY9?SXs0<9Buy{(3XAwmIGAdt%_RB#LDpY3bq zW?``8DpA3iodrf#Fthp#E1K(YB{uYVq*`(4aplRKYw(Q`=2z zc0y{TyqE!UvI#J$qp!p@8CNe}Uy|{UEXs+yBX60=M&G`W@357~g^tU~=+B~^IGs)z zwaneTGN(bqgec6-%tTj8JUL!mxB=uA+;7>-SHn>YOVn(sx6p2pdaA`Ik*$yCW#3Y{ zY6NRORH7%xa+kX648dO9+qr;YDPhfjc{Q!7=MfN24UWhM4aCmt-D20;Po1~3pv%E- zUd-c$dDKTQo#BW>|`z~K2Tl)0PGcyze@04x*DrH>+7H%yD z9f`7bV{lJ80MZNg+|WcB`?l#DX( zGBuR+X7U&C@B9F_b*#sr_v5r!fRapV*oO!Q4MI`-o9TH=+WmgdZ)HuF=XH2 zb(Xa?ba<_coH;1ts$4}5@~PWu#HsBw`f{$&%P`2r-~x0(qn`X`dtp}tCUgu`@+UN#&8?(cX*kd_(FuCD=to^Ow%YT@N5i%h zJye7V6uxz{@hPsxOs?Z!E54jSLNd3S>)731s63*uLk*R3;@pasqIL6dcOh3v?Ack&>h^;&T^3r#+WxQz; zM{P&UHVsYmvhT+QuUg9h;Q)-KBr$Bu8qxUlZ;q^~bynV@be);jj%DJGRxr5(cF%lTq*jTay;{nfInDjX3K%5;Z zi7I#&G_huJ+8HUkBe;)wz6jwi@h|?!Xua*undUbM6{AZdBC^iwpHd|?yg04<8K-Xn z-Z)s|zW;JKBI?ag0B=pZ0mtfUc3qWVC!23wNixcQ_*=`>64SRwk#%Yx@{U4b8#jt8 zVw%3zv{fb1VyWH^Bh}(mAEmynR7xIU`WUZM3!BM~m@-TUoQb4?zC9LtdCX=>K8c2h z(ui=z+(V|pP4i`oIVXMqsA=b^>C%}u5+Q;%+iKj1vs}AOo-^giqnO@ z=1GfFSzlkO+#SU;+J$#SUSoZejJxi#;X>b;&lpIFKhc;09h$fX-1P@#Mfx`{J?rXQ zV(vN2F4^0RLF@SC=u@F=X2{oYIviDRevNd0AG=Yj?zpZuG}PaZ>!nVNsrOlVlUSY% zRg~SbBZm@uVl{!akna|M4xO{-1H? z|F^UKr_1_B!=iuO@z4AHpFi7w{Ko%y$3O4)?}k_c9fY52{Qo~Q zp`zsPY#Y%U^6z+@Fp+=W#((|tU&iFWe)*p{1j&tFKn)+Ze*IQ`p>5)HTxXXjdH&Jc z_b)?AFIJJ9T*O*Sof@@cJf7=LzWw@>OMxz0l*F7ZULj)%ojroLhQ8F#!AHZ?P6E*h z=+%MeD)pGr7oyjzpIX4F3S zT{J~d_;kbJ+h^CSPJ(%8r&ot-bZ7FXrCJQ?=ls4kh1y5PJpVaXam0a&9GQ=O+WGJ4 zdW;q7xTaHORa8ChAN!U4!YL&wYsJ(k(nJWGwRjt|%>>^B8H zI&EivdbDdI#tSKk{pGCWiVKSlAb+>{He2KkiZVGIx{Eu1QP7p2`;JpDQ0wbD;MmNL zO__s!&?(C6i3bu4Q7o74f(jd2YHEwv{m2QKIQ93%e_k@izO$@_3j!G+ot-l`hRd5i z3r(UM>jQQ)$kgrG6KR73EeQf;Zg)gj_+Qn1@Yk_Rykz{J6d!+Qq{=UIGz1Dw0z9>%R*e z^-Mp{)~%kQpy8xYbzK_c<`E&MOm5JBdo=DNEhheCzJF0Od)(-_z+qGTsOK~*KmOS! zh3qYLDmv*W@)2_N61Bz!YJ5B*8iThOewCQ&$sVtgFIt;3@Xh}kmt(~)XC1eC`J%7) z1PostjGwfyYq+4mzMt;?R@p-?gb3eQ`JMz5y1{fha`MGxX%BwiT_a=jhpc_K? z`RIavby)w&le_)%$w5)?0?8+@sF)=|HxtS2xL>71LxYtdakZgME+KE4?0eL{Q5TfL ztSEksM$clIBs_tajt0N~*|2^#1Ws{=HUw7?Aco(+0Ud0oKL_K;x4-!}HXMu{ytbYf zu|NK{HsH3{P*tRGME`A_x_ld+S}2t`=s+|oC9{9bD0JGY^je*OVv}pAU4W`yf5qBRVb~6w^{Ac$jGYXg3O!eMpQwE(^JDCty)S z@IXn0T|xI~H=lFw*w73AsS;|jX(^cyTU{9_y;HYikZZeQQbo4LQ=~Tz##L44ceant zLWgj}xB$5DgJCi5+#Q&?5zk;XLjdFV0aq9(WfCg;cE)oZo(7lQUhD>Yr{V#DL&+u?F&&$(fp^n=(r#~(?feJ5>3iJodT;9m9} zN;E141uK13xxVN7*p&KQEHo&F^#Xmi=lNuzSk*-tH0{0)y*w<<#f$Q$VSWi`mGRbt znOQS+KqcrCs8`%qH)d?(^+6J+qcRF&3=hlpu>lyEn$QvZ(U`{_5f2av%zKy6NcMEO z^0_7EW1g~%5es{?C?y#f4p!SVTHoWD?wtNuao1I0o?Q!*2B`asp%U;_wk2K?v`<}J z-WiM!e^5vc-7dhGMs_br)6@Ev4U67-4mvnOozru_lBjYMw?emY&=Y_;WV}P8lDI3` zphjYuG>V7rwkG_M7rjB;ZMxUA)1%b9!vHiCo(?!Qe!gY6>KV;W$0d?k0;*OHtqVA0 zYY=I!1220t^^xIUwKEZPZX0D;u8!)hQVM7~#Mbr4XaAmXrbzZ2YYn=gGL9v4uAL4$e5OWSk6Bd zQwBs-Ab$-j_{e@120`h_?YOO!IFMlQ1<@L7i8Zf~Vg*iHaHdd~6*^)M-(hnTH)VBE z_UNo8eF-D0ZtNFO?3%IWC;N36Y#KLc43)SYZR@#EPh1Dx8uq>R$yKno4sY}|M?rAo zcByr2*maV(SSxlA_KO%Ow#6r0U|`H%(r$-k&`!Gy&BUF1gE@g%aYjmz@Valki!Z8_ zjwEK`rAL(6;n8Ex&mrc$}@K#vSYH>JQy)~(xVjynsl7t?U20Ug;7+XM6ir`Qn6 zKN;5+t6pR)4YR;aZ~-)GXFQ}CxEFa#A6!HeJP4QJUw+2FXfoBt2Ig|kTNaU*#EZVuabPaS!*G7V~L+FwWXA1Vt&8-6Gz ztJ;}J2?vY+aIF&TEG#8V%0YcS?&){SLEmp9$E0(4a*OG_z2G$vR6)f-fby=q>C4{s zu`A86t=?T^f|+h|^gDY%Cx^p@*ECl`?MC-zMr&TdvZ}p;;J!4q&@TL{N?2Ck_uvR~ zw$^ysSvCv$|KCr`87$ju^^^z&Wk%4V2{#6X0%$}Yp``}egYm`QPkb#N!q)G_lZP$P zu$?tYNa)rEB@WOL%yt!rGaZB4DJT(?IUE&%%%a_QW$nB5Z^x-X)(yOZ)iGZba(C)w z>v;09?34Ek#hu_TI=TAIf&?IVe~+|*f&IIzTCo%CLwKikuD#9g0X<-64JFz8yWPi& z8)YWE?uZPHZC>gbQHHCyksKE9sH}c;i~VM=e`u0GyHi^E+{6{GNcn1gT1pyvpqgp5 zfd|w~VmEr@LZITTwh(9yZEO_AJLG%c5fKvM%P#O|nePnX!SUoeG9yr4MCg2@&@^Sx zgk!7)BV3m@_k=xBR?)b4%x2(|tnF-jqG%h_P~i>{Y5idIv(AL_&&R6sf83joNgczZ ze(=KPZmu1Po}q|biGmrfh~Lc*sCirBMgsyoF~5j&i>^0 z*CUnqwOj1Y7haEj-5G<~(^q*`Z`GSBk1qcH1L(_N_jwOQ_dwo;L$C7oPAphDv1U*P zg~?zy>iL(vDaa@hltk$Z{C-SSOtl`t|D)6L$lMpHg7uM=}!`jpA$Y!cyc;ys8HFilCkmEHTAW(b*auH zo4AdCQ@Am3nTa=d=Yfru9QGHTwaQ*Eh7FN!>hhfoO2hZO$)ab$x^7EYUyI4EnvUh~ zF)~`c5*cGWGux@1&}tLs#A4h0vd=4xcA!Ss4;PiRu07y+I)0#(HE}avzzt2IN_~nh z{bc~=Kq(rqpA0_e5m|u(U_g ziechAR6;J+xO3CCrdo-_WrG%8eYA@(E-Og;KK1{K| z*erH%Hm?=%lBGu6@l4X6Yz8fo{;qQOVTuzCLHi#Ki3gyV2rF{@V0if`g~=G}8&!!n z;a1*=yZ`mu5Fc0a))x-g2_Kfm9l_+WTBQYFRi|jxVJ)-_WG+kPt0SKHd9QY<#5U&r z#&!HV``*H5<+J#VlUv=Ry#7k-d(^PGn6%P-`S3OkW4y=kSjzT!3AJE)e%hbAvD;wD zZ)}{IkCSGQT*R$>Dmqth)O=ZGKo40Tii6(utZKMi_4ctchrF?L-4sDVw6>C7)^efT zr0v6277%0Y2kmF|HgSbxCiA>R9;2CWeVg+r;a4}xN z)rez1ltu<%wpol8JfUqP$Vv_88)1c6(LT+AnC*`!Z1eU_g!&V=*|M_7o}sqGo;=oi ziL(;Z$I>T{i0G;2ZS>HA^x(;BfbdgNaWBpU*XCSE8KR`G)Sy-^TIu^+0LH}aTO?{9cevvPYArQ4b4@;c%pJY?CwP`6yNg@z z!x3B^@BwMd+wR_QQ=mT;(Lay;ZR<^exsjE&*a3HbBjn_dWs$+3c0x(EBT$q|4WLGA zKqFuTmPQILZ1g&nk$&d57=$-HL&QBE!GGfqIT|OQ`Ly!}w}yvXWXj6^>gBZ|&We@_ zqlpHg59(i2K+z#*tV|dpsJoT;$#jF@ZWV17d3`;pFNXND!c9~pj`B>#3q6h;*vE{{ z0edxw?c*yH*}5RJNDze@8!f~SD{IT?Cky&oM#c2meAmwtxm}qNcp^Up#;`@0 zOm^DCj@o~l9HP&xWU5DWprN*$v`%msna^|}B!tF~pAy`(akDuJt-h~5cHgha#+UEV zf@!N8ArY>Y(Pmj3ElUhaMyY~m;pyj0rGk+$8P#JY68=AYOTfch5I$%wH&_zoxhLPW z2%3=*&syV!dd{Wr zu@JfL>ld?hJo~!RU>c+~eX6{ogKF6dK44UjwqsA6CxWs%;h%mQ%4&{ckL!2Ero5d{ z5A+*&q8ouSJ{^nDwD!d7H~;DN!`x#<`w#EE9Nv=}fS>zP695v3Ca3Hy{R=T6%27jt z%fMJqgK*oB@CAJJXFTCfyApzzMe+ZJAE`Hv%mRcIv@Dtx z;|D@pHE+0*Q~2jL3$#hBY*zOS&X@_!^6hGzocD9)6;)XkfVJv3iZk z{qx8F`MX3XGwSD$t=vF=K5$RA!xAE}B|j<#KQ>@L1PVOR8@buZn-#y6`3G z4D31}>#k1+0kOD2RT+fwj(J}<_W2wF*mCRufYY{U*A(~piNDR+XDKX+W{;NLsG|Fo zps!G&i*SYj(slFI(#_(Pi=f`^z?b=Cy`?aBJpeSPwOLZqxHk4HLtSIk_(#0gKrs*I zdjJaS7BHeltGWp0x2*>fwXLMRj151bI5WS91^?E5TdU(=ph-V$ z+Gd8=FTqK7j~dk)j^0l~=xc6;j)}AM^7=IZws_Up0H7Tdd<>V5gJd58 z*;xZ^T8Ik(?DM!U0#ZJJ)i}swn|^m)xm@_#qj}N)V*HAjTT`JEN0udxm%zpmCLcWO zSHjgmlO}GlrU~u>Aq{!WaR}IoI;$zK5Ky5Czy}~OgtNirU!oFKlqz!B0a@%*dui?y zhmto!iKsw4WDS&yAXo{Lt1jQ676F;5G=m%vK!@Sau&|(~_hUqlmpFnfEU1X38?3~C z%!vL+34v{a8u{Ds&O#^9eR|?%Q{ilEFpNHi!3xpywD$sG7UED0ppmXCW37cw zVh)F(G)^txAyRE~1fv0g1fW+iuV9oRoHEcH-Ca*Fx5U`YERbZUZGs>bxc{Lt9Sa@@ z-nk}pw}fQp4?#%t@EE71PH`8y?C1sNkLQ6W30O#7>I?vYFsPtpwUgd4`&J`a#8Z(i z_D=531Oy8L9supx99uL>S+%AhC{Jc%Hwq*KgStu10G5UFI5>Wh$-f%bWh43m6yuH_ zCqqvU2ptrbMj&=utqg4vm>!Zn<8wIp7Vyw2-+?g+X}}_amw@wX| z)YrAMf!dhG!cYr<#a%~XcTpaZqM1mgU+FZ%jv4AA&YcNeeIWtLGWtp20PnMNg_UMI zR~}W3)GvZF2q$R!l1Gr{rXGa>4O8?cn-kx)pI%#!{I6?7QWNl<1h~+Wvyy;4^#nFr zYh551)(RuU7NE@_S{?Qb+;~ua>*~;R5U&I9Y+4H>wlKBa%O#K*eFSADT8}|V_GMif zi{ANhahpwPVenSz~H!_PTWSeUKg7TlQg?YUsZpckB){}e5B(s5^x2B zQlG`HjFNX14G>6%Quyr6Jb846h{(%>AgCYn94*lUmF(FVtW!NH>^WA-YJNVX#E2LaD?@|*G`QDiRxuq@n(_KC}=*Z zuLEegBFhMMWJux;EYy%JM?Q|zp7nbGb&poA{&8Ikp2&S&uIJrp*9ObNvGu8( z=N67aW2<~)tsWd-34lRrP7ptGptYO|Bybe;vr$5f5Y$!qqQ2RC=1k0ek|YptcE<#` zfWx`hh#R#3aZbx(nU4Q!jj)L==X1HYMm0NTXE+LGGs|8>Y-e2kE79jnN&=Md_jdT7 zcEW$|c?5szpF8KtC;$6H`VA;6o2j2KBbzBB6L>8fnY6z(ujfygz5upb=%K~DUod=2 z4rvw8otgr37%pp@8@Jg0 z@;5bljlbYP-Xd=rL~r{*Uo@G9UQ*OYv1Z_m>VRz^I7n;d@~Jj_OC)L(Oz$X%%itvn zT{?^w(P3)IEtyJb;LAMifAOA|_6A(PK7Yf8?%>M|hLkvwWR#r9xy%iBbyF8O@opto z-t5m4-fZQ#$Tp3 zgD~ga$!cJ@Pexn6=K)KyqJa%ky%tJEC!8PIEC!yOHPZ68T5}pg(=i3In1!Vqa+UZe z{llxmZm08KrO#0K?$3<5iVe(?k~h+<(1s7-x|Y9F-#c!*oo%!5-HgGMB120;eG}Qv zK2+-23EOU9XH&8RcXYO03kRF|ae@6d>{5rjJ)d#K{A=%V8r8xG?Cjog zQ772FsFAE^ef$l;cufm#4CYLIkninAj1$)-n)wDpz zPfqL9G&g*CSgN=M>U1}9#wr}~lb>GZ7Y8IBxH6x$zyvk!7%~YuE!8E~yuU5}0^SB3 zeN~*Q$Z4H7*Ac(|g|pU}=ep14%2A|wr#Rly{P0{r#7-5a^-sH6=D+0Uq-%-Q&;Eh>|Mly;~-j%x^USb95CSJ zQ9IU-nl4tiiILONITQKf2KzG$7rE>qWdJLIE+<~N%n}o{dZPt-&uAekxu%02%>I4v z_~td3`*6{v9*J;XaIQn5n1h|n)Y`Zt)0Y#k)z z`uV*#2H(zZ)4&wI?CN!MX9aRuHzJV&9;b&n&jCI*iS|(ycD3dgR~6o2lu!9MdKjgj z50*inV>?jfs^}`JrC~>hkBw&BcUM@|Gt^alM2so+(HP6E%=c+r#h>jr%q?^wg~XV{ z!c({dJHXA}SQ&*0@bWp%1!xHQs&59tDo1K;)1bF$dfTZ9^0Ar+I4@kYeASmVgY2;q zm=QEw*HzQ^FDTz01i!PK>#h5nm=;JHfQNLl(gkKNP*=Xa6xt=OSMt$p^Au5qKTLG> zN;qQ;f#+bX?u zui^v2N2Ncf!GY#*S-)ryZaKNs-Vr!^DJghaFSRxK%DTWgZL8T*6HDBDvJVYSiru*9 zS8&TdxgCOo96RJWQnm9mwmBx$66Up7t9C7j`$!W#)7Tmzex0`0nyvRZGq&u_Z-3BgVqwUbvRho!QBxVQ8s2WK9-R5t52N?6L(z}{d`G&3FNjDoWBp8 zlU_}m&Ppubi9lgXuBvYqIBc=%jZa^plJ$?Y%Cu%(=<*tg$FRDuOT^YMTR`IGf zl^r~79BMtu!ma@BJj5!tvNBG0-P>VkA>)PBPy)7T+Bi6tXB!#LN-_eb2RQ(&4|gu- zp7Ucm$b;|4*G_LTn^N3~)k2`LS~;ZVJ`kNsGntp3l^nTFRVKU}b^+Yh?=ZoAJ9?OM zSPu8z8hFRlcRYG8_ew{=a~$-DMWmSLu5#V5(mIs$ngMKrOm)wv75rJkHY56Fy+=Ic zbtCHyKbVS*O88s-r%n^%CeVeWARN~eDLLroMGH&aFUV`-s%kfa#PXShUmZHnI;O86 zu!&v>*aa&S5-zneg4o&EC_Vet8hS25%o4S331RYV1pbkm+RCOTt{55-W5r%XDt#xU zc-Ev-zJ2nGj`KYhOx75Xgy>)D?)(LEtdrY4sj6=wRo=e88we6Ty=}YzWA%dCAgtfO zdvo~~yJ4WpIanAQUTWGbr1DJr#a^ikgw^~MB~+Q@6CEhmynQ~n%cSjV32p*4J=oUc z>Ru7|TDVxv+y80PeHQ}-PJ3;1>1rD}w)qwKUkXN8|B)ATBwQO=x<{W7@D5@Npc^oF zC#V{*dH?bIzurB8>;C)4zYhJ^pa1iR{_*Zp&0FXNEde;5@Xz9VY$MJx_%dufabFFV zPW;n5%y&tC;yeVO_n*K0^Y{P$(|>yR|KTbB+qV!t=^R)(>~Utx_SoJS?+JwIUG1bJB-{I^9&;8G`+S+$Ve zKA~y(?e)}k=zT4itW7ikP~oOT471rMaguPYhv;K&NQ_$R2)}JWBNrnxDaDw36kYEn ze=33fqY7++QMW_WALiAjf$x&L2bhmL7!T6Cny$=JI9ppMCR2C^Dnt3 zUxr2vo?J2uNdi9+lIVb-0h-@`qZr^12-*P20X~F(ObbN5C|N+oeCCe7MkU`wSO-J? zcyFj3P{CRKX}}u9FC?gPM`I`R6Eu#b)ww4DBpacnL+l_Ug+P8lE#3Gz-G?^3y5>Ismc zy0<^DOW@H1cuIRElzWGcB1c!){zpk1M(twqOu>RU7CR|Ybs4_VwYvS#2WWvn?-<bRhC3V(CDIG{zY0QCn32<;EGWQx&sCUyr=1x-EYF$^Vx-%5CDi*6)!D~x!T_s;m~{!C=3yzHx{>B>2t7>f{0TSf}oGHyv0ISN{l@k zk!HUHr9BA9x^U>1#(>)nt3E0Qh<-vqB!DdwR?Mv&Yjy5UU_}9ZC2iRZ-W^~zTWbR) z;9{>18^fdlRJjc~Fscc}k@9VtM=S==n_;0}ULC2<+Z*K=-(Jv@7{=phv@?7&2Kivs z7pcFwD+V(hQ1QUkYeb)ejIDsUAe#yv*RBl+%!}Ur zU);TSRFm7BqTO5)(?cWYqN z=Alt8WJ;L^q=}l2c#QYng1jK)S|Hy8$2;wnDUWjn$K5jb$^4;#*PNkE-zfG|C{2ygU~dJRQ%OSG9gy{z!e{G_yM)4tf%R^<}&)p!jyZHmNm1?lKkTwDo30J?!%x zhq5JH+R|JL<2;A8A+30|<~=&C^(#`M1-+Sy_KEIrd(Oi+?fyP+f1|0<=hk!SHjL^8 zDm$pUBOqoS#MEsB?fF!`Z#{h-1c4bw+hRKns-F3e7gQZ z^5PV_C%tIdVUoauQK!sSt9ZQe>z@tdkReUq8k~-nvL){yXeYZJ_h1fMn(-AYhF8yg zOf|)8<8msYS$ABFPopvBya`uv>!41YJZ6d`HbUw_VI9Su$V>Kv>O|6qi_Tm zuHT`b;=(u=2l1Qk=MWcq^!=rGmBv0@cC#6g>0e-cBlhgho`0e7+KELxcy2UhSmTwG zNGK!sLO=lCwj$`O=nHeh`HFC|<9$Uu8W|{`CoV~DzXP8wz7UvQ+&iVF)+j-I1>uUC zhoNv1u0<`@YYRrW+#}lRi~Pr`&7BI2icx5s;ZUZ-o2=r%%%iLL-oun{QCbEj{B_YW zeled6dPCZ(X2lfdZtr|7QK>VKaQx&M)80~})TsX1vAO0InIqWc`lAOdBe8ET31jkP z0uKZA`PA8}mt?7aZy+-7N$ZCP+*h2E)yKb`3wgfgiuJ$|ZlnulVoS0bJv<0&Z+C`> z1+=K|ppj30p$U1J8{01C!~1w(r)TJK#0> zW$O#!A4goH5^5^-qbe}-Gn5~ajEc*t%8@D(|MKJQU!tM$8je`i2s#VDOhZE$5e`cG zEkO^+TB2?IDX7&Svrw7lUp_1xxSGPo#d%&rhQ|2UOK}Ecwvi4nRG<1@WrZ z>iog_E+;R_1}`4jSa)G+&uF+j*s*z$ll{MiF`A!cGAp65W1v6 z4z$y8MGtGs^I~pwj?tBgK3w2_-sp6%vHIq~>dt3Y0!AgrG4HEyM#P%kbey+r>bL4C zqn!b0_dLz(W`Spp7M3@qpNTU8F&Cckl4L7k$Bfo%zw30$?pePE>zrt_fI)%Ytn*X! z=!1{wtc$bGv0u1j*GJ#tdgj`PUX)lV;$yxHXmu;%+fbcTn5S@`o@w$wMa#Jxo>nFA z^*83k)D0l-=RZMn$WgR!r8)rZ&(=zf(h@IlAO6f3f}Ck*xB^M{Lufr+BY2}4ZO49J ztB4H8g%wU_cJ$M0MCWKZ{(h2pF~oBhjX44i(E7xN{5^Z_(`^eE=X=4jH{J6d#tTGh zuNTO^96d1*9H`4F=O&~xIaS8zs&ZL3Pz#GL+?9Z!uRxOd`DkcRCTf`|d((V-^|+9a z;?iiu_)ewFl2v+pmtqjAyZ2*#s`ex8&$Od@_U*^ob$(DzB?NXY1&ZLO4%M~0w~Upp z*ovKSjL^bD;kgtLFnQazz)DfuvL@!pNIbJ6;}>W&5#?D^u8{ zs}eI+NEQ@h=c(DwcQimm&KKKm22ZWr5p zgrSy>fDW>}L&ZMekOIdq@Q{6o-WEP_Mb4KM5}ilK=!_D%&$dc?)$C0`tR5k7Epq6{ z_I}bDkIUwsyLRoF)exHnx__31iJ8{N(CwzV+G4#~G+9gT!>tzrTq2IaT3B5(lufUq z^Ve@6YoAr^FL7D=jZ=GxQ`YdKbEL6puB*3}_y9wPya=8MN`1`A=+fDG)0@7Tb zURK2K9Ts#vWhVzgLHFY9!mJ9V1la}G)Vw7w52_Peu=DzA=o&h1bc70jK+ z)Hk{O3eVtP_nA8X@Jtr8p5uH{O)}h)rm~dKN-ctHa@4f`oafoXwV9fds>b;gSbf^M zVtGlJcXn?4xrN^3a}&waPO~uU>d{iJRYnlt`w@wQ#qN_%23b|D`}DK$l2YT78dC7} zkf}A=m<2~`cug(T@MU9UaiXldAuKkl!U5E&2)>}8fLRyo82*xxJCsAtO4cA;%qm$+ zoU$G*lAJuOaS*-uU8N$4H9muw-E&%2^g?COk-1SazWJ?i={cJmx0{awGn-D!Le;}@ zO5wJCw_lap>bmf632mMGuFYQwXwm<8L^~)(nokG0J|6_7{EDaeG;N|EI?6V0YE09s z!>jtV?2qe;n!Xi3QqG%V{xy&0v13Im++nGw%ADK*A`9@A`BfFIMo*Ki8;TsJA<*7! zlLK$mO3u(8-MV-wP0nI>^RWVd{lTRD;g}wnzDOq2tK0l0C$PE*i)LSwwLtegMQA{! z08Z=)dbFngh_>4T$2s$flO6#fU;TQ@-iBux3a89GIAzBgqeeJ-_S_`!A;<7kbf($D z0!vYUZ)u*u*;YfCJ|xMMshzwc4&zxAMLdhghg$1)_a9frYE1f)=@t|q>^d>OvdWvH z;=(uXnDKUn-n7J}D-d)<*p>8UTd{mIa|ny6s&y0W<{O9`E59!Jua>a0!`li z?a~cL&%RH$S=|R|-b{*!Kl=3YwFu)QjXo*&{(yBMis#afiUGlxq7Lbk3tyksDdKtS zmy_Ib$?rwBK>`yp8k-5dh^BqO5s#IvG}j zaS`1H{o9tsBpX#^Q!JJl6f!IX z>-b|m3$)3D&G(yH{<909C`PTdP`w^+iVMy1W)+zS9FCIdd&%gRkR7Ow#+>1thV{s( zn2{aUvgtQwj5Y)0F6Rd1%{x;s17aFJO5I%^+5F%_-vvV0b%EoxxQoy1lS~(vXX9)r zn{U4zo}8iog+>Hh24lf$`0!#bz1_enykg4g5tSwxJ3tV*veG=po!k;>Jby^FKSbJ3 z6-E_k+mh*@x^7Fm{Zvy_Y4;eXq~?*Ld}K?pA?L%ZS{*YPXg(SB_n01h1a%XJ=jy*p zuF$`$H0p=_`H|ZKZ!3HD^DqCd>RR;-r#WJm>)T>&C6W84xKIL(aiU<84M0_pORp{# z#-P@4%AjCM$@X`5X`7+$;L!0$kuJaTc74axe^?P)LpU#}Bo_MOt>wZqz zi-6T^g{igM<@>i!gZm9cW%(QPRhp9`AJ*p`PbI!#9+yB8jUP%lPqnbLn*3HOQ}dlddF`Y{ zi4GG*e6402-R|rO>H9ZX_UbaCdBageuY1{aIV`Vt>)P7;@}`{7LBf2BBzk&nuX1++ z;>YaWCWZ1cn~v)sKR1VFcb}Hc=9-TKGl-K*isme|Nj>DP^R8Pgu^OWFAF^Ftldvo0 zrHXB$qEX1eIw_qTla$#s`?ihcfaNF~0#d(+y-GL_SniqPA`a?gQ9LD&RcAlw5=uZo zXP;PQD{uJCLWD;;W(rde8_0-?I!rPyV0tMas}h`xk6)jyHyUB#L^>5j$6RwTp$ab@ zJ-Agxi@tkzXG7k(Pz#%tpjT2Gxi36@9JH6u<73cjIte75LX#RGvH7!*^$%_i@pQhqFrAT6k)_^`Fr-hLvjVv-$W_~)UR^XDbv%eSF~o-h%p5% zHRTKo<>?2#7UUE#H2%w@ObPzJF>5Vk+@@J>0%wxhbnt-XX~SZ*)F^qM#WgtvSWYgy zv>N_RDV|9fj%gc^=2sxy(3mcyZSJlg6M7POEH<_s|aoL|w-n7#@mvy^OB%VLBQ$^D%e<968Rf zn_f*)dNBSfyTbeOoH)gUJ+EWyk^0v-8y3}h^mjjnQy!XhGoDf)H@8addy0Nqak3_* zGJ@?8v#`$O=a9WphnD+VYD@Q&8QmkAMZ>feSVdlm#~eWC;@0!GMV2=o^AC~LPJ3{t zKZ|ge*K$70ncw?z@8MjcV>7yX{Z1P&!ij#3%-B>(B@CNoiCv-ahA&g5l;rQxd>eaX zR>OBA>ZriHxDiEy7v`_$X_|4rZy-ELdeCl z!^|Y4bWF`;j&4!ESG*PNTuj_}uOBwt7=;OvR=vuR?pzEW(7UfO-};_9V= z)J4l2F`gN}v2r*8p{kz=07>p)|07DTow^2TbZ)G-$v2WQrKnq2xi8mQ0>35e@RGNe zp}3zET1vETq#!T&BWo|>Lh)F5&{fHndhOz^U9m$l=I|XFVVO|QdXi(hH@^C|1Uh*w z_@-^-UffWaow2b}9P3Cee`TM0Tk7Wc4^B1B=tbhlijLI3Km2(Be#qh61g5_<)6)B< zfijB_^f((pB66`EHuFOb&y`{35suJ^xO$j=2oNBG!CYqT|L3iDQ|7Cc@n@@HwQG5p zQe6~L>-);}O63@~U-=Gem8jrqzp9J&`2++$_sYPIu4gz2Aaa;A)N2xyBI&cEtCMbI z0jmoy-}S;Q;7Mu22v+KNb!nYP&H5Wg3&1Y|4al18LN)7ir1PWhM9X+M(wfdI*(zE& zWD20{&)BmK!?IaV96=Yl!ZP;_p@uEmc>a2bcHcmhpeK7?K6TP)T|{bKYb*EkR{m&J z|4-7i(&aPLkpxTU3p`TQjLMsOe)6Ue4d6{jwPhUNfA2a15|@(xC%yS81hyFt$cjII zL`SgN>AF%9slvOh!-0W-92@Bi%&6!zP#|S?#6BPI2?0I4F zvPMwTo8CO{^g;8P01TWTy2XeVTAK^|Eas%$zrDUAc*-ExRcYk$@gX99t8Rr7Gjv>X zABcxD8hr=R5`Y>EO79HlB_JFE9iR0(o|*#9Zn+X1YRep?pBlxfoIb|J6x4oNwz(!= zXthhu3pv$c#QTSSp@dCt7(NeB^<4gVBb5Lva`s7}S)|!Q+1_2xR8t8}EecTIFCCRV z0c*}4-HH@IEhoAr0uO# zY-x=Pv;n$kG`EdFIm)mfyErBT=Vn^ysH)BK2=b3y8HJ(Pj?Hdy{&T(^s7emSCob8ja=aD-w65#vyd>*tj4_ z8#v;?A_Xw3o}$&GW_@vFjkZu}tq+xL%U#n>RxV33JA{IqhzS<*)=86oYtR<_ z@4QLS0^!CG6@2u&7$7f^;n$8}w2$ zP()l-LWTZwDEDt%a&Gca0#qyl_m#$cipcl-FDTMP_&Rotj3ojpsf<9YvWE-nAOUQ; z`f6cGX2=uu(ah?jN^g;w-?8oqItnTnb8x~PTPwk61(HHXdk)qnEhKPp2ArWyoCs;*kHP1kS zYzc3eM&GL)qMqdun&FtRK^-|*kvUmRENma`)dbi9^tyvNuYWd9ep40r>*DW+N)8Up zGYeL>a;G%x(PdYlSb*TkHd~|Tc(^yHo5Er-2FqliLRei(HLS&rW-GR7UBCHJ*(Tvm zz!dqdu}ztiOw`gLnl~mQ*h$)YzEEAwGwJh6DV*B%xO8fDXIS*?GSPFUX0sW zA-%NMT)(?(DGXkMbi_F5G@P_#P@0j`nZ`LZ-}& zD9r9EEi^r6v97Z+51v`&&i6q0hScPpX8q((Z~jJR6it@!V)jDrTJq;#o!>Y9QfJ_7 zyTQI|1o;vD224l)lM7q8HKTVXGse5=>c)l#%q1x^)yp7;x*SW?>g-wGUS~{R;h(lN zN!!WHpFTb%=X$0Nj0Kp1dEKSDlrQTd{io@H%XJ;m?(+Or4DGXX-u}hA@Wemnx`l}f zenmV?Uc556v%o}fJFP>sT^8T+iV8`|;Jz&&emZzSy>_>x35yV<=UKUgdfMf6c^$J16{U5+ysgj*^VQ2p~tEsgEx zxLir+@ggq2l3^1I^OFMsGAfDof!rF0!?U1=nF*~2pK5w`w9G6Tm>70Jood;=W}J`C ze=uCisSS8HezJYqyxm+)yi<6<>!keQ@M{M@lnrwHI;cK3U&BcWb;{40xV>U_-OcQy z+s*kEv#eVCiUB;W;<(c*@&2!8Xg;!OPhxhBI8MB=DSoaLPx`!la;?e_#kwA?i;h1t;*Q(4zh&^4$VxbmG090>_qID1$4>6Q{*sEhu$a zvjfl4V&&6d0AL;YkG$0>A$J-C4GJ{EBlT}((`^urMk`prB2eTdpxGGPJoe(NX};wb z<%jbrCTo{KAJ@ThPp8)KlBX<(91wj{JxZ$Gl08`TVIY5_p;mL&^1%PK)QFjS_`JK&b~9nX+WiH$+zAVC z7z{nh#!#2_d`TR=PeqU!3nbgpgXp$FotQ5Gf9}qT8kE9%sE8k9o1m|l)av#fmC7!5 zUO`k0K>1hI1OmHaz+rFDqxFf#4T(=?(Or&Q2hc-IoX`4M(7B+uyad64cArU7OeJ_z zRTF@x@UC%tH}(ktE8tt~`j-LJ^#VUs6ExjPboy9WH`F0to=LauDy3}HnyL0I>fQU` z-u$&7oAKldebl@dOc6VO7ub_#0sNr z@K|-Tcq{NF;SE>0W7UVc6-sN;jMpf3KuHF20arnTrFB4>y3mJ5_*Ff#9yqUXDTMz0$0W}}7nv{*s`bvlsCRjR0I*!;BaJ<^YzVTFHf+r1Y=V{Jqs6$tZcRW^6qZ^aFni{pvwt39_&ANH`K41|Xl(A#yr7;&$3HdfT zz59Hb&{TG+pZGiXcA5h&rgn?~3&DICs>cgrA0rR9837ck1d}PoVIjEl5-8$;{pLYb za)ZGs9rX=a0oseBBH|EPfU(QRY#-4Ax*uX8c^#!ViIz&zK3yYlehlO{=a?1oi{V_7 zojMk3;;u7EUyfXhuF?siWQ$9kK_dpMt&Kt~x<48qyFKSh$1JI%8GCVMy47deappGF zKbID@&ep=#cP*WxlA34zi>%V;kPTnxjJ)29OU;j8GPaw>N9U33!D!aPS_>n7`CY(? zOAcwjr^MXE+a#6EgPw|QnqAxFuQTh4s|y{+gh^?6esR9A1(=fKy?|c!8Mr9orxZLT z>-@vhuT z%7GA+ex@1I^vq%f7{(f)m0d-)L+g$5r%esDiSVxhyQxfG$BR#=YqTLzm*;l5U8$6i z3M~qXBdbvTg|V+I@imVMZ;vyC++phRSiY2M2uON$uP_2>f9MIah@pDNr|pamj6hU( zZaSeY)wvj-TM6e0E9Xkc6rr9-EZw}h@xCq;!cpdEgYoI7mwF2~!6hU&N5QYbJw2jX z%lwt118kG%Et2d=hvl^z8rx}Q1=};9w4e18x8KY4tUv5?O;UaJ@@t|eu4V8LoWMI< zNf-y2MT>QtH1k(V!Kgi%yluP}^YFVn*7GGt-BU7m&Go!E0kZNbx+e0s1ut&6SeqO% z2N}Jl;?Ml;C@H4A#z<3|3;rVFAfr6)ls?uMJH5Pr(M323Q%o>li9!vwdv<=%&gFfU zS##Z!Jh9NVG;g*`Vbk6Oka3LYt+ig!fi11m%2NBE>D9mvJotlwoLjG|h6JeHkZt2G z$H;jeX8N#vOM|#z@$0FE7q36qcJ2;iZnjXJI}kO6i@ws7IpI}&?!Z2I*oEKIGE55# zDpPH2m48~TM^7?;C!LV#hh+SVU*Ya*m`1M-yb4);%mjXxjN=*L;d7skQ+Tc6ZQ4~t zZAXQFP!SuZ-rzlLK>TVsBE_U&hWDUAC!{{?WHL;BNW?bM?sq_dtutWg>BP{~3D}0S~s+*-Tx%TvlRo?ya5IyN3dCpH1;OOptP?s9X%Py3#D<1(8GgjyW z5hQoz01=0#Y~3x(AfziE4mdl70p)3lo04}mMA{yW3zY<<3Tj@XZxqVaR@65tW0wu& zbubcuYlQW!DTTvM|FRbe(nA2vVVQ6h2CmqPn%NC(;k$4$OmvW2e1Ffd%7@s5MPb!uA4{0vxptgPwn!NYqN*rSB0WQ=>8jCn+L z4y|J&;R=fvVv5_1Vf3xMFvfXkn(kmfVEJk(S*#gFA(&GUJK*T}lGIlu`U<&8-Qs(y zR2X@)2WnyPZh8$;N`wjK1yrjKwkV>?1m3M#tvBOG5Ua5w*>db5Ey2W2>Uy#4!0L?6 zJI}f)O~3Flb!x`OQ*s(6gb}Jfzkqb*!(3 z!-^R%A%t_0p`x2nD~MEW=coS7k^8Iq`WsvO?}XeZoc=$ozfW5Hpa+|`&PDPxlervnUIMr7?MEvIw`~sOvNCC~O^@f0~0Kcp}BE&oevha*bbIMBdV&35G&KMR8c(ut= zY@nE~cxSu#nnUGkeK5~xfi_hcLP*h_{p}&;EMj)a#7G)+gDfunXQJj$0>->F`m*@T ziEW5R;fVWU;gi*wk6j<^gvCC91d#$JkJZaiZ$nX0N#+cT;Nfov1}B3_`e#rr{sHngyt0-@Nl zHtf1fQ2}X=;WtZ%qV<*lHbDtyX$dd<|C+Uh-$%N2t)9p;gJ?v+|Y*565 zu_quFzRlKnpQV`4((`kofGxnG)QQuF&|&A!NJ4?TTy6l&Mk#om`KD)!9e6~u&ZHGQ zx5OYZ0pfk3xrG};`2N+qyyZbR8sMC|t5lvhD?vw_zz5B7A z5c;bdz;EgRoYum^02&#vScqWbj6V77d+n=FL2+_iFJJeXufZ!FtA9&PucA(GpDw?Q zYqO;`Wcfyb^IDuja#Gn(>YkP-Xb(7C56=^{6qD23Tp4(iU@=CWRTn;p&RtKZ`&kKL zKLeC>49|C%KFB3Ob2bPrgfC^r9k=$l^p2H7k1(!hec-z&Y8IGkyQCCg7E9l{0Xh3I zQWk>h#yoc0Foe>uxt>0aoL{W#(;to~mSI{$S8LzLxmhwNO0>tL&tM^YA!DnEvp zyo6dt;lwl}OTPCerYPdwhj^%I(^<6)*}!#}8rMU&BbNwocvtiI?K~@Jslk+*KCoF~ zB@Ivrt{r7aLK9r2e*FZoPdRQ2V03l9haf+|0R+(HBcae%Ve8UhmV)SB9+#}+-XQ9( zvC~ehjnWOKS|@bW;|YL(f2OH{K$*EDwX=(C*!9RJ+x1}0QVDN`^;RaLG^y*a{r!v>PZb=e%7$fc*&%MPISo#^ zi=aRsCa&iVKs0t6dX+HOvl~Onkbuy>Bm5osTxO+CNLh#yHFfh+B-|7>MT@7;=-WAH z#S3CG%vw9Xy>-+pGgn+k2mftLN*Sn?xX$HjJ3|S#X8bZl#1rifuPUoWAt-!+doW2f zO67^LUme=8(}(@>9$gOE1@X-7-(88=alyzO9!^?z z;S@a1Y+0z4V4^^!xz)ZHkyHp#{tbeW|MD@ky%z`}!pe(ZDpRPhkcS!h9~h5olmJQm zl2qRS)rJP)Mydbg@X@6XKfth?DM8 zcNZb$PhSxmB_?&|fh9IGp|b6+X>VU4m|DFa%O@pVbZAXKR#2BfHlxHI9{c2mR+(!J z*Z0tzWu5Qp?^9{$UxT0{1V2FYY0LJO&Xxg2*NoTiVqStgBur9dNd73|ujys%*^i}i z@l-+>hg*iy4IJfmycQPtP7C#!BI-oMMTHDZ&sviXv1(T!li0#wY!I^O2Yzo&_q<}%#~@wDZ($+qbgatg*9-6 z<*9iN_Z>YG1Wh`rIfnACr;$LWPAi zcN1~4$+@;3@EM1u7yaHa!mc5L7lZ!?@f zF0wJYzv*n@0(qJ>1}WEWTh}ic zxRiGk4`F0bHGY7x1PZiaLFMPK=hg+zUNC9{x$&IcdP!YcX%f2BAB5?Xi}4 z%FKDDyetuuK!Q+?JMR>8uj%F)w0Mvlc>*UeG~2^fC23&E&( z6~3MDmCtOEVg{VXxzmDk`X`=Vs;Dcgf*8?!aN({Z0F3DL!{}tLqkQ$Jf{A5d8)#u6 z8PU8#EpAZ>nKchK$huqZtMhf+eY>+C(P0a_d;M{y;R?GTg>Q=^LbUB(cP`k(Sfix? z5o`@l7=&rZ(${R~Ap56kj1aR&(YAFI9rj=h$+$NtGBH%|!m1DApE z379DArF0wBe08dwSOIhZWgm(>wn2yybt~1v0y(#Gxp5~;3)_rkkIp;Lt*}AJbc1n+ z+M3{C=#P7+UHeM4A(8P@(&NYl4L;}91>HYh&pZ?iyPGiSj` zvR^5ASt^w0n2yq-(D5xVj?WIx#v>r@EpDm{Pvyl=)pd`?kyz2itg%|@ zfYQb^88{TQVdj5#F~q?zvkNW?d(p+!5zNfxBv_AkQLf1+w5AN2m8^9|9i_VRVdEnB z)3U_L?iBsBjI0vp1=kBND@coDvsA-4l>oWwc?Bv3Z+_nW6Cmp_f*#D#$0Ys7YL zdxEcFU$a9^vMCE2`-Ing)Yh}*3T18^a*%Mn^o zz~CpeUEQQ2mr}sN#SWXVw4LWy_DH(tl{uSc-I@aJIiKbcD1x<36^B40*A#zFzuWOw z{`wiOa+YBZ`l`W;yi%F6zPJ|48xHR^ze!Qkls&2kt8lEEObxJ^ud5fjsBMQTTvYXz zsY+0sh(A!U|3XTIPlsKZI7xFyBaS_NGleD&s; zb@96ye_Y48eCLWNJvU~!&CsdH--ZmfKTF0z}-XXb{9KHM<20YUzc>S7@aqc}nfgtH?>SUwsC7&6IkB zyG{jxDviqjmUye?KMRiWVL>Avoy_$?sR9Zdk$rk&E8}kqY(?I}$9t8o5PZ>?#}97V z`?!ZfnAYCB5(rn+xw{#*moh#cB7V-*ZLIabS!Pqf>3SD`4ZyygOJd8 z6CxrG;IDlCXsot#?Tgr8v-e~-H^^pkUD_|}wF2!*KJ-Tq9k7&IXyHkj35b>$SF-&A zoZtyMvil)B&Eq_gWUy=QZHT=;n)y0T=5lX^jz&PMm}uMZISc5y6!z{vY(`+?u&;*~ zNWSIXLy&p!*~z8X!`i!I?N>b+p@+UjxvX}rfps*`6Da}#6rb9$LY5S(X2CUt_r@VW z^ETMexl~C9J!JRy1{G&&fVEp;?_y-gRn@ru6&85HA?-;aVg#Z1QV<4G=xxHOQc%9H z*XqieQnFt|FCas_-dNs3o0k_g0#49_QPSm15IpX1ulspZtWAsbx%!j+rKU)taAHj|Ney9Bn_U z%{>q-i6T-K@??OZvDNXhd|jEXu0eTqNZwt(YvH1ZXoGay3G-()DUf$FhK4jD>sqxC zx%u{?79gkhqCXpPl zmCU$LP`%h`-)lz;JnI&-6H_WRH#^oJTI7W+r$pKd$z4<-?1G7cx)#wCJDYH#yvulWCPzl&q1tq#=X~{45f5%4NroXV z;$GR7y#fftB+(?507j6c*8-#hp@Y-p0l1P7{WU2i6mPXQ0K$jJ5arDh^(zNf;G$L3 zIK*#9$>vYU-tRE0dH>b8sNc~A3?%5h1Q4+3^l9(Z;iY*Otd_V-_ecJz@176lWw)l$ z|0CU)0cwCFpj%PMtSHU782b9C0r|t$`rYap?+9&gxE6l2ViJ@7Lo22aM0_smW_B;Z z|8_QoDB=f4`-jCHpu_1E2{;l4S~xe)*lk7V=C%WL=IL}W)EQpKYsvNk45e0YW@`1Z z5`gNs^os$6ioTW*hSC%4vDDtFfB;qc{n!S|y#g&PAY!}O$5_ZHR1DNN2=CvIr9Tn{ zq6n(?EisQq{Sl}O=!Z!WdM5!MuJiD3nZaHiPFl0hQ0v04f8{^T5f-`CFJJ^MpH-rf z6VW({eSUrreG`b_u2T+(J-HR^kIFm1DK=j@xz;D5L~SeLTKIdj<_b=6i|tiE{NidE zIyoQ6#d!qb@&AMBLE$ke<(%Yp^CMX2T7=@0k3JnpK=8LC0oVC}u3u+8#7ykpkzU#6 zyh4~>jRJ})xWNni16XTso{MYC%GZp=F&#lr?1tsr)7M#!AfS=!W~$-lXYTnO8m@3( z)PuRB2CsgHWFy1XYp4$%P@6UIpXE*Ow@QDCh89Vi?6k_n(my*jC)y!nYQi}UJsv@E z8#M{}3)TNQX$N7BbUFa4*rIYkx6AK$TuAr!IRxC27?wh-dVu+X$r1jL3A|V}A6_JkY{$tqlOk0J$YEAp z{Y|K?l<-(H(V6Y+E6}PJ5>e>T?wNq_FTF|HYC<4NJYwMbPIkuh6n1q<=R-7VjE=Zh zH-xco@qKx>hj)hMD zC}_N&SXXRMzn(7`r|xiSDqRuUPo3w%t|wPZi2a|X(@Vo?Q}KODr$9Exm*1T zJ(jX#;Tbo%bta($(|!$(__JT(xa^(O{#p673^sWQ3~GG_D8^oDORq4P6#WxVryWa= zo^$)palDw*Vjw~fL8s|X@IPG`RMnstdFqgqw)GfCsI2QR5UvL8O}8Iw3S3a>f$*}s z6)f8T4A+c3&1G+8{sY;~X2GUb&@+61HC#YAR3uo^i4|zwwt>qS``^;Kj7(JUkJbiw z4OXw?{@&LxKYBP6@31Oyr0U2xNQTjINGN&uSW??Dpu2%znImuMvXKC=!Eft%WzW60(Qh zf9*cbDiv$<@Y_t&5Azn{iF|?#dR~3hw1Tzjjm# zno@gn2*#-K@9>m83Tdg5j;px>Qg(~~na%w#8YaQ*1&#Kny8GF@u)J3O|8kTsZ?5Y= zbnpsfxFM)pe?$Q*_@j0IW~p-pTReSDtb8~;pVi5 zEVX5+nl6y`88T~y3`6b|!E0nW5~8>xu#>w8KJPxfmrs7GIDY8!3!~0_pyGCIt2lGl z7XrejrG|g(rvcC&_S4`{Sj)lD&9o2Y_RxX}^%Q!zz%T2}$D8ddNo3@bO*;V1{x+Z$2yQw*lk)=x8=ney?^x1Wm~3h z)WRpNU7U`j7O}8jM`F1C|egTZ$^hT*y7Gc#=8JM9aiheVv`((Mq{h;XOm ztgI4dBP2xcvj3Zg&B|5ayBY?T!aijR$afb|%WbkSrmeo= zI~~5y09>K3pn7M$r7(JG{jDJ0U}#`6{_yDh(o4TS$4su7)!#c!^_?jmb-LDBcnBFSvvV&2~WuHFJ+oqL?e7J4*Z zNrfyxmOtLBpkb>wfO_gjs7X&APu$u|26mc{(j)o^PHliD;2=Sp;9$#KIED~))c#+< ztp}UR2lsW77EMc5>@(DVCiAw=TSs;7v+8ybZ=0i?zj=ex zJ>;LQpdl}%C61jsaqN^ikVj)Xj4}Ogzf%!L0y>jQV46q4ZbCYOVkp-OT9?|tr6zrS z0HaTHL*-7zlA?gWzB|wGDd=zD4L^&57SWZzw}?{V*N%$^A(ZFJO{JFY9?aU@kKmia zfXkv((mtF$cB)@jE%2@bZXH<${mE{^<%bS1y^4_6OEnxSx5=JzI}R2rOq;OrR*1tp z*un<&{?X)>1OzDF{uWi5$4+if*VbCnBUZ_fs5t`P;JH?1Uvl!ygG|+bOw?o%M_pVJO)hmk!#7RIm`@+FEe%ME?eKxn9W}LSdJr<=;LheL zOoWaPHb%kjDVB>Ty-2Yc{Mfv=D&Bjzb0F9D{X+{c$37ZVrjwfwTy9;&>ewhC0~0$G zL*0cEOSs9aBB@3Zy5K*Ug7hCmq7BBfBmbZn@MW^ z?r8Y9D4$wg%vnK?^;Rdh%f(&%Z=0xDKFaO+@6g*((A^80Nz{_zN98!bG_cZ~ahk7V z){cwHTaCp$bqrsFbjoU!yy3kZ)%Q>nYZ>Uuz)lengV$j$1z!cna*|b>Lb#o1@XU@=5etzT&Nj1rVI}XD%ins69~Y{)NwYhJeIS z8hRi4g*R`)wiu}UbbiaK7Km~$r+qF`AJgiCtJFrooe;FG4EoA;&i#=!A%HCyap-{= zrr{p-%%OjVVA}hkaC5rMGuEXk_uhV*H-aMAz+cKT;Ckj%-osjbk z?`J4uRgJnC0i0HVO$#XRQ`fut&kI&+$cHy5LAln{@weV0nH+p|dn1l-vPB!Oj#|CE z=eGT0g({t#N2~B{M5xHkF75yg(5O!h>(agU1|!wbn@x)Wh`T+KiKT>c*u3_r z6!kQU#8opf!RQA!?TmuaHfl(Tv}uK%N``w=>vJ)nc2VJ*YZteEksbS5-{T9q5ub<& zMks^-DH*q7eXH8bY3%^jQGP6ekf^!N&!@AQIGJ*TILW-x_~};R*6t(U;v(24#L|LS z_TV?~&ZX(NWJ%;t=2^lHFtCA~t{NR`U=Mz=yfvVL5$f`Jn@FLIePjz zG5j!&N}IR(yZNE7rW6z&3SH?-LxJ9M8h8Lfx;*A z;v2-pE323#w}laQb}368VnU+x;H#>8-~YWT}ixx_=>w7k#ClZvsw)AB9_T$?!5tiOqy z44e-V>fzpHeTkm+*YARdMa~a!nH1Kcd^TIempRIiIxSXjYLx8dINAmskND0AwkvACLjU);`B;&hqT2%3QKrykRHpvs=jp}W zJjq}9^4z~k#s!@5_$Fxzr&}MG#u`i_ZDt7Md?gzdt`A4BEw)#*9)o6X^D#3f?V|a; zn@)|XH^NQ(%5@HC6Ne%YDo1~%2`j(u6T#LB-?QhmadP{2fj;-Xj$nf=#YpuzQcATCSvsfu51M`&rMyN&Q3f?UBG4;sdGJ26F=bJVYn6^*eFkis_K7v?2 zpJ2hO9$S?3KL4Do-6g*0NxawnM$bKugrOyv8MK=vN!n&(nBQDmbkoFqED1`Y()HEp zquQ`n4ak`iSS)N)@xz)IDZ4Un=GmUQd$n^m zhw+XOY@gLt($nYN@`bV4#eI(R8A6ai*S-YaYEVr z3<184jKw`cVN&vB;`{tZmUh3%a!VtcGvE%eu!Jv;DdfZO7}$5X;pRv8%C{t!90fXG zPJ-YGM{-OB{Ph?>hAQqvz>gdiM(Ny@!$a-hU zus|EXMnu8c;l%K91`aWsYu8n7KNx-eHhB4?aWA*|7*Pwl?Gq5N!w$4Y%oG<*R1;;B zcNsl9DmRy-C0;Q=k*R70I3w9%LUmC=et&RpqqerH3pEYuJV7ISu<=PtyQ*4$<9^7g zTMU(7>f3AZ#q$LhW}RX8pSD3JXhMxr_Zsf7VHHIfb=DxQ_ym=%9Mm?eY<=>m_?3^9 zCTtD&HWw;Ou{rCyFAC;}|F5nq4`(xd*ZP~^w4hc;XqA*& z+bYdaTS{a)9fP8#)~KyYL)6l-b|D!RK~=SCDN1UKWvW4~ACdD!f9JZ+IoEaaPp&Wd z^5x5$cX^)sx$pZmyYZ?yQ!Yf&FcOqQOF8yZvL;MdJMZ|C*=}J|K%|yI1Ucks!5%8G znp$op9}o4WKFefLi93mJ@6vtbQ_nk+3=f6esEKyOejf_0#T>yQ2Qb$_hn;KFA=n+WOf=oEL0)~``1LjFU-xG z|JBgzbK;BPj>;IFXXYIKo>w||w!RkSE0Grq;3BKrt)M4oX_M3#aR#_9%{9 zypyb#M;&X?XuU(|hTJC^qz4R^RRS~N$Rohmqd9SZ(v$n8v)^ABCTE`^TzE>IdO5AH z(p96x_(K^64BzA>9U0>au1;n6_{PIv9g>_3OUT?)q|!sNpEa8erL8X@Pp!{^YKIb! z(l{8@R$~-dgmPY*{`)RgYUru(qgALGk|tN92YxJl$U=XxYJ6J(nb#@qxbnm;`Ehp( z&?reJhH)BMCax6BJlP3tJGpz?*t<$e#l13VPP;7r;Hvx}lcsX#-h1e@A7_f8_12GL zpx!)OYz`+Yp5V6JCf+^;Bld$5LnS_)vROqor z>0lJVAAkJ2y$Rz^HUgAl#e}TXBLb6fvqGoWmga7f%mj6pqkERLdahw34MPL7Nyns+ zhv#^{9Q#T=#=Zhubq!5?D=Z^{S zf!Y+cl)HAM)J6yhA5UNsxV1gRXbC1`X8q@KJwu>s;M=o?Z9g=<&LqgC@oM{V(LXv? zhW0|@0tJbPISng8JX_x(fqDKN|2v1)mCL2;s<}QG$Ej4BFw3@TT(26?jDk`8YH&!p zZPj)5+Szg>m}|1gONTMb(4OO#m!I4)#35x{ROs8*~xlX zwX1U<<0Pr>i3>OwU$7qDZ3WApF~<*|Cd~~g!-1S9lR7de!8O_Ww=cZXlP;}rI)D4t z^~bVfHu9n~ZvIyu2SpR`DVrQMVeb*|)7i?Z*OALZ7>4^H#=4)!6o3}pFb>MH7b&Q{i>fZc*w7YsXKb)PdH6!SaSV8n)DDB!+Sv`$YOgr4vWfB9>EAY?@~ zo{i4Ch`>t$;u9{MWO@P6JliVp6aq8?B(Pph*)q>fK%=FSwxb9%gHTaUoFB}ls}sFW zz19^Hz?svJH^fIfxEAzvi|YuklQ6W}b4MDFbc6OlaO^Na7(TY11YnK-g17DGO8=R| zo!>8$Ba>omap!(I`_m&^9PAEcFn5=J`wl6?$1{#e-TiJ`tToX!>$30w=5?aBolD|A zUwJ!6(mj1Q3uw*d`$MKs0eP!lV3SDHY;&xv-B@iC7UrZ6jckpDeF?T#OHJzh^1k(0 zbBhPOki^CF{h+N)JbdZ~&i7m_D|*BR4ieB+#wK_Gue4gFj3#gZkOPRYw7B%$KQ7OM z2;5HaX>9EZ1Wv|UDyI{~v}Rpm$!QJ{NPwL#6{a7u&((?~23*b{L6MpS#VEA9xfU)O4 z(0yS^;any=z(h$!S>xcs+St{(q}Af0vox$XLPe27E{8i^UTX$xc-TEBm6t!3k8__A z9~LvOkM6W7bh=EM;`D+m%e1M!53J56PmM34|w6%RdcA7GYKv6D(&Yr1W%Fiio8ZxYDesv zp!@S_bGuN%`6wD^jks{BY+>yC)r}K9GZ$|e_Eegjfa7U=f1MWd%|0_=wQOl&l zKJIjwMV28pt?wi@Y}uZpFSt$- zPE}HJr12|kO<+K-&fDRk8LNJl$~5 zm-R^*$dh67B8WCc3FyTiJ;rcrp$1gI-%_z zv3Wt?wZkmj^e};pK8|oYzp*52bLe*n4t(+ITUeYyl$4G$Qd036ulJZcg{7*ifVAHF zntH6Xdasw@)#REGPn3t zHpv?)uJs*|@xVh9)dh@HoBm7G8qaH;i?h-h3o1h1e{4$8O8SMDn+`$esO6lwUtK%? z1u3JirRF(-q(12VjV1s4{poqQc*uP<4h}5q>YCbUMsW*ddpH?U^sPYKW9NkgJ}`B& zgH;~}A!r()XwX_vZP6rHQkbGqwUjX6`bYD9BM`~ZmTW+*KLO5c{j2)M`fB>hNpVXs z=ke_V7+(B#pH(E+pDCHXyMJL9#IjlGW-S=cWP;tm+2Gr99cz$gQim<(Wp&F5AEF0u zS!^WWUtnf|f+l(BR`g99O}DE7q$dF-K$9!%YtC%(V=n7&B+tg&{JzvUq?*%t>~NU$ zzLvuY#&ah(vi0Xs#}sp+KpgA4d@U+-H=VxKeJW!gaUs8t8;8Kn!8a``=w@0fP)29V z;+S<%o`irE>#iQnB*)aKFz)ECJ$i|0)nGqtMKyK0lpSPP0O?&0I>f%`x2imsEFAcU0oNQ2qH`hRBN)qKD&qQ1vV#aOLq2mc#BysVGgEdApmz#Q@D zg@Mx?d+VRIXca#R)fgOIlxO||eut9NzAZ{I5DHCX4FTs~37PV5#}2P*iQuL965aBYZOf!%o`6?mw^$90M$qM!|0nl_jFG;+XYZJ*y%K z*%T4mH4Si{I^J_y+JV-vdePvsf1ek_oI=x{8?Asq{VUk-CbwFTPzRLz6bFbieY zn3eeJ+6y^wyddfyJSmv}ZWW*;L6pT7=^we$yZ?4Zf($OcU(J0hn&=P$f>q}3GXs!N z0~NrHj|(bY*M`2~NqE{<(wzZJnpTiqI>uYBIScR%Trpl$Q&%ro7XJ!pD-m=Mi9s{* zb=ZRfueH)u+=W}h!FWaqS}$5$k8VcMRz!XhGpX7T)5^y&X-ZwR4d_flVtTf{wB9cV z7b89owi-16;*1tStmc4+p(0Uufj)&}AlTghU~qv-jOuB7A0rQ+vKiXu|Gr2J^JYEf zOfWbjDK5!vY~d#%5va?ssPy8L0eU#}yAKE8!wGlsMSl)+_lYJ{>Vhx70@3+sF4F$@ zpR~;6=cKiuwKtEEx zFMmXJJR1A8Z!xY9Hrvg%7iFG{4~^P*qm7X7Jy}^NTQ!ZYLRnR97jGX6NH_m7(b)H# zh2J^65H7A-ywO5~cAKW#3xV^?a!aY*Xu+9`)BUoxM*E4bkzKfPhu=V94Up?}?mS^q z_kjncq5-|oPU#|i#W`zHzZs^ScJ;t7D3ZS3EURa~y6E=TKVySw`E|z(xWY-)f@A4lDCfela-g|mpr>1)m!U_17d%N|w9&AX>7&p`?X`6b(P%?G{Uh4i nhT7WSw&h0ruMhZOQ4+v6qH<4S_Xn<$_iG{-rJm{1d5cH!oG4Mm65_eG;B#|1&Ai` zlY|l!TqjlC&ngP45DPdRX&l2eD%+tD+@WxPaXegiBj)8#-lFG-oo9J>KfGP1gzujA z3Iw_C@&)%M;0jYZu;k&ZP!k!a(q&-1nzy&HbxSs(5r_(*lqg)hIgtc~gkaeT3*K1C zPoX$DH^D>~H)n5e)fMM(oMcc~@y?tk!{dYZh&;wuG_!V4>#RzSe4+-N{rK$KDW8!( zKRbamM!ZZxDYAqK2x88~1nVsB|G)8?Q+8m*$j*=RJE9ihpCtBIN(Yq0}Ib zt?-VzAuB3xW#hWWls_g#a$s0*ki3Q50-$1g&39!kg7j-*?-71{$r2PE>l*#Nfch?F zGK$D5c!JF(w^R@FQkPMk{FcwP6x-Q#ws^;&|_KQwejA*@Xy(pOVNOH(+HpNI}W;Q}yV0T^%> z>EKs8d)N$Mq7D^c@QJruG@bKj*) zusb5IDPF!C{6>kE1Xd7Lg=QCOs9B}P!56FT+i~OOMeG*C?F)cmYGv+ zJ$JYl^W)BsD2Ol#F^Q=kJR9QOn%;KY_C0@he+DZco?|NSMC*j;4ASiPlb|fhZODPh z@yWI;?^F9xU_~i?VVsE~kc*`+!^Fp0!fL}Ljx3MN8#Lagh?1J6(Mw*J2hgxG=4i^T zO0231$bS_-r1qy`O1$~*VIJrLqo-sn&PsnqT|lu(Axe#|mdZGcqoY6&raD?&Q-1sf ztX`+~$RMeSTgAALM8NtA0=@DK2L}fg#}LQzLnKZ*qb381W_ux|_&lpj zp}Ft@f(7IUyMu+n8em_rr<=eA=u}#@&}wdJ#`eH!KjEMNhcJ3Ennb#LTYc!m5Zw@Q zbbT6w!mxr}DZj#l!j3{jS{>sz^)iMWh9?Hsv(!8ohYo**p zZKvad{DRbiYFfR1p?PHBmG#%|HCBy6b(l3n@H#I|#hzg8z@fv!_|SD7{2&-&8eUUN-CP{Oy! zBkmUWM4Z$p4~jHu3NMdKpx!O(tMOOhSBtOoZcjG^K4d;r-#EXe_^|07GV?HnYV`?> z6G4_1(dVaUHY+!aM9ocYFzg%p3;TX1Q7%AM*oPV>8YZ23<)*E}XQ+w=il2sY%&g3c zE-Zg}{7RrJqidwAR?b$=&#}pLQMyt-QXTS$+g+Q9&F1Gf%j_NQKJdxhsQ^2P zC0~t)wd?)v{%;2N8qZ@-P8S82)x-Innq899uEmECl)j-gi^_41jOn`gx|O;J!#B5P z7e&vty<5Mp2X1}WMsvO!XzLxPf*YN}x@7WXqTX)4hrTYt)xrf{ggzJFJbZ=Ca+>Fs zpH}L>*>{!xi2FhAZ|INrlzTb!0Dq77;BjC1DE*}MD16I%u6k2{MULo%s7v7VArXUs zL>Fs$XlCo2uO&B_qP%cO+$t2)>VICo_sE$NVCoE9O7D}H0DWC?DvkOCn`}XZ`s-e9}IiEJnRj;Vw@BL4dN?~ zMc!8KRzU}w_3ifY3;l~P_vx_e5=#B2pgz5TW}eAIMK?KSr7o^Dx7&~v{5|W*(M(sr zx>>{1^#OTM$#RLmyh#UO-R9Jkliu6*5^6f4E$c4%Y>4gG9m+afX^0i$DPt+)w8n-8 zZ`m!1aM(`24i&jt>AteQ>1sSU;Q(;pI~&3EnTv>XG)q29cBpFG?P7&_qXted4@*(L zsKht_s7v_@zNBwAf-`YC?+2mYr=P+q-+i*IU%$JM5 zg~#uv-@GXUC>;T7R4>xw_&3a!Y(F|+^^&_v>?^lsLrVR}VbV{GRE+YwpAV69a7&qf z)W;gF=oe}<)D(JZ^;UhA7?J=2mTcM<1Os~CC?1Q<;$E^@Y`gI&RhES7q94 z%=*f2kOw7I}AXv_`tN-GjPJqZ3iVH^WW?ThEJAN8dr;uXi%X!Ujj02A!(Dn~wru*7CZ* z!0p!~1V41}yuremohc6vIVhAL!Q?sldOT0EFJkq*AbFkWXFqdW>f*)f+(z>V5T1y>I*hd9^@YfZq$!VN?kA0R5Hf`~2iwA9sNpH^;U0Rs^f< zsdWLtE4z`7pRV{*PX(fLQx2lueJ}luF?IL}#xC9Sek&I)bGQ*qaXO@RJRW==+(xcn z7hN`Xd~Elwr?7d29CN!5e6OBarfSFABpp3}M0LBiF*Q~{vA&&N`4Zm!SfBnTdTqKQ z*+1|_b}V6x+*Y*ib@I6_++pPOmvFbRPQH|gH)Q3oK#>aziPWEs*0)Aqr|sA@c{? zCtu%(`||JV$0x&nGeV7vzh}NcT6sqDnvTvC6D`Nm|a z1|>nhX@9H-|3>u&rHF)s*a}beXH^T8ZlNo0sjLk3;SY@n1qY1<_3jS^{pSM(O#}t^ z4-EyS08RXVXmx0Yf6Kr?K}7(eVE-+n|EK?Z$o*-5(Esk=C5J;H{5iw=(?EqV|63Y% zyYSurreT8q@SwysB<1D*^crR^78VY!){btXu#1y_29TWO^jx8!P_X|tXsGO5f?9iDWEPT^KbZ{Ghr%gH#a8%Ha1UB zPgYMZR!0{rHV%G%el~VaHcn2KKN2ji-VSa6FBS(^>VGNu-+H7hT+Lj7PHsR)2a3OX z0iPY+-Gr&A{!a9Nj(^!{;RXDUnH*gI?baU$+5U3aI9S=){$DURpyhu9`^)(k>>t1W zHJ#93Wdd5R7A}&G_VyMIZX*8?aiM=^`oEa}VduYqpMYK#cDho)KSzdLeCL$)t;!%Al1Y2L7{?2thJrJ zt7GD?wQ##^Yb=Vl&Ic18MEw)-zw5*lF|BP;0CsN7ZXO=t$~ipYQ?^U2E!;5lyZ)Ys^SX%wfH5x=(oe=saE6v*4F{Sy#Ocv%4n`X+wf|IxT0YKT7U zwzrc#AXd1F^Q4ycY@P&KwinY0_P^^2iGmrDUBwMQet6yaA8i4XKubBZM%wb%s6hS) zeZ80!(!FrbEt&0c|3eV}*r^W7M>_@Q%n~*q#s5D>d95un20rh+A7x!nnXkA34=BS8kjDJVd6CK70 zk2SK>cWhka@NXEzke~|)e6;?Fo2d`xd4Pw_y+8o=2wM!H1b?>?AFnfsFz6o#Jw;&V zm?CX+`hRkylg6I)6JbMV9d|{fsczO>^{-l8HPk&;;QTq8y^P zZbj=x`a?o1@x4r#!9p5JI@xWC4|P@r>*eQ4CB)LAaF=BBe{&S(qg6HOgsIiMhA(+rLE35!1s?!0f85 z`>+dqkSW_h`05-D$s-NMqh{|5fIs7&LxQ(;{*ssbuR;{7#t15K=lTdsGElj-R0X;5 zN-;0Uf4CpEhj|G41^<2I=jK0&TTelR7_=H=M0~Q{jfDj4+F275WRf(jCJQVsSP;1v zD`tex&?-ti9n2NI>^EWv2XgPR452f7ckoqFurr8x;Gh48wfuMnCk*D+vBHp$GO!8>zV%Z;}}8kng^IMcvJadtqWk^?sTEP?0GM>pI>{@t&IFT z$SS=RVUvI;`#o*Hi?}{VMWphl3iNQJS2~?Ti>6mjX;=rX2UU(NPq}kC8(VB+{UpLK zMK7jwL_1P1Bf8H^f2z}g76#au(73*8M>IEgujj)fxVSk6-n?-}TGP|_vY5dgu23sj z`F>J2vV~_w0m#a4<9qC6{i?l{x-*1rGK0n5gk&ej=WVxNNh|L{^(S>schrgZ`GfSz zp7D_*UX57f!=6n!r|sA;qe$DSkV`vEh3ghV%h||Z2q!J01l9+-d|XTQ5x04rl52-r zTf3_*MfShKA>CH>n&YY0ID6*X3}G``g+P7T&3h z8eCJs?~LP(oT{{~cj=()Z-unPvRfwf5jW5G@0V8RJ&bpej+MN_AZx$*WkB+aWu zh7|VPaBxK}IwPk$!hME8L325dH!xd)NPYQF2vq02kDz@)+sPu8+Du_yw8buZu5D*Yy_O3ZR~3CQg}s}nIWKP2K)$3hTy zmynH5qc~Jj2!GZ7UIKSxky>zyf5LIE;)7%gH*=8)`FudFgzQaZ>-{3_!IK?@`&weL zh`O;{3PwAu5z5ErqWVlHOpo`Ka8rq4ce;RhcQTJD7%;Jv$W2+lZ`fnI^r!{GR(9oT zY+Xc;Ljz^o+f~+JRQy<>D`gWtJD&fsIv*D*PinPV(PEqCzo>XCyc;TzW~WFx1>)jc zaY~DngVq!{P}OkxFm<6!27%ZQC%(z}8*_25PhV4gV!To@6SVh4QLr1+s^lu9TUA0- zhj_@+Tq5lVm(Wu020e|YEoNW;t{>`!U_bphI8H^Cxk?<0!4J!aLrEQNbUK*baeD2S z{*@3O;kepwzAbGK4CA+$%fDgqAWhVRIfW)4?|yUG0;&JCknGR2T_bC@9m0pCBg0!` z=7DczQL}%G4r-mVQnKb*?BtB46lx}1lM>KzAO6hV&eC!+TZ(~kOSvBUKG3LHa;*`& zNI8uAtDT;;sBgUB42#mY30hxdba>D6VW)PWQbH@ewacoouS{m8Rn8?h4FF$)@D)wo z7cDe>DQMQ&2-hN9yBzb2_)++5q-pHc4+4qHaHR3X4^v7=v+OhlP8+4o`|1RevbXo7 z5N)J>%)q-ELhS?7dBAKXs1c?t@qr+w8~av>9;+@=yOwm~*DS3Kx3F+JPq#cZ&@$N> zush@=9%uapYo~&P;G!z7<%Z|oS6$c%%=GvOtcT*7a82@E>l`O8c>C{-xj)OYoHXv{ z9xo9Jq&}9i3Gyenb9X46^84>>7P=krC6Qm|&}Ad$BP`r@&d1OF=HKN^KyKw16ge8i z2BrqM_EoLZ-533i_<_r07vs~KdudMKrs71Y@=o&p*)QptiuT@KH}%34dL#50pl z9R?FuXYK}bI_U{%(VSPf9-uD%cP6i=F<)h~bIwOdM31;)o(Eo-A5oyGSXrN;jtOGA z(YDSh(HtFf4Hb73Qa}!S3_fdYOcM5x{*Kg}@9Xvu-RCG2Ff}#DiM3LHgo@4sPg-G= zmkriQ7vA8+DkO&3D>4)DmP9?NG&o9#-^P+ZoT$ zTj!ac^W+>9R8%l_O{=S3sta@7o045o8dkkrtJ)icb!I0ddL9{ci!F*9o=}PvGuYdK z^hv3lQDY@XQL6>!o#@gh8qOg4`(uIg&`w6;WhdKl48s-!w#^Avx>;gb7vSDUl;ZMs zHfobB-za9bN>xL0mTrdREBB4kh8Jg2tnB=PvN|Y(Cs2_vB52L^nw)}h=Nq~92DHNt z^w>oWm+)3XWmPjNEEg_IF9WF!yZn?V99r1p<$)Qn9!%+zC{|NOU>GU6md~RSf*HSO z(`+q9SyMlo$_`H6DkSIP(w(-#ZemLw!LwcamEj8Xek$C`6Me#ddrV1a(Rc6eq zuDO(2Q7bWj3N2Vn5BxT(2}8t}By{QY%}pNpknS1gBXAXp7{W&X?(?G+QN z@I+7rlUs52B@Lsnr$~580`hLy$8sF^?#m?$3`S4t8Ho|zJXc^+_@i2!r%tVMgO@vR zI;lj}(URk;gibRb4uA_+(24ul+KQW5xQ|FO+pD?!SY{-9F7?Y${7)0`-eK$-+|}O7 z_Que-j@?8yKlSa*;LL}2=na4yg&R^XJ7zoFjf3)(N8WtQyUIZ%xM7dSPfWAn@5Jnt zK$Ofc?3rcr7XF`Q$)`3AS>&b<7NsqHdaB@@@S^pOnUOO}x6bbPU@AjO=$*_rug*0; z#?_1*3$j#$d&PVR&(d& zmbb#%WNPkg%A4=ipY6zW8^tNt?6$vsBJu)B3J9p$#9FZ$zPEk!86X7y&Jzff2Uu!x z%ntOIdp;yXmOA@kolDSZw3#NPfscQ<91}S+rSenCxc9N=ke8fNe%?7SK18VSvesY@ zKYjmmgeyKPp=)?aCif=Nu!0s)97D%^mL_EH+55_k^+yoWR|W&RpEsyVyQm*0bcCG> zK7PmdJVU=~=acMjstI@bsRrEw-3r|B|d%MeL2~M#kqeyWKXbM%OQe zt{hF7Hr<~OS0xxTK>0w9{R{P-kqY_+V@F9u4=Yj>Z=C_@R9+7Tt8GpaaAY)(yTlM$ zUL+xF|14AIQgVbpN4^{NYDz@>* zMUK*kf@tzm+CBOh-mMDkQQdp>4n4lB!shooRwq@sbl(L64wVSBiH~P^?BL^HgZrFL zw{hJSoncZd^zKN~4I57OatVQ5R112BaYAUUT-)6jls95HjnI#%d3VRA^ zQ>v|^+caZ+9AJ^5KsS_C4)j(p85JoIUDwe4!NDkeVVfPO0k6)TZM6St z8tKegW`fthw|{}X1-Y1`^BWRL0N~kf%j=q7P{}je@v!X5a!Z3*yC3cuI6_Q`hNQn* z_bB4s$R!4-_@`yoCCk6KDEx9u!w2^GJv7DJx6{Q9tq?ha#ZvU3!x=#cLHK~Jgay<7 z*bK7EaF5sYNn-GcdpFW~ZwODo{r5&ELwoylFNS@Z*zW=l$~!}yLt0I3e2P1X&)*r- z+k|4k_SAq_L6_syQruNNqWgLzCc$W?&qj=b&x?x04z*mRW9kkEztrkVS{LbWa6B)Q zo);c%%q1HL(w&)ReiEW_P$Zrl@1q{+)MIUyj*oNmAlY!?xA^a!J6}*e_B%Eua9^l5 zQIlQajzeHF0+J6PuzD73Mm8{%J}9cuh1D-Q3nG7JO{&=k zd-(TDs&fCZROjUoC&3ww3u^AcdFL_OyKnnNs|R0&ET+I73)B+jj7q$e!(OLM(E_Am zcKZya{JIKu^wbQin3W@uK;wWSh1i#(J8oLT6p{MghNbMWWxQxWp<91xG@4+lie%qC zu`fL-JAB)lJV~E$=JPvP$x!0s)iJ;Xc&vm&4@ANn1=xOIh>et&=pD{-b&f$Cjos^A zhb-#0#(cWKFc)&$Tx4eme;OAwe!m3Yg40zqv8SWcCs;?&a!9lcfsz#DtQsKNL2v8od(Xm0Cxs}-cDu>mLrsk( z6oY&qRndz_+KRh{u1r4mw%Y5SV^?d~XlT>+4zQP`QRL{n{uCc>X+u#`ykcMXaJ_Nq z2(7b#v_m=Gfe%<^m|VFm=fm*(lJ;(@Qt2WT9?t^NfBhnYoQ@f>kRf8lj+#w+vA#9T zgbrI1ZvDF~a>8!T0{ue7$_LTIIeB1;P=kN-^Az&*sR#j{9M}GS)Z7*r_TU<~c>%rG zt!X0W>aLN28(U=Lt}7~ZipNrR4{f3}?3d?yoXyY#hiM&c#fAL->vcVCClOqIO95f*%)X|<0X zj>~pB2sB7bOV=#X-mI`zc;`QD2ck$1Ie3siP?*=Fpb*I?xXjzA^ZDB6p>@d7=I5C9 zGmV{{!8&M`t$yM2Y`DAa$JPzh5!He=9w(z4a#W}CH(6h9TL!^(f&qp(0Eg*(;3|QC zOQ{CmK@Z{M^bAJ}Z~>u#`ouIL>f~cOus{DcTU)KtbCgVpJ+^H=<4PY2OT zS-9MIE}o%y`E@?r+{8ijUcm9l@q-(77*>#^VG zq>EuQrGZx_e;cI1`t6d@$xyd?rU+R4V=p6`)X+zg#4gBjxMo0Nka- zM|I+HbJd()NOx9lNeL!=U{rnW@7b=s04f+pyQvw`a-?9*H;RHE8b)^VsVgwGREEw$ zOLAqObL?EEqnDPQs&bu4G9P<)<~Fo z=m{PZcsArz1S8~8@4Qc~iVVGjs`dnP3*S!Qw0NJ888^XBFHJRHeC;(HR%v- zby&F2%F0caf%?oSGYzAt?^Vy%$@9I;I|;Ifco>6FWBHU&P0xhA=IKrqb;C5fl+FRi z&Q=l#1L_vnqqq2(c$v7*&2bUznq)L}h-aByqEyO!1jo~&oz%?7i?`&;$ON2OKJ4Ot zfW7YXK{NE6JdW+)JD$;DzQYew#8RfB@9k|7s z4ZU4UO4;krPE+hhJ`pdE)nPWk>Xum3Lnq-cJ5#fdFtT5Zkdu59>jC1-$ctS$aP)dS z4>AtWt}%u*?}W4#c3pBL8Pe)6X{HlvX*ZWTJ1eJHPEnWGQ$U104r`Q|t?^^-JbvJr^?wb};kxD(yNJJ`y2O0Z94>*fk>Z6w)is4fLx3M9<87p$6}7X>3(f)`?%l z!!ZV1!F%v2AT{Y+u8VBb5YViLCY`BtxEF@gL~<{U%A&gMudE-gl#F(SphasF40P?1 zo2{p|Y|9DcTJ<~u&%VcwI9nh0s2J{a3rM7Jm5)SM!bZ;!o#)Ss#U&x@OtPihx1AW@ z8dMEpfodK%85q?^CDgT%zJ$vjzn!Mmq=MFf)Br4&9Ts95uT%&O^vufD9&KcM#U;es za8*Xh#O>2bJU6wF!8ezz8);v3GVL{E61?v((cG!&HbXICAsbKVKsY;(<%ZU2*aPq; z3O<{BsA>rPLe`PZ6{1`E2WI^bh???|{&Pgi+0?Gi;v=Y^Ov+satpxV* zYGkxz%IBwvpgxd~p?Y#RXr`U4%wXbUL_ZQMd{y4FZbX*z_Yc@JCf@7C_T#BDG4mco zH~U$It60L=0U1^tdqljEG=mN2a9bO0`5f@ImR#z5d|8}WxW)$!brS|i>?o7lsouG;?(il3rn*+u(yu5%R?PgR zaX9GDP7_lw!!(+$PW2?)q8j#yG9u=I;8mlS+urxSV1DsAiRhFGRNORms%G zp0Sl}Y~U3{d=hqzUFOg^D7}SAF^wynIcVi^jK)mV?~6eer_-8dyCUyl1~A%GI3kbQ zJ4~yt>a=b~3cLZNQk(JVCnB!HgP5$)$RL;$B@qGkm19VE10TxCTAUxP#&R!WUTM;J zolRyx4?92Fw3r<#IIo})P19b6od_X?IyuIvsZgy`=>$}&`WUi72C)6NGjD(xOg07_ z$?Z^)CaVZ9@r1=m$_6FS4opL$g-A~7BR$80F6*ET(4WiVzzdOC#nIK2af9^6r2c(Z z71EU6$$C&mn4?~`KrheEc(xqIqzh?izY6-cm69Yx<$5`F3LaW3=+6;rRqz*|DLup2rRTpO;DPz8I#=!Lk4V5?N>?VmBr~ z%t$z3H)hYIuyGPE{5Sq<(vZP@Y}6%O>`1H;;(R2^>!26Q3R_BS6scou@p**dY>;5! z`Bg~_y(`AAkGSC%ZE2P+A??~_6sj?($T(@q_L8KyeA|d$;PZPUWddNsUv>sEx@VAr zE_N5=Q1sC(Z-^G2+#V@D+QbiX^Dp-&ws6i@?+6wLM4oV82{z zwF8HPxbxPu?rl+?29h-P`d?<949n#g3kl@uXccM2a>_^hUD-~-^6QTa-&Sv{N3-di z5GNt0K|B_GEO?7IW~nZV&^OU2NVlWEO$-v$byNO@Vr8i=mPNq3kk*8!_+U zzzDkI-vvn1@78#-vYD**#I4d=O{q)#Ix4xz6H8FA%81!+Nh7flFl07RH5AotB&K8b!(U!+$8XRdXUef)2S%8#hmw3DzH2cPRk*0H%9se-yn%N;#lH<)f0hN5n zsU^q$eH1nxy}#3wHHnprq_);|DhKK~qyaB(-fE)_nD=@M`RF`=w{vxQ`mMEBa!opDSXbOvl3+r65;_h0MKFd- z2$K)e3JhE;kss!#$#8!qU8AW`yXD?U6^yB=|UaWr-_{amBd#wi+ zc3>`^XfV6j&V?1Ueqzh`6e-=B_w?T8+0Y(_3gnVH!vm5)@lo=U^xeu`b^vrr24vVM zI;pLkd@|~&x?V4LnQU_%P9gP0S5f3QzsSGz4bK8V!;(Q!`eTXEJf`BpOkL?isu|Z% z<9-*=FQ8L0!Q3|M=SC8}sx`+^<6(rWjo`CpR=bemI5vKV-%Y`qC@Pf`u%>4g$eV>3 z`l>a4l8M7j?Urz_h>$QYgs@(!b?ZJzq;=kv7SqSIG+lDcqbDeU4RB*y!H zIvo6Yo8rj|6H9)scMgbp6G!xv5mNm6`w|T6*zQb8(Us!hC!6 z-YEZ^@4TYx3c(Tf?Yo4gI*&pC5(?UL%c@kRdUa)Maz0cw2lGjc9uCz<6Cz&GZv^+`~I%0&^N+E)NUFa2^1KH8w2LR__NvvC< z6tY47(EVU7Va!K8khkS$4b27|Znuh(r1#k_D*!IygIezdpbalTgI|XBu#09j*Ftv4 zsK{QF;JxPHpWUbLb4^bHHGK)gAfq{W3=2L%^sDt4!G3R})J8d&$%xZU2LmUS{BPE7 zD*|`PnRh57Y~cvxD*)5&x#J-ukf2AP=b?>jGb1|syqL{`^{_-a)2Gx7S2E(U1Skrqq<-pGq~ zjBPe%4w!=y)A(At>oC_S_|cEz{R{>PWG2IS!7K7JEbQ z>>F=~d7Qx1D{F!0kgfB1S6}Pgt=f9uWl*^9d(x%fPDSpm(>-9W;|+gqyY+SAN9|g0 z)T32NJ0ezp>`yl;KyR)u?t##Gi0d|qCpdG9VZirhexQ^w;PDALLsSyj~yjSQG@Np{LO31Eit+a=FOf4Afy_h|wDbgfm- zfGndbub@@_LJ#nKFfD?Q$DCyqf_cdC;>S-(%Uyn1Xe6G(S)PAUBH9mb8{&u+j{Kd~ zc%r$hZTO6z_7PAN0~Os>*da~)s|%HG2eDaI-T;+_$f?a$m3IGOEzpZH)~0&3_|B2L zZ$*MzN=pJ!7!&zy-E;Xd>WQWz7_Xh$F6KTV%pU>d4dxX!45?V}fWVE6Ooj|Qoi zr{a2_UeFS6D#Ch&t%=tz9800moCWlFKQ;OWeQlS;R@ept&Cf4Ff&&irDI#@`4VDj7 zNZxHW;tuln$8&576xN9$k|Kpgl1BR6GuI`B2Dp~f?5YRJIK{mVQyw7eo3 zj5;cx29Nac`g|i@k90tjhv_H*m%&W|XOW2ngX_*1F7PgIP9hlOhUKJ@K7TVUQp^>w zaJqSZcrv`s3ZbH%puaFqh2I+|62(;Kq@(mIJ|&;?`0>zMYZmVynK68bi*%)#U~OU- zC;eU4n_R?&yFsr1OYe&TZ?-9k066DMlR>4(<7F^)ba6#qbwCU`wGj(fl%`K>v{ROG z4$HhbzMhl9+K`4DSkyLj$7!_ml4kQdhxvUF=`#%)<5n>7j*@TL$sF5US&hF&+ckL) z;)ONwF;S%1;?8-^Ff$%qYpp>koXiOpMuMAG_Ro9S@84uY_#auUnK3s9U}l{cptk{y zpfK5c9WV#(;G|K6m|79;%80m-vz$2Ip}Bqe1P?3ZZMm$>SPopN9PN|AU?~>< zV9M0~OekOqh4gw&~=Lb~fCqOHmH+#j>5U3x5#cjJk2H-v)NkQTP=&>N!fk~C* z(ve_`C3kw+RvqrAiN5?4D$=aTO_CrGypstVY1Kl;=_3h~@2Z*IOSaskgJ83OgOwuX z480RpzL!4;YY$e)_X^S>Z-EsIu=;7mpdq;)`V(z5Z4jnKYaqG(0EVI%~fSt(rQ?UIgZkI>5`S4D_IZ%g+G zbf~sruuJQmK>sg-yaGf@AoXX_+Lb`gqW3C*vNCt^_&|;_E$4 z2~=P~s`oXmPC7o7$dL>!eC+SA(I9X#D7^NN_i{7LuCZG!a-cP~eo5VD}-n8yiYumG?3>J~DX4qb}(_2O>y0dHWn)9XN{nD*`C0Pbu|W>1roiDkVqk%$d%t zQxHq+LErA(^_~k_DHWHX>->^zEnHCb#QKy3cA81qv`Ir>32}S56>XB9{D*^mTGKDz z;73cR;`Ibulq#U>(NjBkqJ2Lc{0sio+Qmrw;SSM&J4^4Ri{=Cj_dyg)(D97;CXB_| zXpwgE>&`xv-6ICCk~#3uwv=)-34Q(|z}~D}geBSOAp7v0RSOn6u#jAvv!-SyoZjJ; zp#I0Bc3K8Eg{QoAB*ZL!IO4dJbzxuiIYx*A!=jV^1}|6LUv7GEowf}c$&b;jfA`2y z0UEGEt)1nu=-OmO%(yc!^vUF6{{@dk1jK1bC2oN8Gj=7K zn*?`{$Yqf-mYp}k{X4loSxhW*lY81jx^8-`88bK8Qs#1LL#FVbKbh3$mx{LXHcfJx zcz}7Gx)67@k5M z`;f*_^q{hJIgRZ8oc5}a4z9&X^c3N^l{;G%Ad}794;S?+oR>Mv=*_yol_bq97w{H8 ziTC8z?bygB_Tsl9wEsPXmC7mq**S;fIv0Eu8O(QYKgNAf%cy_%l|Gs^!Kx`{Xq5_N zLp_>2-H9iY4WsF;l5S2dplRo#=~U;U1gc_o4St^cvxn|f7k0K#i#`~9JU@2i1f{S^_^AkJrr%9&)23PaT2 z=hjbwZ5h5T<}+Tx#wki0&YV$#N~nl3kqg|7nqsL=TahyVF^Zi6L*OwhT`FCX267@{ z&ZE2TvI=uXG{YEPN&lqma+zUh{-sgJ6q=^(Nb zp)(86ULGC`c73K!Hg7sm_4p-Jgy8?GqF{?s)8w4Am^M4hWp-^wm?ZJ|EpySCs!tDK z)>-6#_{sC8$0)mIhMXNq7INO+T1Ew17=T_K(3WxJNph^t4B47so3fLPNd&v@QiY_L z^D){cjD2pIow{W@#0jKNm7S_8CNG>U?GBN~;9f73?kV^t=Dh|V;0pLL0Di8wez z))&t{&$#+Xi{$Q;A%p92xkd|3eL5dDuAfJo*nk$i#A@$-lCn!fM1xXd(f{#4wj|B} zc#tsXdvdFGW~OXw5o>c(BuuM@cF#-zHJg+L4BHNL>^=3K1<4@|$Z4_7237cj_-f|t z6k1=k8{V!y3tu6?L(O|QlU??@NFjSUIcAt-?SQ=mMV}_z2g%s-7;`{ZRIus-FOW0c z!?#ft1$vR-P@_NJu@xO1^?hoX|6qjbfb6wGW2KS)4Q=F>L@x7ek#Gb?K;2jjOIYd> zWnPqrg25Ap5V)!aGO!IR8eo7DU)TCmjpf+PgbdX9@j}M+FhKcB zE9T8wCyiRUR7U8ZBIFkCHM zcsUaVkILR`#0r%D>NT2=gSJkDav8Idd`_ikjamzdiIS6E|uo(xiW!SFfh_ybe-2E2|~CuXS`b3UjfzcG;Q=~#aa@g{W~P+s+&d# zfZleAzRH~E2G&T8LWMEhC`XX_oG0@K97%;pTH(`{F${9k8C348i6iBEE*@B zJA$eT+k>^oH`(U9ZSic8g!>>Bu)dum%6gDnPF;3+>KywG$nE7=8oZiEAE^;6N6Hdw zgA!%wRTTH`a;^kf1|E$HdPPlyuZIW=P#H2u$O%ad)DD?e4E^ko(JF;y3{Qu|o2fOG z1fV6`4VkUag&x^j8}891_yE7fS3ij?fu~JIE2@b{K*wDP!TV`yn@QjhXjYAWu|j!@ z1?2=0L>Qcw5i5^Q`ap!)hw5Cti)@b|_Q6*ILD|y=H~ZePoU)$%(VAi==(zGy{3$Yg z@IgW869?jY!vIW3mp|I;~YO&$ziNJ zCd~`vYXiwe`i}gnG2KJLdToM^Zol7LTbW9nZ9AxZuZS$DgXWX3y#`=jQIUORFM)ou z*5o*dya4voE2DsfYkqPC_mPJX&)$|=<)d4%-Pzs*czDhIO3)wr&SU;#+JqE!ZE-jX ze${OHc)5J^Dek;%NrWLceUaO5)`gz<(s8G>i`L8{N2Jy$)$bswCS}typ9A%#REi!> z{lTf3dR6wS6eyD;=zCE`=XL>cIWMcRPGASxk^XQ!j%}^*Je>GK=)r-!N*2@d?fMHC z>K1q@pkv>v8WJ8S5L%}b5>9R9_OmA}USi4X0h%xhd;az4-QqMefC+c!@fmXw3^fr+#~sp??Kv-Q&`}4cY^Ns9zc9}=2Tsux-TgBEupKiHadVzN2FB6(xP!gpwP-0NGNxlE~G9FXgdbV$Lw|G*D z%#nFV2k?(iv zq^La_YpJ7;FF8w@!p`62>F5|}$Az{y^v+pBmxvGl7gui;R%N)ZjS3Qj?gr_BbT>$Y zbmt(XLApV@8x*7)q`L;)9n#(1-OZ0W=U!{C>-vtqgEya!``yIDu{KM+U(DA9Tu$w1 zeg@GTF>6{5b>xIUUyxIhvQBX_-0c6Dq4m4U*IO{`Bnglu&tuHZ&(ZTL9 z0&Tu242iU2>P!TWFg0SPhd`;_knMP7Zc)2l{&~;FH{I5(t#qGYg63SQw$1uFA)!={ zvUDf*>JVio@jQbFW_KD#Bu9Qxjx+32o$Ms>3@xv1Qtm~G`K}a(TmOxz7_Q84S)+3V zG4|`1w?+)q+8Y3!6(=;wpn~((dNcUB_nVZ(PmpZVj(Lx>-&%a>17Yr4jQB@|mPg+= zxNgRx=A;@0jW}rxGNh3J0!4mP>8k7VLC7Y)&j?xzjGr$tWNlh^i?ub!eDoss{l$+? z*Ch&x&s@SO3cy~9eePpz_9}wZ;qjvj89#~Btxve1!onzR)R&Hps+0&w2q;dI;c!#1 zu#M%ZDOx0Y8VK{oSkOM}54Aq`{IRy zr_v8+b3F-bv$5pod1WG$_oaR+CW(RE9B~`PUActe?Tj(makWbweoS8CT-G{-vZH9_ zc>bLqF~w9QeZUPoE*#fgF35}P;W(bhGR5CFyWm*J?=ssimxZP-BMBPl5hk@8horeS ztAEQ@O{g3v-RUPnk(s$E>_RA_C2AzuiaF~0$-2u%7*11QaR#iT_VyH<#cNJeasS=o@E5P$D;ZwM)Gr;p15TaFUMYH)Up>N;=7O2HYaWvtrzMRGE;?BzPuBX5W{^QayF$Xkyc*lvWcwrfPjXep+E4J>1a)q%7N;qU5h<+l{}F? zbZr>pL9e+LeReBu4HsVCf}C}^SeC)9zEkE_Y5)Qj8TLYB65dzP?iA;krhvyQM@j&T zo7h_T7i`ko(8=B7?4Z)GvCGM>6EN!1piq{_&Pm44f>jd-?>Y^TJ5ORr0;>!=C$cno z7ZR>e2xhLrut5s+1Zs@(hloYfEzbvfY8tp|HU;u#k?|;ms-KFWNVd}GruIK}&_~Nu zZE6|~-sIw3!2Adxaw5q3OaptcP&xcNUW~2UdHX8l({_VgoxDL}&G=3G-4J}F(ruaU#aZE&-&iAUFt>C) zYqdgGx=uMur~uH-MVaB8WXj4)EWqb@WYKrgkb+p<{OFAZC_KJ!pn`$C7nTUXgIj=v zJDn7qmN{T~OlB(v}PgAY32p6U&U`zk*+LEH5w)R*{Vh-ix0ocmXYXQHv}rVxZWtm(q=78>0CiH#Dhm zB(lgph6&7(CK6ASmi&CAxt=u3cc|>nLprqd-TSx;@wKTaHFDhm0)XLt>?9=+Uxv`XrHNOJK$*3zk@KTnS(A|27FJ}{Lrf7*v zWaL8Qp!{RXuI>UJ4%xO3dPS~kL*PyOwf^FXtd1b)G=dEpZ*y!|YTqE?-A#=@J;Ch! zy}au z<0AD6MtVoMxcT$iLm@tSc|DnY64uzcytAq`k0gn5423{VC{2)b4lp3=UJq1U{>oic zExRCEzS%Bh;>|p2q>H##fE&yq8`kY+7G8u*#rQ4|ON+s5t{8Ok!Oa%rdclq|FN!Q4 zkfk3QVCHF{l{99@fEmP8LSoOwiE&f41BzJ2{Rj-P)D)?7D^v6ei89xKc=*S?)81DdrLKXtB!BZawhayLS*ic}bQDzsoHti85B{On> zk#p+@4E~O9SD)9^L%d)uT}(X z6yfZaQ@m`m{0XBmTrIOCzb>vePU z17Dn2NroRJ%Xra5J>b%Hga3 z5&RHZf%7~6gE?pVBsA)j2(TO%$O~B5+Zpp&pqDLuBYjYxY#ON*z-VOOAey^~6ToWl zSjzSVIQuKfE9UAff&JcQDeS09|ENWUVJjDmA}+#Y&I@>-oAvcaR**)IUp=)AZ?ROT zJe2WRLL6LnG$FsqA`W%5#e}f&G(6q@BC2yHQusT-d-0zrbtvSSzB_0-6M_T>lbp}p zK1_1S1ONyn_xVQioZR=n2?FfRNIp#7+gm=G`t{EUUv#VSBICopPV!9tkTj3BwigA6 zK5uf&Qqa{k1}GH$Wt z;9`hkQtM{j+8BqFihl7Wb|j-$%6Higw>RJu_!Y?5D>{-yF1zJRjvi(HNcYVC4CxiC zPu9onfURpBenSfqL94Mw#kljcM$lo>gVf56CMdVERDR~l_9CJeoS6LjUTh+ibW{zw zA^Woi2L`XuK7-3wX*S^s4aS79vN2`8;Aq0h&Q(f+@|&aR=jnq_^cQ$DIUGTDL6n@t zpj8vMps%Sg)vg0%&=S(xllm_S)9L0^LKcd@Vw)B8xH2wHN31CSiv^Gwz-=*&Qw|bo z8u!b`rn)^eKX6Fn!$Hu8a&)^~m6wg8xQfcGS>eE@@BhixG5 zyK^KJoq@p5)S^x-VDnfNO``W7Z~Iup=bVr-UQF8EQV4womA4n$m1#5dXLEN>Vu{cy zU`%6b2XS3)KnggB-cIa*hUQ@X6A;4KV{C?rN`VyMZ$nW+eS&$H0-t|N_DHy073>=G*6B8ml(HbD023L+_O_ zZFH|qhokr;*=9Gky;c4h)8&k3QY+YXQm9RX zRe#5$xzg-aTX?`a--&?F3AHnV7=FX>&F5IOmBFpei5t=SA?(BCiVQq@YfU)M*DG9AGf9qPjNX z*dTxzMtMH~oTm5dmaXs|C;-My1RwBL=&pg*sVj6x`@XC@&;-scl}ZS5CuWJ;N4JoZ z6I$I>mP~e1IC!-96Uo}~3@%A7$55U_EJRzfgUp*ble}lpP3$`>>+>gsl$*Tr> zfg!EbJPzK1wKLY<2fO&5wl)y6G~OTGF%!#v-N5@UY?l9*k_a4Lxg8WrejRX;tDB=E@d)Hwor+W8{E3M{F^-~ugZD+r6p5(w zt=HU5(1K%HVn{jMOG7g`usvK#-RP$RIEZ;aIRZi?fVWTw9my%lVFlHXLZm@4qmX

cj;S0JY{!m6v-srN&jHu*4bCWmy}02}Rf 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

dkdpzy?_t%9G#c*I`zGF%KSU zR2eI$=b1YQT~+t%ar3%50kgpRVVbA+AsXmhV|bVQ{1x4NsiyAg0GkV9PxrMr zuZa!mq2%E6Ddk7ZS6!o55)dC5pe@7;6Y^4vV=C5e7el^^^;V01YNyr^&zM{Wzieuc zxcO&i$6qLtYCKN8!Vhl9TMNpD=^scv_;7cbr}6cc=p&b0yv3?|MV z)EkAz>V+-*k-Mold+(f&xz3`IE@7$i;;2ZQY+)<#{R7;HhxKUC3Z}Io<7dV zyG?kn^Wkj=s7KOyL4H_DYaR^FA5#q~rc6m}a{7y+b@Is$IZrxnaK%%Q`FB;4(iYbr z)JCxrFN)+t%*8DBo()Ld36mTwaLCr^!NVn7`%NS^(&I{llL1-kKbZxp`>8b5&c zlOIR&S{XXPSB1V$4#f4s_4s(obD|SjVyrKZL>vU^qD!Tt>o$P>lOEZKLbdWq_zgZC zt3|*Xbe2WDpWvHA@*FZ(DGC?982*NMW5A+}oJ#%dkBEj)4VU|tr%`h zDi*-i4!;x*ibC4^5KTBI)}U6mv~kkbdV z9tqA1Ba;NJkx&XfcfRc7x&GZ)!?E*R6>JQ|^u~VH0sfAN@fV}xP%P&yAXBF^_;Z@h zB3uhBzoDO{U;J!_nSZVEV-@B)yF!*(^RHo8Q&F$d(u_WJX6=Hw|5b`vnr0Vc?&Ycj z149{V0RUGW3jIqEmS~`2qt^R8I9O0EicKqnGfFHN(y(0w7Qu6Y1gTSnAmiL{nM}qiOf89N zT)6#!u^Ag5K%kj;sdHFmvkdoq^1nb9>5XfxGd!{FV=Q3MVT|=8gm`i^US8Pt{{4u6x{dW*^Ei@=&I^f5$xeDW?5+ySq8JpR=^c z8LZ}T;@LzE;zRK_QHC|H(n<0RrDs z5(lJB2*?aeMb?9rh*iK!z+R=}J<nvPNOFv{&#?h^w zHW^syIW=jXdqX&&aqMcngSu{7#`p1TlgIRErIUiM>9W!wlc!|!-3^v2Utl>kI|(5> zW^b1Cd7CAPnftj=(dmXB1v>XVvRH{m=!`^@MD(MmI`#lSmATqSRf)@5&+*y}FXLTs zcyG?*e*YOA8?aXe)t}AMk1!uEJ+dP3iE+j_3Y7VC0roH}ix#xxUy{x@6_MJ6tK;T# zB$R7qXZQTA&#t%32&WU5K1m}=5A1eah0V{-7us(VuQm4XhTU$8T9E^-v;I1IOog(akE)s=GvyGpHURz&;_=VTfJTi*$re7Y+=%qk`( zWcYz(qC8G*ozQ3S+I4r{ytYQQcSp05y*VpO`m@>7vmdfg0Pwzihh*Z#=_{(uC+u)!^K zUV%|usPRktwQFB8!s3m`>BBC7&O0Du_iet9ob7GkT1#^e;Wc2d;>`Goz~$NFqd2s+ zt7p?k$9uGQSU>ZHFN zp5BNUs{x(Y{oV~@`+ki-y1ua-h$(Fezi1uN`eoS2!l(F0!?WUB#M}yi8pBsCnV-Szr{I8Ef|6vO0mw?gUG_ix!hJe{BUrH zp<|c#nn+WrDGX-ShyP!+^PSGLhW_7dafc(6?$j`Q=I4VnH{KOC$zaDPtvCbg?8vGH zCbr4$0+dbPg)(hCLip_#4H5S_UPtyasKjsr0^Qlf=(42`}1dYHT{X zHp7+P#Xp{Hlp2d*z!IZSr5lh4cGcrSV)Iu@Y-pn{Hw-%e&*A|gPG@E9KOZun~iGA!BEY%5j4#!RVRMWC(Z3d?2|J)W%v^|_SMA5h$3U6;nl%NQ(D=UDn0Z`1>4RG$~jegctt_~3JsUpQes90u%~RXJZZ^m+F>mz9~W7NRPAah zbvF{$-We*9A;*e!F+E^gmE3ukVkrSGp}!w|jzl#1={oi0GTXqN`JZIiY&Oufrqso` zndT)7(XA!1Eo5HnC6JK@Xpb}N^->7FKMX-=ArlVu$ltjY6zPLYzs*Zfw}{Wsvyq8K z=N|6uOEd8~VxnX2XfdU8&H)>k+S_34C7d}ArKa>UuGH9}FOCnt>;ITQLsx-#5H9US z)D76{RW=|V-PP7WAY^7{_^V^ z67HDE0gEz!DtftG7Ct;C=NF8u%Oiw5jwm3zR$rLyIBvu(*Ze!6|B)RaKq_?+!|FmO zPFcvvxB!cyo>#$I-|kf^U)M}=B3K7!omo1RXSvl)JvxfS@NL1AUb0TxZ5@_=B&l{< z6u7%sDgC{RSkb;OKi3#!;KIsf!@q^hO)pg~C7wWRcs`*ORyQgMMk~UennyhyK&AXC zJe~WG@z)U9+R9)D2PEW^)uFi4Az)gUkz8GpY-!eSMH1FNVX{GyEhExJQIQV~ppv&0 zD~&?T9?)w$cH{yrLfC*YYx>bSF18 z4#q7y)dc4!IyxRK!6Gb7Y;-zW4Zl*Fo4H2X9t=yCIAk8qa?Ptz$Hx4sR?p@B?T-JU zfOsH((G|JV+s=C57wYbdue>M7Dl3fw15IMs;fvxcR&+olet1lJ+5{F_+aCEC4A znHJsrSqXJE8QQHF`7MHY<%(D`xzhKX~xg84@@rN2aO-UhtmVbh#q5+HFm@9BrQ z8MT%PObwE45V3MX;xNzta;cfwY2*%uXZ$FH>H?0OkN>|V|Mk9rPiPQx#U^t`=|m%= zNk5E^#c-YD^8%{=WSJb(Oa7qGv=!BVZ3H4MsE4GZ##mGDm(loIuibS=B*N&h@|vCf z%O8OvBh6NZJ>K-cI8$Ht<3EgG+DB@HJD+z3l9lO2MW^spS-@jlz}xizGnnaNDZE>1@y59%GLXOFu3-uop88iy{&%hY&*b{| zM?nDD;bDNON=2a5xZ$5R<>7$jjmI2p{GUPjnvqwVqz0!GpPfkMkY88T_TXIVI4Di_ z>2x7qkHx0~75MHTP&82R9C_7w;Bz*zf2ic>*(m{)xWrkP@wGMlLudM*x8kdTBN|jR zR^=D$Nb4@eCgaOwzEdp#|Gm5Q{o5jo8R;ZMU*|s^l|>?ya$Si?qv@;P*Iux|eqk+L z1+6!u?EBQ;Hd0)vl{nZo+I+F0Okm=9bDLV)z@n^3A}1d0T;-W!I`n_vi4FQSQVo}p zsK1i6en4d!IqJET9CbTSoa!k5TySk2ysb!**UD0^N_#S+6JSglil$-hl{MJ>dV~WV zu>v0kmqgnak)fw?ooojYlY7kkt1aXxRH+&1V7{2TG=FY3xo_9r1S|RsZAx~j=kfOP z8VbTt#AsLkcdmS}g8x!u_yP5)sd_f}@24e32wkJ^bJKJxm}UC>_+1z3Lt=9Mb*K6h z8eLZELbxUmQtvs85z+f(TRZo%CE@Mw#%vMn=d|UIc0>lMwMzHRjx>6g&yPIVW!wL| z6>$lHH+wm%N0HaPbZN0fXZ*Xg9G;M=xZyk}rS$lY(k^>8Cc>M8LZ?{1(E4sYlr6FS zX$;qcawDaf=e2>9F8e`iH^M}m-?O}2hn=^(onzMwd2d9@sFemoa+b&h_Gb%rVC_%D03@tar`-zx%t%pMAod*2=+D1Oiu(T|9tpdgk9?AEa4&3*U)b zKA0VMui60VQPg&c6QI=@5$Vb(;@XJRX3cEc#VjVa-X6<|O&6b(R!t9NrR+FbpKsqz zkK3n`C;d#-k(TzW2}egC=ByA84-8+QkiX`p()Q|);g_M|muJcTP*eboWr|ot5pnjN zitztzGn*44xlUU3IIv~Z((^LKd~`wm69+|@;o43AhVdXfeShi5HrF!;W$T#dUY!f* z@5tYv^{bL3LHP zIXuR7R*wv`w&J!S^p&_r-OcoK*%2i{=Xy`XJoNo9F8#h3RJ+DK$SUfg_N2YP69i=UuJ3jtCN1EA@KGb3;D@3F^ z#41cV^#>0!o?q9%9lG^K>hl|&uZ4I|`_4OI748TwFVL`nl^(&8ksDWvtCcTtLX@Ax z=8C?B6eE2HtRl@Psc8SRq?^fnd_~yH}D}RSkMQtjX~RN-;}N2u?M*oaF5kWD(6+%1df&8G|oke1}Hw?gCk2w8BkT<^}RB=3wrWmz}! zKWOTj3iF|TzUXEi2h#*@E(3>}@|yZ16V(aNxFi)UKb@ zGD0!U16p);*9bC(Tr*0uLy@^s--Iops)Jq8x!vzkV${)((wIlT`_(>j2~6F6iUo* zL67W9`~@u5Mk%E)iAg*>30STv*19iUAXBcPP~|CF3}vfffcNHWt;8+h*dgw+HcDxrs<4$Rb2PD|46bKtV+_S{(tQXsMw02#*JA# zJS<`HHwQAJ9Lz6fr)TwYTo{^aj{k)C$dMKebriplq^l%?&YcUQ&y6~ym`%)2*a{8h zR8~{!?(S{{xoEn-q3Y#&bIDt?Iism2Nv9^cUKYjwH>B&CN%JK1a1quZWfQw|zH%zC~T!+6;Rp8+f zi*5Ash=h$z<@t;kb?(Nnng2CkxiB*_)$FmTWUl9b_zMTt32lRmj)F{-u%$%F#RcuX zRb#Lxu~cnXxRF%o1zR2V4$3$Q&`RHk&WcV{&Ep9#bcy+hVD!<0YenF95`Jpd0Hhh_ zD?VMS$RlITs>V|AdZ$rK>XImDmz1pY^$NuQ99e6S7Uz=iVb4z&BLwvHRAn0ufr{1q z;GGUGs&Og5C};13z|!4}4H&?rzBeN0%a@{Dsxrl-Tna%6hTbJbu;FyJN<-MqUt{>} zRGx>XA;a_Io`wh@#$s%l_+1>{xM#OBUvV&UI@{P}csZ4d|3}zTAQf4LF~}2ohugnD zI-xI&k6PeU6y($hw)AsilcNb@n9YVlmCf~X0f?pa6KV3D&8NBV23eF0{A|pGWjhl1 zTCai${zd_+za)o82Q#1nx8`FtvWkvJ#YDzLDp^RP)+J1+hA#bJ5I${@Kv+IcSxg^X zThj84IH$p3iD6hRiDH~@cP^09oDi^D0tM--R!Y#^+#CyTV9mK+G9OVwa+_;d*B;+9 zy40eaNUd2hZPVRNNba3f_hVToaXS+UNne46NT9)g7PgOjp;EU}!3pr&DUtZuybk~k zAQGJ4AJQc6T|I0(Vx(PlwLLl32<>aKgo6A)Ej8*F?DMQ{C^BdPp21gB; zW2*p&M<9dt9HGX79-3vTR&ygAif=sL6}(hQ-p@+jtfWl4Rb~!btZQms%%UA*cw!>y zaM+%S7UjMSCJg}CY2e?s;>=%9q}Ha@B$QUE77!L2ffS%I2r??upjpzi|9Nk-A%!EQ z_(c`ZIY%Paj#aFuF5FQexW7zv@xo?peuVjdJ1V$|Ps}$r%TaGGF7Vi~bF@ALZs*n}4HahE0Dj_iu8rNjo`!v%{Kjz9YBqm4-0@Ocv~KGs4> zaI>%1oG{uek4&y4Dv)o_VXein=7(!wSs+&L)lC8=iE68eW3*hWF{_f+Ok#O-L|UU5 zhZ2@pzA_lQs9&fI7Nwy)x^n5SGVMY`d#IACFAgtHQZVrFiKTQLl6H+~MJ1N5<+XF8|MzARb=~S1yGZO|MbV))-`StM zxm{31S@Ux%*V4V490ZU~)`^AW%!brZ1Mh2y0#;T(+iLB4R4%<9Xi-qygbjmtm}P_zrU;EtWcv=jsX;&><}9dh@7ibeG3}yiQkHGT|9~j_!XkcZHKZvb{r;W|R*rm*=2bM0{BnXMVi@9;_xj8+AYW0+quqrvdJHEB^DMWbbcurqt&eycXKo?gk9LfEH##wprphq zvrZJ`S8Gv=?_Ac5_!mt;4zh?3RhdB9;}0~W-hGSub8HC9V7?!cHryuNe>c(B5EIOP z-iG?VqkavMO$yq^RrnKhGr=5-kyU(hi(`T3==U1CIm+Ab8l5`aY%;;o@Ny(;flSQ= zt4FiN#Nur0)%HPsy`_|MvJ2L$)N{SWIPxr&EhPYKtDb(Xl|&irgo!#Tp>is5C{K^fHG?0?-6Y|6L|H}-Le_k1gr~&`P+xDB(7H& zrc1?zQdt*4^NsRpv=TN)X?u1H;HA<3#~~?&wka1G4+LKf3=69$1-4!_SoU&Qlg$V3 z+3q=(-No0hfFAVK65=7ZC%DNQ+p#t4uUqip7wkyBS#(mv8l`W1*w1_oN3nroH}aDP z8tn!xmC6-@?&zPA;?i;7rDFkvJP6HqMYybP(rb_u9GT~VJD*}r0O=3yum@ZtODY7| ztX8q9F)`B(%98bhNf{aC4cjIBo{X^8dj9_Y1zs;|Z3$~z)tHUgt{Gi!dj|4qG!D+q zii(cI%*)Dz-D)uC+o{gcV>IknOGz6=l;_IM@G&BeKSaNfFRd6iYM%c6{{P+4S9=Ib zu?Vv{0JH`|9p+fQ)WFdkC1IhmJ-XAID|h*SJhea1&=^Sf=Z03!Oc0S?5L#%%Gt-Y0 zVfqWyLOs0#Tm23}7QWXIEj5*X0hk{v$1^!~H`1@*&8)mKPnMfCd-NHt;Dkj-1&kXS8^13n^vH`^JKT5Vls5HR4Y7RDDk7` zvWCQ2)3vw(B=P?HsBmCy1gkX1IjBe|O2P|zk7leGnc$8zLH#%zV;b-8+Qbm@0b+#* zb3$>hr&>V&NrR4-EBXAfU5S4!HLdWNcA2+yd#|Gdxv%~1q?ka{{>n%;U`>L9KB6R2 z;K}lUmQ8IEGOhLpu`L-YP7u5@0e!<)mdyqfj!44aIbjyK*H92jN)%0&a?S-2$Zm3L z?by=}Y)<}?F3ksfTWOBDwSkHg?P|+)R`8ncnnq?4ElNdN;ztW%^2|}p24b2Ir+xgV!H+soX}7NaxWj zh9>&)!8dr6E^u1KM^;#2hM3bDCA2KU+c^I)7lS3N#}Ie=+-N4AsJE&;-JKO!zTDo^ zBP(>KUWpwN|OK9>{`?LyCz22#P)pJ8nJ-tEdSA=Y$N=EAN z>LIrNSL?h#7dufkHcJx4xOku^Cc(PTnq9Io`$up1`#*Uh91cIxKpi+1b)q=ElLB8u zSTIzC5VJ{_jaIhb4}T?f-#i>JI>+&k<`i>$IWii%V6f9x>wuKPZwIjp4m*y&0k-Wa zM1CQXpe8?Zw=?Y(VH~nsO*xW%hn`HlU)@G-5Piy$e$9R+$^@XT&(9ZW>K5E^fhNb( zN&Mb3vi|A3Sn^4mv{ZJYl1jx$qPzu3fr<#O;rug6lSDSP#4C2S>vWZ}iR1J|6s38} z|NQyQWKkdUq|+5xK$*raf2Fnuzfp?1xvD}xSm0JuO%98HEz1lu9^GZ?cZYeyHXsTd z)=usGwGcr?UrAI*Ms`Zd3{AiNT7)xfmvaaT8%0SFndDSbl#0ldBj?{oHpBw^EIS!_ z?+XD8t20~&Pz-|<4hS|$^LLii>H7gaNU2cT45!>k6U<^;zML3;V|=+${&v0Gbw7J+ z<2P(;(Eot+-+d2)0t>ohHGvO;lBVi^Hxh!8U9@ZlHm57felKmIdcVa+E+=$1B~6x> zVEr4_-N-;0!6xjh`m9zsSW3#LT`ul6d*!CrK-+AvFIAk*B}vOK#Zk_hc8#b*2)i$= z%Qd`?hFEv7D}y`+*v^7}F>EI^-x7aPwu9BuIN%8s;^cHk-T*dYk$AoS6D#>wr+?pO zBrrBqF03?TOOXEQ`-TA;rxKL)cDW*Uu8BYF02Pwj=64zknM?XhBe`rPotWPj3>l$k z6n{(biu40ijH=ZtQGmBBB#V6PEQ>sM;v=%!7(UchP^J-u`T_Pi`XU2uO&LMa7%+=o zUQy223H=znr}*;`C$FLZP)k1+x>{ujQ-!AF=g*DLmj3RrMydj~y@@#Leho_H3rYsW z#C+SQDp#Y@pp5Am*P;0~4+@{gb+N@m$&F+hW*-xqxh@8ArpSH?x$$ABsl;C|JH?(x zxkCd5!qOp@%R0jI5#dyReF*;r`$z+b$VA5#`BmY0roaC2E_7GQCL!I-=Xc+^E9fT` z4-F6HR{(3svvz#dY(OrC_4EX1!o98S_LaAgL7B}Uf+?c5=2L@p0qWUuNeF!iD@7f=Ts&`^TmSV#3wphTDLy4mgNwbuAQchcv$$GC$H2i6<3 z;uung8tOr$=RZkeLE!sv2=$maNGabyk5~)J7vMcs7T{YBzr->v^fC+d~rV z76$=F7MRK)zR<{{66z#SwZZ88C{<-A#kuDL40NZq1{!>jihrX@l9(OI+6|Tjk*f+M z1m1)o!DGL*L{GDjZW1t~;7y!NfU+{z!Z%)<1kt}U3YRX+n?V3nN(a@v_X~R$lv9`> z%k!p@2prW5aic3)75T|w92f*)u3dc8?5Ui}BqbFcCwoR0fz7N`+=3Mix68fe6jx@z zt>!#WM}+do57%O8?5afQxH9pe*T+bcw0*`t69jIUSsM{NFNcI|(IzrhBr{^2St*l3 z^JUdN2oC?QMyXr_)$^ud6*Tg5*`$q3TpnGt5FqxzuR-tL4;501UuyZc0Uao)0s{k| zpyvUKPzwC5=t9!huc5EZ79&|2xBe^BzWak+F%ciUw89c~`v--SCfUw+V#zva=whZ> zryBv451xD15@tdF@$#V{(igyfrMh8c*Asy}im$$FHx*^a*bgP!a69$$m+Ul{>hVBE z8q@9EFyTEH15~XDn%6F7vUy{R*m`w}&&t1aY5|aSf)M%T3do+U@ar5ga z$Jhhel$RY(X|QW*jE8YbrqP1#)}A)=KZDe;kp|1hr}4$&oyCEX>3nU`qE#XUOW8C| zX+Tj2lT2vhl6Qn*4KiW1v8dRaG`VfL>9bW0Jj>0Wf}|1p*xB5UWv`WHd(~nlZMWN* znnm=JugF%uU6dbov3q4)IEhEKst4auP#r@PUzS7%;@8b+^K8G64B&8IAI4;wD@2kRs%kFu(vi9vfZ%L0Q-Y;H;1#s819?Mkw3{$^sBc?8oQm_ z@AtY+Z4Yw-OAS(kOh+|sj?|E*4HEcjcrF6!$>gK;xBFwqmS%*o`bqMCnm6FoS*{_bR zJ4YESqbAMd-O3F5C1O)+BxE@64Gx#`f~3;eH6zcbP`Py5hV~-}(lcXrn_=_sFWd9Y zv*JMQOT|HxOTM_i1QY^(>2NP~w95_1CX?T9Fhj2+=F$hMeHS+AMyNSW!sm%zsy*Oi zxR41kU+2df92{w_Dm_Yhj%_b+US zg9EO`KFjc}oi$&(%DPM5MXd046A(?DEeO6!WKwlk#SF$AKorV~*TnuP)u91TGMB^v zoM8A7#E2?HT>{ni8zJA38e|JMfADoZ!W&7ZZwTMxY_tg3&ztslrbr$BqTq2-xNLg} zHnFm)V8|lt5MnsLTE+3|klfY(u_TBgn0ZNs$hl{>F15=FXl)>+dH;%^9AbzFNj-|esLfw~bxs5fYX zdh3%*r!gnoov!fLl=10!Zee~2`N^}CNL%>DMF5;qHP)7(qAq6ATO(d{Cpo?vSg3q7HI3+uV$~lYq~u_qN3h9>ks#lE!QXw4h7~Gl5Z=tFcMe-J5GVNby=t+Lj{@*R6zw zdieOG+=yz$iDt2y#EGQZ?EMtoDnT6&$EM||(KQu<0J#+-a&qeU)m5%$$H}$W*>5^J z-fxENpCCYOn8M4JoT?(SjPyQXL+|tMfbgq^~CGk}LT- zTKz8at?>eU622-x+yHG~lFmn9g2*Mj(RLq2EQYGCj=N`*!;cvSC|)KZN`D_NRG~d^ zwphImX?1nG`G6$JLZ|Y*{v0~V+oQQXlxX*QtcKV~+~wTyodWX%gujxT2W)X~TN_vV zi=k{ML6VjyHy57o1?L+B=F=0Wo2RWtDz%t&objmYCYP2IUo6o20&XGCWcJCjK>I%o z7DT*8`1GthecZ#S^4PqSzWhq|AKp1%|F4hNP5{!`bh@g^-+|y&5`t1`kr= zG>4PzQ9=u41EW=}=-EOV8Kk&3>B#Cz0VFy55+=%B+(ooYL^&N?_q&F6r?_8+Hu+hH zv*g0|@aII-BcXV$qZ88Y(d)>A_MrF;=uqzVF+i<%m7dOgpU>iCxSzI1tR8VQg+A(e zpRUfxC`?D>u2^lpJQRZMaU4}yX*w4(=}Ab0mZg4awS{hOW{-uvvf5?k6{~OBXK-Hk zSb`U*Of3oM`HYK0L#@#y_Fejq_A;(>Tlmwh^AZ?nr&}tFevLz>B+`1i-6tN_?hifN zSlHef`|m!PSNLg|%Pu9iqux>}4Z_t$G{V2c&)%_Ndfd-U=ZX`%-VHn3tZP|5m>Qq_ zKl;l1vPQZ>{HR?GZZh=3!1w^VaB@0G1Tn&e9{^R9pV(NoQ<;m&@L#N(%EpGGoh(-* z8odgOTXr8pBC(2g%4du_7a7=~FT|SVAsx|`STKKtM(v^XxPI_Q)*1^m&xzJDT=cJ_ zw*Hf@8n4eL74W$k06Kd^$?H5P{5W=*~ppSMS3PJlF z!RU<51HZ~(uhONQEcb$Vhow&J7~}1(_$vj* z-Om1mjad8S{D1Oj7*L>(sXEdF-Z43UxVHV{ELsQi_@1w~hy9reN5XzRKXc)?$^) zzE~9Wo471|^^UC2!U;9KkAG6)0HCf)%FHCTgI$R&S#GlfBAKQ|NMP@47J8UF>)-cU8`g> z{gyYnYZk049$<78&udJ$j0u4g9?pq~LR0?#(<9_~kg^A&Kt!M!GC6~>lsi*S+6u35IPZJU z!^vx*ly9~S+u*ye?q|{1)}5LiudkwqtIctPuNv@6;0F*lCy$%i*t-?oUq=53hk?W$ zV?+^zVPSztfV#pC2IlR!80PZ<+u>qEy;Ld($ZYROG+)1_8U4xc%okk%3LJcbGaUE> zv*M7j_dFpov`HvC6dWZw;-e^&7yyele6Q<4-3=;3SfgMU9p;b#!*kX`cfESdx!Sd- z8@^pYh#yHN6n zLoz%1s!CP+O2fWn{l&LO&l5IRuivFL)Bq|-nf16l${h%hFar&m-aKDaCeL^dFu3P{UN@GTy*&jP=#?@QXIkI=y#2~Mi_!UPT^lW$vbAl{=6o!j!Nkkn{(Q+~{c3B4 zNKM=PXK~cNHzi8XB%mU??$toRe|}y?FwhS>4x>tr)eLPxjsPRe>yBNaKldbeL+i)( z$jgy8aDd$;E2NohtlRT`HR9;V?AyAAloZ^6G;RP{mg_no_@TVcI_}ycA=%-vzd)ut zD&$M@s;_{)#WMNW^Gk%w*5#*fp3Alv^d3#=nnHX?-Rnr^%^O;twM{m7VXdzYR005Y z@B^C}_=PkVb8Hr?p2`F5{=-zEPi<%`+ozzhM!k*;+7TPNuFchan)Pn=NP|*_uKFuz z_MY=dI;@ph!?c@wRtyO(4Yq6FzkIgg5=mOE59{n%Id2OCO{0UHko>MnXUjW2Iy%@6 zifc+E%hiRVW|r2c?Qu{!6H=qdZ-2(dGU(r~jncd+ca0HF6W^Zv-n=oQzhc~kukBFU zV7UEIOK(2Fw7a{TB9pPBX85}lSq@x+4~86!BpV{$2lS#n&PUV1E_IagWlam>EqE#y zBA#!9lwe-!Syf0S`aBre^#?hAqu~na7?1ONishwYmOl~Hn>Y_RF(UgKS+fbN)1%Y! zZM0-T-p3UT+k8z!4crtLgF9PyWTK`?00kHA5(W8^^7AwBdO;WX6y zuh&Om$wZh+6o};;nL*vdwnU+idge|%T&rs&Xj-s#wJHX8#hobfrs+ny<%2!_qv5b- zJ)8935qNa))Ec)xg=cV}_L8_$?exBEXzCJE^H)%jcc~Rl(J)mO%|ctG)n3KW6JhR< zd^A&5W?uI`-bnVm!X&ROV;TMmC8-d?F>IEqsWh+>%7)bE*I#1+O2}H?ZQ(Z7`53n& z{k(l80hcIMWRXRD+75uiuMD^~MpY({qjxUT^t~L!i_#Zlkki!kZ+xZkEBuDR^=%$H zwP7yD)4NHRB*lag3b6THW+@f{9eb@&QNK>KYh1n>KI;8uzUqChd$9^jx7lgk@ln^C zS6|m>@ca#Se~cZ^B*UF&zw;hVCw3RnB;A$+$o1?&thFKDX!#yS5jGLP1<1y0X#R`vb!5p7;yTE8$ov=0S6`_If| z3#{_^%>0tbmi@|>ERgPF_f@qYt$M^P`orB>P40GTAS@Ma%|pNNmj5<{p?)qc`i^eL-VAIuHn}{8u5jkT+DAXF5*u@d1~UWA*1@d?1d>l>~U>33WSlW zKy#yQf4h*3L<}?lQMyh1&aRx2Vw8kKNkEp4ScvR~9GK?* zgkXd(u|kkMMA^53R_drn*CmKaSTq%dIz#1U{AIx&9YpI>pDjiaY{%oV+1W_YQ0s5| z!VtyN<%kWLM_wYMAwF^q*ehJDXsHG6w^6er2Ty{%^rzDGjjcrX4!Fikuq?)&*qLqz z)a8?3i}HnmMmb2BV1xMqtir4tG56 zJ7dPz?!QDr+}kThny3fAts^EMqGEGqr!CJAm72;iJI@-NkYI>3gcM$H&08HT$zX2> z=f-EPHHK#v#^&y)HL1{57l*Vs=z5Cc%Z4r)Ux)m8@%E1Mm<_65E|jZ{u3~U8>$ZUe zt#R#;gMu|~@JEX1R$Y5W^#S#H7J-hSbFH(eL{dmM6_s zep#q^jgmr9qhrz- znhU14hOKL;X@rb(BNXWY3)A!SglKMH80;3ok8RVQZ00*?)lHJD=#q_#^~m^9>grh@ zcaU|#nx~Gcx&it-Q85#BdpajLa;)z#55jyLQ1f|-VDrjKCd|=CcWo7UQ@qJkwX#T) z0?IzzeC(oL&{onyB}ghBx(kjo$o}$Ugs?jloY<7rm($LXSp6-_b`Y1A&NV zZEnuIEiY|Zj}rV5|U$@**0uk^jL7IP6HfwqieaHaNB#4$5U1;O^wabTS_>zl@$RsE6)( z^RVZUTcJLx{JKdzHyzcue*c-X6Unb_Sc}Bn3SZp%j}47f z1NTrnph{Wf?njOs&h*>uK54*ZM>a{U2IZ~Eg#ER)n?Fdhhp{5(IV%^+ArL5h5vrN; zr~v3%?8uuP8mq*E2>5P{6&asBpr{w`VpxxO;yXnLRFrra{#yRc!Gp0v>V|y6z`wGqfBo*T+ zZ~4=g5i9f*Ns1=#534)p)xYl%gWsNBL!$S1wn}!j`-5%pAL6$dZtzORa zx3}cKB;YSMkjQ35+H5H;YU*>LfcOMuFqw@aB>Yp~U8duw_vTaCv473FgC)m1o_F~o z+02v`e}a;8rvf9HBnGP}7vjv`n+7&y3@nf9*wEi4RSU#HV)-lE<)Go9{4D-Dm|B+N zz`h!j5%H%7dB=`TEEc}6zdsb6BB0KToSeK$hz03vF)5%Dh3dD=BB1-;FgK6n*rXg{ zwbC~UAKu%T;i@xn-lzsEdRO`M8$XN2&2H=SKu zwi*uZ;+lD;U~<^Ss*YoqMVt1c@Q<>uNI^to^&Y?S3F+tZ zaV%RK1M`&s%d&8cAs*I-ax03_K6xsG4uT%C&2JtQ&EGN-`a_9+Qt9+d)fuo9laCJ% zp?g}HNE6dDP(&2;VC=DbjI^ZSFvwLMP5BVbu8?99!K}Gjo`N4kM=7N?bey- zMacs|6i%o&*gEj#3&|7ZFVomwv(#wsODE-U1T?%h&02iHVn-^7t<$?sCAp9u<7)%h z={3md9*V{Q(dh0?yx8}Yp^X(jA#H_{$bUSjXXTGLxW2jI^KxZAvO`OoVR+=LZK6Fd zKYtyQfBwfb$bs|nf>jI~RjQ52y4~$@f#;P=|1z9w14~aSXjdNPD*iDz~@!7 zeu{Efn9P2q0D;jBVpV}xvw>C1Nh+>;7Mg{_SDMPAuA^Jc7+o#eVYHF{+u2^~)gA$X za7Z;*&v;u z4PtH*Oj+)G=eCzu(ZyKZ$dovdv=-g5WCNk*GG=zQ=HKSmN-eDq!Ypg_cMhf&jP@rs zfI%TbEr_P2uOgV}TkSCJW3dgzML4yj5#6B9Y>|ao-bmOgRt)Om?i^8givMzRH_(dR zJ`eym`a){kFq)v`{hQXkt#3br?j_qd@IPKkglAnCX4W7og%JJ0aw%Dvd2#~#6&W^C zR|>`X$8CQfoP(j5v^c-U5g#1RSc7&0|;g z-i_^HWTN;7;WvE(wfQ76Ffb$>W}~-UW=j8N61m8J-|GYR@tgBm2r0AEZKUJFP8Ka| zkCuc$_%OprNUUgdCq8(eHWS~f+Q+`>i2fQqdr=#otm<6YIO;5Fi|Z)#tlo3lP7ISQ zTddc|D_F762oYbkjYk*P)MsO?mbS@^1_kPaNX*PYGJH?y+V4Oi8?mA~a@L9lT4_hc zf4!UEk`PAaD&%LNGc?W+^36T$J$U=?VFzfdXFH8=rl0G`~!GRNlY=bn<#kQzqw(&9Fr3tQ8ZljeI8%L8> z9$U^moL=EN^RLx2Wt9a_=4&4lQAXsc&M@6R~5Z#i@J0*O3a~W=PKQ>n`mUBsOX=7ztsp724?9 zD2pxVpT!(Z^QkC>vwSID_`%2tX`y<95ct)^s%+OzXnU4Mz7FnsuhiPpR0q%7D@f&j zlt21md_a+_5)QDsFzN{=mI&ODq9Lt=HUftNE@*GB*uE-8@j`|rc>=-~o#60U;Ih1L z;faK`{(yr2J0-52`X-920h-2+@MkRtUQ#Ixn2xs*YInVqT+jDO)lTZ9p=MYYBj%Nc z{U!L-7JFt~cQ^KGjRx#22T5C3x*5LCyblk5||`v=$Tnsp#dXYm?Es z0mX)a`b~XzROQ8tmpdoyRhcBzOc3SeG-}s78bWY5)si*ft?eBkoA+OT+SC8T!Vljo zpJ?-BFqmp+7z7bR!#wqmi^u*Qwgqmee|dwiCs{U{?K4oy9kh~FQ4r*1j>*&or>!>@ z=#5JAOWR8_NNMB@^1-7N)GSshwnSY4BjAxC_$4JJeRDZuYJF1zK;#{2^Jav@LBoM| zg(uay+1#qCA%M$f2o)gCEFsuk3l0hjBDlgkTr-Rfu(bu}uH z4JrysPFb1O_6}sP=&HDSTYGZzBTBB`kQ})w#$tWGYtrn=AovN6$bU%dwH>=@39Fv$raD7S&X@{1T zPDkHG>9=cwh2<7EIDFQjrsDbGyPM`hY~NX~(?t7guj)F9HqK05KD1rb2pVWulu>S) zf0$J~A3o`3pp7fy0o4`@myz`Rl!WZ2IyjngOXz5+td*K; zz`AB}Q@&eRm>{DWcB<5!qNI;~u$2LgingNv*e&)XX+;9wfSW}jv|J`=KDJ5H_AV~0K#>@H(R$5a{(s^f8*wd@pS1Q0Tj(fE?}C@V+t=XX6S;e7JaY1 z7-bnET=mCe10F8QCrI+l@H#ZBK7giLMzU+x28W`IpVZVE7FG+jZY^&RiWqFYT#Ye) zL~ou_;0q+@2yo1xsYjLJl`|a5gekv_6VmMpOQ-F6PFhzFU1dzci2N7u<1nr zDy@jX*`3Sa2Ms2WAK8+P!;CW&v9NPW+zYJ?|Ae=uHx?G6Vn-&L?8|C zzJ3zBf+n4XPP8bIkNx8dpDNr|E2M-3VJxMa@_fW>j);k5wY}8MI}csMmBo$DNdF%$ z`wsd~IAA<&_Hnnc3yfN#Cyq-;r_GDQk>iw+<$z4_jw37Ho z99~}rPx{kbog2>9tDZY*b1Y7*GmbHWyweO4Eayb%$I&XO*<-G4+i9(w&zu{o`(*Cc z+K4B#giO^_Ox+IJzZmpIJJ_mA_a-9=UQXu8Zt_;P=^?ALsgB;Fyp-9pqeu3(J1yNs z3n7!tIjXhRtG%M;wl#DElJu}cExTWN*bc&HF8Y&n^|yasCflCaoRF8Y_~oVqcf5bi zcR$B7(MO$rC^yI6aoU7VnNI5O>8_H6m?g5xvN1LMi|kz!;$3zN1(u+C8EQ#ha~`R1 zzGD`&P`v@^xf~xAAy7g#x7l8eIXOp0-xLY%tMp%WxeCjONNb(aWUMx5h2liC^&D zs}3|>qb#6k$KDGmMZGWeKmI)EOHMAp6q%dpcJ{h?f}rq*9z0fG6ylr4FJ@-u5TAKW z^9cIg>R})e{W6w6zs+bvd;=Jj+xDBD&8u6*`XzlO*gJmKwh}np?DrGy#XD zBIvjK+^9g}a?R^9?+0x+d7F}QO8=y!34ZLETL_#iu&7TYpxJ*VSi;82c1xTwC|@D* ztKC-Gi1EQ<9SG@JK0gW10 z)B>WfZut?Z)R<=6NZ6>pCx3{T#JO5%VOX?C1xuSomPG*lBFRmTj|@pe1l~IhBj0%p zz#6^!6;@qVESsKzyj!Qv>-}qAxcpdh5K&O;nHe zR=^ui6a*|)|Jr0TJ5APEduB&CN(1@sqU>M?8=cQxz8hgWnVA^rnNXw1aS&III)|bkaqr6-u`uHdO$Do>( zR7Vv_CyqXU2XqVsY@JSN91pvuWZ%&^?#Ec7t@C4lx9WWJq*r6dh~UpR8zd=E6MGuu z19L1)Aq6_T505T+(tXnIFJ`bVO9}N4N1b$r(|s@xN5|H-J!OX&v@m#ByIOa-D~?iY zD*t*w2>4*A+ZN5z0ZqO4#WRc)zU$JPT|b+Y##5I;c_Tm_7@Qmk3zF~FGls3SQ2^tO zdTZHRAbqQ<(}pw4i9I&lS|SGCY<~&0SlK4VOnG~TILZ?tA+;Fw%#ZS>8EAWy{^_s8 z()gqr*#&8=SwnErOqMR;Js1=WoRrUv!)oqSbr!H!*UaN=P|qs?Xx3vlSfq(=gEBn0 zCKzHHwsEwrEF-QAlczZ}*EQT#~wE2ZeE zf?HAcoqiVn3y(Hk5aboj)J83*P6!*?KMfdHrj>FR$oQ#U(S5oDmM0h(|&4+n(2Lgy*bp!;LiW6VEhjG&}pFN7TIGt zclaJ10#)KJ>QYVj3;*2tu;#QEoJT{Y)lywP=i+?JDeY*I1f-d1eAa_Q+qUS5$dvhU zJG&q#=riw*a`Xc?DHF$X-L$^&ogcgtVm1wEHgr(Fp&_+?5_5`u%gTiFnt6n_T1j>u1Sqe8aI6>qPB1 zW*ezz$w4gLyOY~EWqpl{=tJq-Uy|+ZY_*YTw4_9pd9?tdZ($sFI~X2cw+3%wEm>28 zhxtvvgFh|{rirsZB}cZO1c+n*XcsYO8}K!>Xv)e%Y;4{66OL)pI@AITxHd{uUJ!6z z^4e}0an)QPYF2WunzTQy?OX}E)_Ud?JuUo49iv0qbn8MHY+Z`8G*N&&upQ)xutxYm z8sk4V$6V}GA;yE9=gRxOopGR`*7zr3W<(DQlA8bZ*xH;Y`GPR_kESLVP4DrD% zLSQJmd&72m-?2(>zh){v|J6U35P#Qf+G@W!l>QD0SnKn0Zdls$sU2|i-CG`UTv=Mg zIjJ64%4fB(In^Lql1m}?B~ooLqAr-jY-}e%5)1}t*wFfXxrChBjyO1IHaFr)PcpP@ zUtzSjx9zziSJIzHL5;_IY{gtKXltS*8XRMc_o-?7*=!HJp$TwI>cZ5E@%2=B!%HE*C(tJol6|bgWcOC;N(d?I&k^ z-TU{W^%sqO1wnVWe7|MQn7?uKu(fWrpyAkTvfj=5MQi1#;qU$3oJ0b#-O1{?n1rq= z$u1Q^FDUSeLZ`J?%wKk5GUCUy&bDu3I!XRycrVyi&0AP-_#7B-E{CpCDc{i2ROkjy zyZ(p3T(f=#G2ES|*^RR>h7EYEKBk0W^ss40I-~HLp^8d1T)o zYoMC){C0IC{?tkw4Ts&=BZ@J(yo3?~^KUmE_ zQZ%^^EoL&%6@LTQQgK4c75s=q-jjj*7NO!uor#zEeVw6hN24q&zu#agX!%ryIB6lA zUNPJ;tuxqai3GK1V%)A6Qbab>S9#^ycWjUCad)ChuNxf2TE}oX`m3uWuc~uc3+>0F z$iMsje`Dkln*_*u0DIJJ1R^ATF)vL;17cgQe5OTbbLG4-w=OE zWrnvriBRh{L&2ia3YeS2iooFqQdP9}tHoNX)x#`oI8f|ox`3ax-4p&f+twv+_GSQ8 z7f055xwiBrUyIBKQvbR8)f$blYcjB*Bp0Eo*IrHk2LU{ z)_~wZpZX3E005sQgJ?Th8L`4l4 z*KE)OUJhc9w)IOUZKqOM_ZErwf(Eq&%}lT(CAHt65EW<(dUEf3-?5)PUZ4JrWjEda zN}8OX51gG<@^~C~ZrXlYiAV|!YC2s$II=1*ktw75e;4chI!W;bGzr3~1-+Xh94YYG zYH}AaOLA&T0cB;>5_23OOxiSuU^tkFB>1i?Z9> zhXp|?>69KqkOpa_q&ozL?#>bE?hfhhQgXx$g^A(|XThA?cctgdc`07f0RKWyqL;im%vgF=$*O7|0uHUdlT~pmo>t!pouN#wBu ziQtXd8}p)tBpa1g{gRJPPq;SoSD^}f)w6_R2$t5}$OR8Q;rHp6mcPg0mdXy`GP`a( zJx@XF)X@2`-201@*33pHRkz80dJ{8jto6!WC=l%R^V4El6 z_HbIX@i{iErpF^lRW`*Uk@@4>tmoQXt)P_ zbFl2PF~6hF>Q6}QdAWQ|$Zv*LG|U562uSIb49?AKk}!harX7q9(|7Gn(r!dA+IPm$ z$U5A%M6iBcVzWr-L3UoZJv+Y=3qW#{yXlatlx=@RBlmnjR><;zCv&}FTk>oLF4LfW zI!i>2tczv< zHcwmRnQQ?vP5HS4d<1pFw5btIMGG#UR{uFBU^-q$o$SA0{R99lqf1~Vd;oJ}U~XY3 z);PzL8}OR2%t4CSUK2KmM%RP~`IO`>YKCys;>VD_ zAdA+zbQXvjooOok11VU85r4}qBzK*Nv#{YxDJo8veZPlSSbPr~Mo$oSe^@j#Ud6MN zQpwz`y{#PDuvx7<+1c&fq7g5Iu_`cmyWAM&#ABZv%i>d2F& zUMYh~h6WyYCOHZhH9*OT3@9e)hY-6@OZxBd4+~UiMoDs?X&X1ID^OfZC?zj z?eTgQCDDx*0z|R>21WVXHzT+0h&`yB#K5*ds$^^G>m5~9jhNaL2hS=4UI`3x0ANYp zpr!FuYNhqMf5yyLJ)^qWsU=1q1Rx-@xHvg9LLPGcJ(<;N10<|R>wMTB+zxC2qsfp` z12*`my5zGR+4mdD`>MBwFyCVPtD4Jw0H3-VxAwBf2Kk#xO7f@udbn!jA@Jtr#nY#;iq0KcEMz<-iHs)bB4k5GcktFChiaAl1ztZP;^N{}}2 z!O|=7JEhzkQ#BD}_lv%Au6?v|z&_d|V;^V(J(q)#QzW8vuNnK(psKAXTQs<$4)Lk$FY zWaVrvZF@I*hsqGpHu46G`DTen7R4I}GQSRqh(PFPIIrA}~Dm0IuR&cGO=uB@hO zg)?u`TY|8(1@|4c19kEA0@OMurx{R~JamF~*0MrGtltP6%+U+79~caO^X_^nW2oDm z04{D`-BNjOVa9IT@?Uq&U&JDeTsyFkr#ACahu^_Vn8AB9(jn)iwa*_+C(J_IA&K+8 z=W>I%L{$S`k`YeUiKDA8=P4|Shbq4CR9!nUsgXo03`^hj-=AIYvaHKcyl?BmPB*BekstPC<|ipy*MP4zYNAEg2bpmp{j`p<=;t*v z235e`WqR;BbK@@~9cs2^6&01!?!m;86*E)ZLW;eEgD6UD>!WNRpFT9l#}j7Gvn~Aa zIp+*uE7oP^wP0w&^JSdZ+puY8!D=r1PTvFzsxGj99>ob$P~Dp#QzWc5wYoP_Vdzd* zHQYRs_hjc3^a1SjIA%?Pk)M3kT*EgL~VQ6gGVN`=glf!!Y|8g#{<&{g>i4^Nv_i$-S zvUv9jys`6=|Ec79VOry)g@Df*_Hd(d;b&HVQR27QfpCxUm~`OjI~j0L5zN^Lg8uH}@VRFg6*%#fZlln@ z6eq3{HR_R1`_M^k?|Q$C5zFdY+xYcMq3-;$&r^PnsRzj)CfWP3^g82I_QY*Kp`MH{X7fYmX6f^6^GH{M=@XLA|40ClfBoBiS?a@+j> z3|$8H2o{CpB)BGBVy7QXg?%(V5YstR9z9wb*lP9uK(p~{B%lw(`DttFx482qgK~#~pq;J!2B4JRk=!11XV$o?b;oxk@4YP*Z;!gSZgwubZ8@DjWorkQi zAKNh|TO9QcXSZRGXM@J+NDx7*v(QkxdNs8OBvD+|6o_v{oEZtb0}|}MLg5{HMdMUZ zJaY!3IE_Ln5Q45#k(rV5{SZpcUin;18^~Z~b?7>BM+PCv|lWMi<=3x5&6E;aPSH1z+%%Fc4`Ro{3;LF6HgvK~zWU3%~?tu@UXn>QP2@7tNH-AnjdcVZ9p zWYt-sa@nm|bx8&W1@o0pUfX`K{p*>4$+A5Lg)F+!dVl;el4Niw^thkj5(>$QRWtG4 zh7xd#>eJ%J@--ou6%-G&L6}3=`hWoK27Ad0B$H3C-UxsGToB4WavINLBIlv?S%(@v zqMaJWVC^Xr`=AV4yWTWCNLS_yJb()D%QI<9$IXqVKCCn^1Oa8+KUUE|yH}Tu_WfWe zsL@TFR$4feRB<*S>^&N$jCsnkMQBruRRQS5aW)u zVqzsl!j9jtBtS(;Pd)m2H7#a$iaJ$NNP32qL_^P= z`027wI~0GYQC{06$LC7{QCod_wM+_vYKwtB{3I(UJ%_d*U*Sw!v^1J-O!M(vILW%0nh{Op%Boy5-%yO_|7@wL{g<& z_Pd1!3`gKsdzUwziM2O8@BWNh{sO1lKdNrkW0jeE08`>kDAf2us5^3RGO~Hk7DK?# z4=~EfU!B0-Ocyi)R^>QdNzaB8d6J66fW_O&IIb3Bu?9-w(TXcu?)-i^IzW93q;8<3 z3`Ux&1X!cAQVdY)R44{tc-c^GuZvJrLhjl1j4$1{A{zN6dT5?d?ruT_aOiEvrm zhyC2hB%(B`47!FPk;H!F4JO8&+@*f3LSX^PhCM(WTrK?TPkrb;eWY%{T4#>9e9??E zB+GK>aK!}j_W%_zXi|bBUO3E9)$*O&uA!YfY^(Z@ihQ$&aO2BN$@R(`&bu$XO_{91 zG(ud^=OC)iLto4D@hjtk3T#`;&v?;(2b=tF+{HM-peY5KG@@e)*IF&kj)ckVD!2qgKyY~8b4TnP;Wg*BV&k5uf4P>q<&&DrR-D#v2Hs=Hymdh~wz$)=R;7^uP4w z?ni{O_RmwCb=OO5ncuN@qYy~Cq~d|{YSQq21|Y=W=O_uTy7O_ob#KGsTKq(Qr&E&= z2H}T))u~l>HI(Dw@5?w0XWe8#C^JDT(hGds^bsiDHr!LqB)^19-{KT{V~#8|qoM2a z^>7Rk5O7yxtIyc-WCrRkM3u5MeG{E2(5YkAKKB7lA(9ZGWmYR7D=7DPphb;=Q#;y_LS=KiDf;$p9@8o=xx zxG=ki35^2;ssF7UzQLoCs%iiYs<-fiFEEmvUk01ACqD-ww?vjMhUUmo@qPL-3NYbD zLCMnejd+jFiF0XzFbD(V1Lf49SE9*42)-^G$e#TX+)vvWoHtU%rz> z5Q!c1awLA9ZTk3i^8No2nFiTN4uyv0n{!d^tvc6EteK5} z9Y7t*GQP*RX`GjIt+(#;GHXiMRlV$v=<|mDzNa@xRIa#qcoFF)tatDFa?5_aa=N?> z4GWXdk^yYw1E@>9tEEensKfA6OeaUv2EfD4$(`j0XbQ5tnum?e9)vEl1%U|jNtHYV zreW!1*!E@BO%hA&#F&&}q=0VGD8!ZAU!an{?aNfO!Y}Yz2n!ajnQhpwS>%@3Q(OFG zXoI+D8FGe-N_Tt2LB^^PSnE8B%uxZV!9n2Nsb*o z^uvpRB3~G@VvNa0MKyG^-Ym?5c*e?iRQ|(jjVB{J=)wjqlNnvL;(8XM zkKXo@*7tsk?7%NMjQExto325JOqw^`Rh66Xf!e(F?S9mn?)#6R7>MRw*lv=pdeGe3 z3#>!~qslUEupI5G5i^CL*N6J2exOBc!7J4ae9*RV1A&X1N-NA6wQbRt^4$VASVykr z)LUtM=*}?W5RXc_I@LYAYQQn%_C6&Lsn6oGoTj#m4*ktjm8567(1{_im;Ry zKdJLN~PYg>hAJgkCdC5!AJMv+G zRkjmcn}-r`_s&Q=Ix)(odypJ-pV_c>qsbAQo*p8Z?G1PRaI0RX(}>cr*9kQu{u3Iqxk~%O)jJ#yf zaPId)qBHDBx5;|aFEI<(u8%sQ_Kb{Bmgsa}Hv-+zj0_ST(13hDVZ2#&9vKqxQR#nx zy)g2_S9qP*wY488S$j}0&L=&N+i0L=%meXrt!-xI8t)DwypNMETiDvUhnN>nM4m{t z_~cd&h2)r6kUSJejair*lQ>F+vlhY5jxCy*^OJ?yvK)2`UQM;Jq}HL#lB6R?o#nd! zs4cNCqC{>Q_S$g0St7I00Eg4uD2e?bfC~ep zZt2m3epwwTX{uUh#i{6Qz75xR_*z&So><~{Hd2sIkGr|6HOJifxZE3+Sb^>ocj))L z3y=my19DmwI5JH>_Il&q?%pkQot3S-_cD}ETGS=AGQU<7xZ}o}spve-4LFNQ&Vm-4 z8CMx@0d$;>@E~Ww2QW4f;U=PEUWgvf(M$o_6DBt|&OEQ!8?K-u67X`@f8TV4KJu(> z>O;eNAa4yDjGgEX*dW$bp63$N(e+b_Bo{(|H zxsU;8@SC~5&GP(BIh@9e(@8n}L)%UeV|nqQ?f(ez@q47xToN3vDQO$Mr>m0@t+s33 z+)?1){lW)fiqq*CV#xwq{8wTfTkDj)Y{|Nl5vXk8(y+RDiL)qwLUDFMCIdr$Ne0zV z;v+qHAmvfMWms;sz`>O*v)q84n7!fZZn>e`AOm~P`%Fp5&KG%C#xpZ~;&DB9g zB8tQ=s~#==!%J=N8}xwwu_k6^WuFSIxO#!R zD^vdvDx|}!xRgNU&@JDvHXupXp#*NTj&*hVAN;(c=xfb*A*?Wi5RI6s^Z8oM@T6U# z75>jr)6s^xrVqI{lWkzhx7Pl9_yqVy*wt&(P7Oxt)i?BgoNgxDg8K!utG1zap@cl} zOnt2oj3uhpKLf!(<3k5#)r^bh@Q?opy;8obp;lPM*@%C>Aj|^jKrE**smrY@K~hsw zE5T1*$=fg*c%}VDNXQ2qcfqcKi-}3c_M5R`0!9|wmMxBF#qb}uVKl6K$^TuRRDkEk zb6bED%g%~utjMDp$59(*BS|^x|7GPn2WU3aCvMoBvVWmUSx053OL9po6)E5Zcm4|v zoTR23@8IB|G6i@mt;Yx*3(j@#wzzkL39UH8n~S}{j7la>di-sST_F7Q7+q#yFy5Wunj7|?aSaLe4W~5w4>4i>ix&Ne zk{Mcj+oPMCi^}o2DFh613=>mR%FhdlYM%j0V*Gsw1vI1_o7HHfd_6!B{A4x2((I4` zDLdZ(nEC!4E_MSDEPm&R>X5g9-KGC9rnc~3PYmQ*VI(q@o?Cc?mAKj)fGGS((EaUQ z8ll^}OhHm|7GM~|Vb0dhX!rZz)y zaqKS+#Fg@qxNWZnZmuq3s${AwvmCle;#+5Ab(FOWg>cMVLuIWKfjex(N^-s|3cR4` z$SNG=?^IaSVWhy6@Ab7s%Iu|PRJ`<9EG;+K>dZQHXX|~r%Ge$j|6gwr?AOb~C*`-n z{;G@PeKmT&?Bo~i`WfJ1^pnpzind-Oq$?NX6*WKEM<*u-nPp`;^z9k!h{wjf@S9@t zS~_hOS(J^KpfHjz)_>fLDR`uBTQPy@LJMM`xYrC`tUYp-BUB3FD{N7LO$fY!>EFsBvIsRhJ zG6cI`ms$QOoUG_@Vx}%}Xq}Aj^669U*BaU8Zhu&RU$k~wlCd^Kb$6laL7OVW)U2X= zX`bWgIEDTGgKIM?I0qN&oH{xk*aZG1`_NGPVJPTw==LPl_$M2i*-yEjkppjeFtmj? z1yZn*!J9IUvb|r&NOPZ7LXpn7;Yk-?-@9#Qa42-ApR~lND)JI>M3Y7brKbNB0*EcC zi;w@0ghhgf;M{EISUwljv!d?WT)MiM)0`oW@psLkzk$0Y-XBLR%ZDi$Fw^MnR0vw| z=<;a!@;Nwt?qoE$nZ#iRV&zd$uG5eiHPT6DogwdjADk#%mkpN4Kc!zt21{JvCMzce zMc~VtO%vUWX`ih9II!y~*7pvm+-Z+c-5z~qRUa0+eJoc}JO`ZNdVW_ed zt5V>aT7m!aGc0XUPwnXdl_Vd5UN#7XcEC}|>0g;Q?pH5j5)ee-(9n{^GfYXLL1<)? zr3D&4sGd;R)sI9hp)}RGhtueom;{<=zY#a^B_Fij@6nyMGFyGAwXB@NKu0g>jDJIXJeYTS@ZUr7Pjtj2IU=WWK|| zZyf2KNx!sG155PPIG5?2Tk!!$nvPP~%0al&;~4trw)v}SdovkJ-fX7a#?t*UJ`7Pg0j!B2`m! zEp6BfiVvQi`H#P$4+8`GIH`)OJ{z^U!NO9cDIRwpdc-8x?+b+t9MuIC()h8`>vvLy zzYA1agOY&`g`Ah6c!o?Js|1S|5m> zZR=7?V<^OtRP`V8(;t1^4`#B0rLmJ}5rV}?YU@y;j4b_0bt#M;gj6$iJToT4Bo-*s zyz^$N_|}@8d{DS%(t|nSXnqskdtctc?U@v3A>KotprK&PgNd$;KC;=*eD@BOL($)7 z2>u@G7bmM;!ks7Lv~|Q8SwCQ5Y==K#Z2Lc9Y@G&9*#_P9-RcLBomRPXT5k%fm1OJ3 z;F?BDqZ3}MOa>lFAIUnbYltm|5ymPskVg++oXw299Cvm+W$i^x%2pwP+AO7*+=OSV zKol#q2buVbtnZ*iAZ;)uB|E|!d8TTlq##Gzm!1M2G1piPFR%~l<;AxwSSmhwTGjHl zwbr7X#TC}2zcTMX_atK%-1*5UP2&IPl>s0jCcIX28>=Ym)p+(IilVffg?HdbSE{XbW}RoSW=jwzU>yV_K-vBnlvtOkLe&g`c zSsdSpQg<{I)Z=kJ-{U!W;594Tol>^=V3cER{Xc}x9U0s#qW*R1qN?t7y~o^Yw&7Lr z7+Fz{+TUe#jBrCJ8U#81{8j(2S~7&Jw7YMyBU4>7mzUuc=&xIfWb-l6ImFj|7Hq5C zP;a~3*Ra^s<}&hYU+X1R3*zl9#n<02S}q6RU2okf=>>r7_0E|tvwBVcy5jz8oY`GQ z_#Aq$e646+`3E-cCL-)k$mB+MX}f=^bvt2xaW#lKDg9sF;Lp$dgTJnXbF_aAf2^JQ z4{5W@iLgtyPdTrod3Wg;KU^ZTI{R_+=lsr2#vvgnBi%N2HXaEon86?6#bjnKq;FwO z@ayEY5cvt!;CZxN(*v~Y;4m`E82x|d`v0IoM*XDd+)CkgoR?8_Nj+$c8LF*4ev9dIViCNfjq2M9 z;ONK06sP;4MP0GhY1uZB?Q_Jq+O7lNifqU8q#goFzbB8@q>m%XB<~O2Dk*E*rya!} zm~1b$p3P6D{NY&s9y0$vypC_OFar(M^~&z>-w*D^ef<-&V=$u0h2XQ|9zQVAli>sx z%F02|U1eTPamu#$E|}ux3qrw7);tG4b#IsawFyC z9=Pd*lhKX}iH#g1+?Bl=ar}_*kcypsO9kiFM?IBEU!T@9{HLqk_uRc!9!z{hh9h5VtBM|&6ij`9c%A42ESMFh_^~R2r zv{B?yJ}timg3%P9<=lD^@5tPccn=&q(Mnia?^pz`rfT}%({ay$I^mL@^Yh-tNS|G?d@=6fMW(W~pN7&}xnlzpC~M`JZ3(O$0JO__Y-X&f@ut|GpsZcyNNT)z6nd!VXOST2n$|U+AhfKOufaQY`ued!Za~ zu`ns2@+RI|2Am&iDU z>U(P#o88&hNBe_2 zu$x|bNBKwA)R#GYJ(6kYIB*GOJQI<5{sMt!b%HU$Qwxwfq_Otj z@1HJa_$}}fe?P_ub}nD9M7_&$BV(8~G3wkXgj_~9%azq3HHIAc^mk>@Zw}9uJ-c@T zhMRZM81ro zbrWWME&!JP^t$fTw(oI?NHV(p!FjW`smOMna(44HbbGUCtY|d&3ns+fxD0s%gUn%l zBkUUHs1b4kPq1)iwNc{lTdZfV`;V>>H0ym2*kIP*-Oc3Qk&;=iT61kti9!|KWvXLb z;FB`zphmMVMxZ=akN+EWb6l{H;yJr=;G_v-6J7}2GTGQp5|*?cpzqtj-WDEQgx$0s z_@EH~vgq%yUDvZ!>8rJ5MkuX_h#x?`jE&jr5Mr+6Tb(&pKM@F5vwOe81E@qSU+FyD%(0X1&L2pR@Lz%&3eO(W&W~ zjYHVjnIrO&&8)`;0pl79f|4;frbSq->wLCpoKH$j?37(n(%yO|78mbD7tHzyMAo{P z`KMUukEMVQuAH9}V zJF629*qlG}z50C;!=&9{WbK@l*s9V~pVRSzDi&tP%PG%a?jgxXEe%0(B&&`5A}9Cz zgRwZ&kzE~_A4)qh+=F+4SxVmre-O*p#?gl#)G~9$RaK$ZXIjM^u2w4?Cu1CiOO|ZY z)9TfJrR(8vxbPlJ{qN|~oV8Uk#~X}6}q2W zpMGFLCD5dy+^Z~IyUO{P0f|PtITtqIpV^J z0e@}4T)zVhB@4JGb0w@Cdw#WdhNZsolE`o1En!G>myPbH{2o54+nbC3KFQKqb< z6U?MtzXtgpf4ba7L&*C@EZHr2s70efi{>KBY@vR@-26_uy{CQ}6$I;3-l*Vz3LF!9 zCfUhy3a0jU#WBzD=6h(t`K8m#bJ7)YHnEx=f1npckJFcWF&TdPV`zK57gpBR+ur!1 z*A%a>?y6;VxI2$UP^{l~T`ClfGbvAgWCWXklw9pgcE8lW0+C<0$bUz&sXAHRRDr+&v=N5Pbnti5;0zeJ#Zqe-Jo&3$gZo2da6kFMir zdrgMuJok=@&ShrN`J;kmht=IL|KTJveV&hBqI%Cx$on|Br)l*Zz2vt20i%}{X(+7m z!!1OxPklD81i%R>;{HI^>F`8#6(jif?-{Bz#UL-R2I-34C@3ZhKLPLul5v7!m=+uI zTk>QWQ?%Gbb%0d8byf0ZK@h?`R#hM{ur9s9F#NYM8kjScxS--?H+6}Rr6w=z%G92~ zZ$#(6Inju@ZAEevUu^W^aad29Q7y6MTgW_9PlW|dM`t_;HuvO}{9+12wCeSWL&kgv z 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 E1A=74od5s; literal 82211 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

^v1a1tO^-AuYiD1=5cN(!A{kKhxM1l$&FFX1g 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 diff --git a/docs/assets/tutorial-query-07.png b/docs/assets/tutorial-query-07.png index d2e5a85b4f62ec0e20b8560e54f329f4579601a7..5997ba2f762d0b47dfac832f445799845ee4039e 100644 GIT binary patch literal 260071 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�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

#@IU|h|8VnoHXxlPjUJM-M0ytP5Pnaa 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

oZ7si@1gCbAE^LXI4emeI#cF$! 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�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 O4{>1`p>lyAzy3dNvA+-i literal 78633 zcmbTdbzD?y`#y@T2$&#^w6v6@umvQC96F_?yGy0JVQ3K;7!XjpN4h~;TBKx%p>r6{ zgTDLwp6~gc&-vpl-P^@l!;1U4pXwYFwNkIx9mlPKV2M1q9T0#W}=lU-koU2Q> zuYx0NHux{VKiAD)$-lzE`5Ju}V|WAn{M1-lMIHyolK}_kT>uWwDLC}*Hx7==a~vGh z8yp-#7!J-8`}8_xVQ}K6k(`tS&c)@QkB#~9;FCBq60cOJJ`Vx$~0Q2;KKtsB^%_U|@MB zs*nF@Eh369vkhlnbmge?(KNBbvg-VJ(dX}Lw6kkG9(x{{`LEfVcfeCzZrlO4ga7W~ z+=pKIyZ1jkoP_^5_3zIAocW)ff6w6jyYu%<2h&q(Mw*=GTzhj*VwH(wV*K66asGbB z%vrLL>S`e;of^)&l*dqG?hL;1s8~6Drn#FS4qkIUDpI@%#4n#)I4~y8v9Eh~*26E{ zbT;b!TrJlfL;Krwz!=yo*3EMDccDBZm;*+fL*` zL4-GwkOb=eEQf2?K?J0AkfxOc#BW5G!e{VCBL|IFLq$cE>j$KXd(Lgd#pE?mAvCMZ)PBY(CpWzZp%iseeM^y?HZ49m<0e|gVUzf7 zp)$#di%{hmrI+Kpu_CUGZRr6j0G6tuE(Nzr6*_!*XtLMZ@IBS7%PO3Zgk zA*>_o%QR`U*D6dRz*PEqYkzVNN**6DV*BL|#;bc1<%yZxW$ULM%&}ORx`#Q^4@1!U zWwfA!WE?xCe$ZN*zFH+R zL6lh8Bt4>99v|MzCJDKLLyf${`_1csEcHvYyw^QXseInem(s3P7U<@Wxw^Zfe)`^L z19628Q+_M`(Nk1DaEtk7^Eu30t{(1@so#7rM)GRL7g|d{b6~R;o1e&MR-W%9#P|m9 z9XoLttdBKfR_aaqayg=Wyq1dWq|bDy%S5m!vA+j1W?y{{BK?b95$-;qU|8z^i` z?eFr=X5SN~^2p>so~`YlgwyujPt!YIlCwMRFsqrN=X6;j=Usv>Agr8yu6>Z3aPDln z5D>|KJL-ZxI?>?fQ~kx)pV2;goc;;zD>7Gvo;bkYN1Y)rmX8uXl%H@&3-Fv4Ko!(?yB~oaQ-WF;g#Vp~aNj_a)0(ka4Mp6n(6P zGTPid^?pk~+*(8qQD=j?H9Sdt1s;P)KJ;5DNJa^aG_evp<|aZ%<@77q;6i%-3kfIT zPb_On({A%nL7&(nhN17pl57QLv99U;&%`41+#I`cZNS_s`QDnSsS5PR9jN>n0j?rQ z$}KF+^IGj-p`1gpOua$yJtV0iQ=G88?X?_>Eq8RzV8ryiufxXli1{EA3<*Q{#leJu z_?%dfgx2C@K-+pX&7!tlsZ6N`z!%YTPfVA_Jj4e=fuZ(Z_RndFRz#i{p3y2s&p

Vpwpx1UCBdTSyJ;+fKBY~5 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 diff --git a/docs/assets/tutorial-query-08.png b/docs/assets/tutorial-query-08.png new file mode 100644 index 0000000000000000000000000000000000000000..32aa5d48a7e446b68359a869971bc9bcb921937c GIT binary patch literal 297253 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

8HGS8NuB>{8i03+Qm07M#{>pDC%5v?f> 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
KTj4N$FW5<;!XbJEfG=(r?@C1Ay5 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 T#x3CC1qS0EbSPX_0m#hb2 zNi$UMH{{H9m-$Yvk%p_RO9F4UmetgXz?(a|NIP`|8m1^_q{(ho7Nd#CUM>qG_iN=S zizNL|#@sL2{jsV|IfpFbt6wBPe7=r~YBQxTAEj#d_zUoG7Nr<$^W&5e5Ve#@9zal%cO#a$B5rWt#5(+GW)#tg6VRpreEV$e-aV zNF$8qsOY-S=V(|155J@WtqIA@0|DoplSlr^DMNv*J`ar!UW5!|P3qdJzL~exsyR~= z2QBnVmS{wb|Esn?`|nIMhbkk|({=Ff+mn%z8O*HeO^4;D*SA_w^MugK&^@XX+8d+~ zI>ac$$e}Bs^fr0jP=WRedgqZ*QGMgVM8u?_nB-;-qxWVG38Lw^c;+`h35frv0G0VC z!LOP()@46rc594Zx5OM^BpP*8MI)&2zv~P1Vl^@w8xC8JaIjqz7`717*U=dR`(AER z9xe49hb%#px8EIBeV+e=opX7=KC+<<^>w5{l*Xf&Pya9gDs|9* zt$(v7?*8VVDY7k(YR|ERO8rl?0nzxv+a74s(|jdhuAUHWJk)xkyfLlug|x0Hd7d9_ z`7H`|P;CB2`4{46ZTVlH{}Tgy2n4q^9j*|wP6bp(jPU22nO)x$zK{0)TYWGfY*NB1 zSbK5jmy=b=IRhpt42NjWQ^nv86LCW1Co~{VRW2Y`{%t<~tNEe7K0<&Irm|Y#<+3uv zM%qqlC&eDvDh?EX=?tReURWeY2t9Z=lj0Z{~YaqFY7Q6 zGzftMO|Ry4I9G7cKNX$bew>{V`OWWFN{jtDVc0b$g5>Z}bQiP{jY*IF%N5Yy-i$_4 z!ZKovDlb;Ti!b#0*|x8DD1Mgg5YTfjw0(GavgW8;Cibrd@_(_@sX?#5X}2gL+sn3L zpko54ma!HrTM}<4;YPc*Koi6u7Ha~9N86JZOco0b9U=)_QUr1cd>sgV)g$F(IwoSo z?$JPiHWlTSc5$W6#51K@?~&vLwIGR@@}8r^F1y)e%0laL$HCc=7saN$D7oPxPa>1; z3@Tt>z9;JUUX&Etg_|UqE|3#lhF?7oAnwrV#lR1>q|wE1lbB*F`K-Csc%!M;LMZ?C zYN3AYa8N_s1Fh{N4uSNan(%-8p6y=2?ZczK28N{pwWa+tZC_Z{bMWg1fzMC>31}AG=oxruCU+$ z1!qq&Q4D413$In6L@`AZI--_LSvnU@VG|dMA+-iqZ38pZt9`iiuwQ!v+h6O-IH|m5 zP>gxq!RrF2*D-Pq-UB>twT zcJ++_<_2{xO1UDtO+=@hv?O>VW#4y8P8#7WSu5t76YZ{fJrU+_4EgNo*m&hHF%sl; zrpizAc0bhZa>TdIIfowu!+H*W+@b>`zD!p^mUgFpYD0?K*7^1K!TG!5;I5#8?t{@F zKZdIzi*(i=-fr~4Q1$@u{ds?zsu)4F+%mUp@hm2pT&|mACVq9hL1www6jN9 zYSCL64&Ki3HPzG0&!ISpHewbQIDyIySY=l{KnA`&GdB!7X0cnbEhTKQH`tc#$~VV? zayv4I@qu1Q_JvAhp+Ce&OgIZYLvSlxtzk0qB8CFC_^^C%3LKP9HgotTlUW1R1bL-c z>I4KCi((txx}Zsjq2Zo>vq87F)#0}@Ftr8TJilSZ4U`gQpLhr{!Dt;>(9tn^XNqwx zTH8rZjZn=$G5qhZbBLJmyRA2iC&xAnQR{=gYSgCFSR#cGaYgFBbN8*R!0eb#?OjfI z{;1K{iD0&rJgI)m~)~r4DhC%n7#;}LUjEFk8 zfaP2)=$a3Dw?7l9BV$&(ETAh=8f9%x+<&pv^?7rXk(jLWQ_`Y~{h1#<_RU=RGE`Nx z>%Wx-Wbrb)iR_k|)xixid!-elER6qeNdajetnQQ!R|0-iAXf~inidgEw0=RLU6asL zJ3RH#X;t`av9tw|sWDK^&(F_hn8$&q7Z<}Llj6tWXwrcd-X+z)YcK=wo&e8Jt0Amv#Kb1W@r}|7rx<;!eEm%3HJ9~mcvOOI2RWe*kki>DeU)E zyr=uunmU*FShKBaf)~CAvY4!Be+4-#uj>`qzb~d(gx)PkAM3U9jZ34Wu#1a}ti0V} z#zfUUL7rAV7uADEzw zuc*Q+WUt@OJfQNt-&L>uho>AeAqs-0#agJ$rcmTIg|S+yc{ELBPiWd5?^~TQy=J3@ z6qzxKSPEMYC=j@()-6l0=FAGO-U_&+l>gbtw+V@JNBH6cAHR5U75yuX zn7?NHZBzZ0AzScPFW1TUx#@2H;aTF>MQLZ|HTgNoB zOyKaL5$U^hFYyXci%)*S`^s{#4j z`}_4_;kBSX+2KTvRE9o9+V$dE8jCH7m9_Oh3LF;BaJHC*g-wNj8&;Ze+7;t|Ee)Dq zUKY~B&11U+=-b=C*by}zB@6Fvg1pOe^WFUvp4pwguQxFCYCi4=IXi{qHVT~x9H!9r3k&Bmmfn3imFisyp((u60biIgzk&!WNKs?*QW*RdA9*4D1Xj2t3 z@^SM@b@G^uAuZwi!Fb%e;H$^531;ps8WFXcEl zM_^)p-{!~>nq`!vo7Kh?yN_d)@D;a=1>7WBW2?uzA5$4AyT6gPLhTc_xITV?;~(sP zYt~Nmbd`hf?A5*SZ}&t>O7&f}ZAEl_uH9IE_=EMK)x%p1z6)K~R6*f+Op~pef}Sr0}es zrb(`BreVeQNiX`ubZViU!Ki_2ZvL{STudmcT%yA2B|z~5-PDu7b<-9Szt=&MJe zr`FriqcsJA_W*?y`oW!op?|Yr|E@nHsnY3$)%d~)12;Q&cK^yYsz_vWJ6|m=T1y+o zOnJY#QDtx-QEWMF*q`#6AI>yV(_>7KfBOJ`3%&e;`GYPb>5?P+_bf@i4bnS-w!G_< zcB>kZppM*&r_n!j3*@Mb=xH?85VNlPE%V>!wMf6=-nGb*`F}*XE2{t56r1zSUmB&V zb%yAGb`17HVKjRy<_wXpr2st0zJY~k$yxxb?F#OMR%5G^N<;+gj;#9x?zdh1XJHn~ zZVTBc9s0U(iW2Tk-4?+@B_}7=uNh6Gb3bIpfqWtd^j?=D&N<2@{o7lG2=$VI47_jn z=Jo%Bg16e}peypg*5`@|EOb+>Q|LI5JH+8XDtfjsZZ?Lm=LcBjl*^EhUTT`i$Gm9F zO$3K=;#sY}QBrg=fqB0OM61$k5mR&FTrz$LC+mMr5XjQ?wnk<5`b2O!-^iDBO#ddI zCkTI@0ckKfZf-}ZyczKJb+yWpKtkYvs-kFLA;x>v-od$ESzVj>oL?R+r!5yNNgtx(LW5SO+Ej7e|IRQBSbU%>j2Tz-zr_s`VswP=qin*u@nsBSXXC|D zr)s&VjGo9h*^nW5Zmo2>xJ*`EO!4<*4PB;%R(0iH{znIX! zqn4GGxf$;_l*%3P*M5FEP?}dd2DDp}1-xAF*?(9e~;|h{3CB^cSieo!qBNPC>CNSH5dc%P+oRf7BmJAvLLC_@1qq{)iOQ;QT>@&Neq$b$&*#4j}_G1bvb%8%C&w8x+ z7s;dFI=BPUFdy;%@y3qh(%->xb}S1~FJ$bqWf>!Ut(2lc?eBZH0L^c4{6s4Tn+H*G z_X5~>6)g_i!dz$`eM7?n?v(g=f6zF#0#735v;X10xst-`nTzUZzfjRI2%Yx~K6z{? zVDnpjH<8V*gzj3+Y5abgoLcKSrRzlIYtB+E*Bf>e0w85kP7g-JpCyeAWc*?fB=HYcXE=Uo}?%0Qh6i2mOIc!;xJ)z1tEO?Qe>I{Imrv;uzh+z?OKVD^^IjR!_ z1$nvd+~GY=VK9P(=EmpZ?M`D957MFMS9@4NDUvwxhxiPl+kaad=3}a{`Sh7lsmum` zkD#6KS42?g5Qel@C3DA&NP!3+R6&)yi*KEB;-LenDR<3{-mZI@myXfHi(3r77iR~_ zjGhhM_h*Y+T`L|SUlVAXi4Sp+EL*_|t=&mZ2O@P8Q9@xjXnbvduP>mqczRxn@?45$ zyCR`*&25Ma?r(31Yfn=CTb6MBEk_&2<}@t=Y$_catjlU~?JE&d<^2&e*lnOGO|c3~ zigtk6JjqhV!osLeiOJcK8|^Nnfj3#hB^us0fhp<9BKy+|?E|>xszzwp-hAf318 zR4GDY-l((a#zKnFU~t8cAqgNs?|%P)DpzyuZ-+bT8*SiEk2$1ukk{cX2h`*u+uY;o zFPyTC2C)GEsAt_DUnd{9Z{iy_BaZv21-g-)@qYh-Go&E>>s2`0;#jtXP2}5#in1?b zds7(hW;^3mh{|79W?qgUYlAs`Jz(T88qTEXoML{$RHmA{6k3N?97k<|y9uC6q1UPW zYT|Xjz%d*>PV6uxX%1Z;u^&sEi~LLI=fg~w8Nfu`h;3@n6?m?}`2=20@~*uF}8|4-DbBI(&j>!y@5)0ffu zvYp|U>AmQH{@9)zWyUm};B}%oV?gA4d)XeJ)G?+fq@ zcbBra&y~{MwaPu`Ee;QY|FU5yqAWRA0oh))Vu>be1v;8NeY0OHd^SQ> z2@=x++6XVumx+Q9P zI}S3sSNfiZ)gRvpnz3r7yKc2-q@8qat9T8JPRVS)?Lm9nl zB;j+hu9J<7k?J>{hR&(NBJtSNE4qlW-CpA(nz~64CuvPXQ6-4vszyduQVv}|h;H7b zihxyEWC|cB)k4_iJ$6NW9+a6FR&p-ELf^Z#;qLarYdRNtSuulvd`;Zy*yZ7-YFoWe z^mc{xFE{e=mI==(Q35zmjE^sL{?JXEb_@M2#<;s&jdo?bQz2PW@QEL)=eSjY7+_O| z@Oeb+bNI}~l^z!oIiH)c;1t;LG_K95k)dM&g`xcDYa|VsR@}r|32T~ZA-$ITYR`k( zOimFvWm*2`apY%&ftBo3E;vxa^udQ`lGOR%6}=(i2Ryrh02>n)$>1d9Mv)4{0B5aM z3&iYM4jvE#^upU;dGR6x14AZ8OzGi(JZZA#Uz@u3+@YDMR-ztg&Zw zg<`W$f4$}b6Cb2QC%DF03~G%qU$vTq^rfg19N~6s4DGd6`;HD@4_qiHkeD5B;C7a)dx(>MhXbD zm~+(3&%t`I%}z{>Reo(cy*aV{HO6q-h7rGInbP=%kH7Ad;Bf3-Y_z+9Ep)*^w&&y! zv}P&|b{}!Pg0#{Kb#*1Yr#b22up*d!`BY7R>h`g>&_Zuz-ZeP14x{08B3l1X{KP;8 zyI-8KTx!(gFDDyGdwb)>j?=2a^9EA`g#-l?SX$sF9Q6&A3&!E`v+kY`ZsH+|M9#y?DsDr;P`h?%Ly|`s%qkjx7T0B$>l;|pKG`^yI=cZee0L0 z2jF7mtS2{!g~EKRSCF0(cF~6c&G%E(%>#hccsX<_6kK2ko6HetyUZ9A8ca%D%HwS@ zog4A0{cXh^-^*X}t7h4b&s8YY{dyDKXhWU~zK?$LbmR#-ibJunx6I_Qe=?3aISRwN zxTPljF4*_Pz!T9c4W*nW3!o4IXp&}+7m%cvu3yBo5ok`; z%nNhYk#TD600JAW0L_+YFdvKa99JOs)AHRT;oH+a<#OvD5xmbS2J7Yg-&yycd>-m% zNAb2GovqXjy7n*iC%+LFdVE4S%oKm#{`MM?FSwo>kIIFh5ulil=SS;``pt+MD?f26 zdXgJpC zP%CaMlK1WA)r3y3o=}cwUU?6lVu14`(RdxoJAJIX73jrJSdg6RXt6$^6lcuzjwCbL z6HALp%+Ej=nBx{>gpZ=ds;U@%(;7ocqL{`H8tY_%QfcHW`V*dCqZ6}TULwD&gjUW^ zX5Q!B<-5GLK(or)Ar{P-86j<%%a0rjsO+eD(j20fk{-S2fPr|!OL1$Z?eZ#d zdZONWUR@OHm^M5jMU4s^lXDwT>sZx$pPgtGKL}<)R@0@9*DAr;BK-lxB%*SBK;b@!!VowBVt}e5Gy5Y z`N{2O|1?+I+#u#;g^%fVg_9r^-m|cY+gj*^EEJj5phpmsE9h&?bvFcDt9|Ofq}m2j z@_Dj07qrfNu*{Uoi=K_!la4r0!QQG?g?3kfvpoZp%w~EPW)K;Xh=MXM*y4m!L@T=A z3AZkJgD42eBF%K2?>BX-hUyhSyJ}F<(w*_wZh>1VoHLWK7T7AsF_UcfR<5w{m_CrZ zZu^(1;{fq(U$7GAY?n9sxZjwa#^O`kVx(X@2`4a|9ZeGl=E}*xIkFRw86h=%^Gg)r z?YIbKdnf9PCjHJ0O>Ecm!4wgrrv2R!Z+Vgw(*xQNBgv};W@^((37{GO#JIXMv&H+N zu=7xg5#$_1H9)<%Ar6PMbZ-R(1+pMyN%Ri%izC>zFjND_ZUy6-x$M{6t_H~hz)=V_ zwVpHArq1_4+b-2Z<3J3*wf;YT7e!^SRq=M(l#jg~<3=H|2mH*4AB|b_aCLsFQmsBx z0X-PdSM-W5T31*|X|bTZk24cFrrghhVbY!XYGW3*vYl{A_W0@5pnYuvntUm=Xs9cT zdWoT13tpr@?hZ~VH_9XE+2L02TCtb{mVI$DNMtrejKC;{+z-l=!f92oVV}Xfhm)AIaZx2)0AF^Vy6}$0OmtF zyjj_6J-PA69DGwq0yT+xA}D%&`1wmO4kJWy@9Nm;6p*nwNy|&(C@j{Of_~GHM=sL^ zl*j*L`jVl+d_LTF(=l;FyMC80Hh8M-XLA)|PN>+C*z{@5!h1%vZN)OYQ6UMG<=E^qm0sfBatf4C*cTQn}NL% zu_e3jVvaLy&a@8Eis1CrxUZnVhU=5^^yTjRD37h9gbw3$8I`g>N0Y>TF+&v6IdfbZ zkRgh%Zr3pf$rMK|=X6Jau<5PJcdzAr>Ctxq%c=t@K>(!4?J+dyQ7BhmN;> z#~K!RV{S1nQ$a+J!2(gHkmq}UiY-w{c&3}xIOg+*Z)OVfZvXTQT@Cjm<;j%DA1@X~ z-b}3(IZ7NfEKMwY`{?4gxGomd!74Z#^|aALA9{-X0jPDNon$z4s(Df}KSYQYJ&ICyGCq4;zXn|| zbyGxvr>b1C(d3a2s$?g=3|8gVU73gGclt2 zbkm~ETJqtYw&o$;0@#}O;LQUM=e$dDsH^sjx5R_2DK?IJMQ`voUI=u(T%h@CHjvdG zv~Rp=PP$2Y40{8Q(CXemc=1I5*QvWDI(k?`JoV_?ZO;g{*VQ#E2*BnBg*pr~wqOm$ z5(M8{V(w0-V;jxfZZ&8i^FRN}^I+G}-k1ilMCz~4%<~j*ggJ?SP+=ZlO=Z42{AsXr zvG)gd3z&4XVZfPmM0u}G_N?}4kCfzj^BAll=@}f)#TJOPvr}<83U0{Ik%HQDbvOOw z#}f5DgNumgRK6<-?rtA>@^`!?WlZP3BEm7C7PtRsnBRw%U-P{{xxi|S*0FXl+3vhd z669CjT#wHU*Sfd&qP7vPPUL$i6kfk+1UH1zanC2alUHuu(d2ea1`8#5a4d@YyR(BE z-ZXvG2mxNsSq1HsSZeCAz1|pAao^0?o%&Qj;*IUgl`Z`$RkIL>U4B^6@2#PMkl%)c zLn5q_vQlzth1iLHnzM{G=X0*@f0DVX>Y}{3T8I?`beD+;Wr{i4`~Xg975<87dW?Y7s(G@Yp*Q{< zwr$BnaaoUBvcYWC`*wkKyoOYth$CIxNV!*qI9?Joh@0f$sYJH)=-}xf#@ZMtLhpLG zO`X;l$*Y+sn|_l<*i$)8E2zmo`8M<%@W->b3t_T61Tw|@Ms1_CC$CgfBa^s+9xj50 z-Q1kW@ud`MyvGWp^;KaM)ZW~`1-F_IP-G~MzXg9geU{d!X@7TB+eFWYl! zo6qtu79p&%-V8neaWaZ~piw}_)_I18bU^x<CG)WHhxhoEH@EBX*v-xrC zp&HNMPfKcIu!=^S$RRu3VoIx3fyXMbDOMWDX@kO+Ct9%2Ia;V=6fs*Jg))=tl_u2G z#YjvYT1*rv3<0qUy3ip|mkYH9FmB3&Ifd(J3F71$fG|{cOoy7Jq&!@}1)$q5xHInIloQ*5L zlZSiIU2WXxUPVMI2uj|8|D-+)9es<;bM}wIs8`IKn82xKn2a*J48Z^9SlVV}nr$R9 z+PjDgcnl8WcBU1M4|e9FNMX!^S~Bw{S)HHF-ejX7gliGOf;Hjvx}npwzjaxCGp71M zgJ%;e(S0vlE%ApqXg_ye85T_g0HnbGWT?M{p;irjq}x4$a7B=Z}B4k^58z& zx5C83uGr8ZeGqZHkWW#avmVL6!811zOV0gx45zSEdCc;wc6AzG(BPpjUMGtbQ`|>D zpmMDiWra_BC#eEIARypJg^6?Fb*-x7ByLgUwCw|DqYGRCElFK@zNq5mpJ39GnMP+r zP9D>N?L5+*#0@k#E!W*cAI?OT^McMkg$Zn(>j zPKOn!(GALZhXf>c(ig`N4e(;dg=^T#XSF>N1ih~<>T}4=WS7%u#peJ*rWyvD6y)z z9NirKjrxKIlx4HH31ryc0_y(|A4 zj}_*_)>F^P^Meh#!!j&o19}uf3bHDL1J5|bFk~;5I?{?oefMhpkPT zvMOl#0@EeLQdMJE;~|+l98PpovlJEHR9Zwxh=L(MlI0E$m~tg8+qW};RuD=QUJZA$fXNkpXB@VBXBGtU6}6cTwOvk|kMlpr zqG0DG8=57D95H4B9O~qTs#}~$v3uu#VMe#c@won|c7mbH^o-B2Jdw;VETN5IJ$|3~ z6@|l&Jrn~JtU9ey$3v^su|`J54`E*DQWa`QsnMReb*ktg$Qpvr3T}~E zZaHO-<=Y2>f07Ny^}?$hrmHCs_9tDIP#N6PyKl82Rnh*9TVWt&S0I3*lz?KRjE?m5 zLouY>wlP8%SLBfhlbLB6m9oxm+ITUTZBew4x9x>~RML(1keey7cfKPLr?^|o2v~j~ z^w4O6nh2RV z6nl8q6U_*x>3*fRV&TlBFX%a(Mz>!cmF@X(F{G9}X1GyIPc?C^{<8WLl6~#|jIf21 zhqNHNF+(A1tsSTH6{)Ywk)zgkBCB~b`ebXWkJF~uxK1`!Z)rRLsHfYpU7W?chvBuE zEioYo);umPqqM0o!~DCUt}mU%R59oMq$hqa3w#;u4z6?#C@LtbJNst~lKcw~n=NW< z<4$Vla^EvhdVL(es3NBvL;70k3m%8JN?{my93If{+ds)gWvBafevvY&S%vFQ z;kcH1OSU2D%7gj(otnd$JRI9e67d$Zu9sV}Oi2XJy6MA)?}o zFXbfFkU4T>yYR)^25j*;x5`pjU#rWetUuH^KfUr6^H2TWD6QY#mG17%-^ECCQJ$hu zBpLcQmy%o$UoHFmwNQaJs0ng%0aV&sVtqK43`e40#sps_o}-=x4iwVc1$Gg$OARO& z3|^DYD$j~h+yed5Y(EEok(fY}UN^aqv5R?RKL`XCjj2@(gbPp}MW!;Sfyq)t;fl*O zVa>yzg~KG4^iB+X3w*i%F)-53$OBZX(G_cG%C_I>6JuklP+mvcSubnOg>jxP>x4!? z@NaHzZY#DL$v_MTvtrvS&ij6nC_g;knJ^B z;v@2+e9U;We0%cBFcgxUpyh<j&&^9a;dU zvp;xj=buK>_-`dLLRR#VC*=I`ENoKZ;`~CYcxE9Qk!;qXMOV1b6{|gbmhA&~aT{mN zDnQvlEDl6diob;s#`U@p$j8`+4hoK_UuUOdYr+%OMrjF{l02kp8KgjIZ<%m032u0- zUg%Oi?UE8zoGZh|R_-5sdyn%Wmdk_REk0p(088)no<#Ng*UC4`{Tm1}C+&{!Q?w`D z7CaL!L${jlzizI-aTxE;SNvNLj$+ z$aY(QEZG>02aQcLR2p@%HS7_C_LNIadi zQpx~EKZQ-fegWjj>kjjnr_yfL2ZzNRj;*H0-JV}qvp^g3UzXH*MZygUaCgkpIa6)1 zyPjCq=*#xSII|hQ^ed@*6#wp?ZydPbzfW@1!bXTPT)ctb9B8o+5|$((YPd zd4%XDu4ikFyC7bdAAG%+7Us8QUUF-*O|@R?a@k zle>sQo!C(`_^9gJ6|aBUCyXrXktHy|k*P;bbce@>(b?t_ghY?0OF5Zmg{>~{)fkTB z=whpI^=%+8JM%|yz1W*`iT-DzQ|#2{z)JeoxMT_=B=fUFAba8|F!|QgaI*aT$$3iG zc(n@zT;U6NW3C)S8ePvC(+QyRPo`gwb%~Jea0fQkSQsv~=q7!{mAHw)XWw<6>cN@F z_owK&?7UnY+g^(N?<{~uGGt)fy6!j1t^D7cl6SW0zoTDX*QT`h@~)q62*rVk$9iAo1sEA@p@4ye{Q^>ms zCQjcy`G0VCbn@02j0v?E9aghx+rNx+J?-zZ_$e5*bnsMF8Z8McFG3|m8X?;rRR0)=#wav;s+Q6!vW|3sJdM$JBe%IB(JbP)c zX7$V?&huRo%zE#wZyHz8-Cq9UjMTKI@%sctfa+6KXb`|0294@cyve#lYVLfw$=A_= z8OfOSw^T~DPh7Hm?Gc-v50Zg(Ac1UE6ZJ`GoLwb*73$j#8L<_ zjDy9XL&Bt%RliK&e43nZ(m+rw`w6xe;yq@`vV&Og$J5iDxIn@FfOTVK;9KHgRFAt& zF*@BAG-8aZ^q!0na6NU?;e_;@A5(!yL?cHzyTU)Q%f1%vbG`f*)W4qN3{qjAxH|KU&}BnY|D_ zUuCJZ+J58W1tr15z@b6qO23K?MrN`ySud9Ik2EG}7gDA^^80y$xp}z#dLNv?UM}IW zJ@?1skh+SyYbai+{9KWdUX+lKFua_y+*WJLC~r5rV6*-adSBn)LM&SBPT&rOikz`5(OO^=$zO5ozEvL9h((`imyYj7@JbdrrO!z4zx zof=C%ea8P%yyn4KWh?oJ z!qA>+lK!onH8?YnymCVJ8IhM#bvA(G(u!J-<1bui+4m$C__~ipcW2qpM=+?(Wz#^};>2sd_~GLw3T|~%+WX`%?kde=J(`wBrC0x* za`n1$-j;QNTH&TVAmhtOp%h@Uhq)v}Q0k7k#Z=St!Y%OJ`JS-U&HEPVy~YVHP$}9 z7+RzB{voS)C!x?v3e{g7lf<=D(ICEx*kPJuX5q{~;?G$8gfdE4nR3a$*rntxE~Zu# zIdBblHU^Ej$Z6AgZWy6M?SbtiCh)J--yBe`ews@U+jOi;V8SIpKAy$wuJ@+ChM!cX zT}M!7kWs5q{yPCIB7XdFL2}6Hq(+Izm;j^oG;4}qkB{lu>D!?=OBRWMG29DPy5!PZFl4KWzi8NR+V7a zn=~0yLr#eFkk{$pM<%5p0As_scuQ9(P5z*e8Ahs&ibS)@h+g4DHin99(p}=970I@l z?l>l}!_Dj@lHv`eo7xH_Hu98y0UN+SACVk&qNVa(M3YBzi=zgy@!%hG^ppmzzu*gs{nIu< z<5J;5zeT1PJXMMfOAIYAGr8J3d@*1scYH%~yAkHfX5Unl3&;l=e6A2P8|&@P7TFyn zmI5X;XBE+GA9huob}eIh-5qFwL|Vn=Gv7V>sW1N6#ln7)n|BXTpkFZ=wWtwYvGnT*4-^29l$&1T0pVAdw~dIzRBJfYvcmXD(7fJug)iM>PLh2A5L znMyYGnV)5g(_dVR8IH_h!Jl?m;Ijaqcu!$s(C`K`B4;{irE5AZ%HrXQWFLm zzX8EK8O>!8RykGau0;{-9=m_2AvR#e=Uy67_zf{VTnV6BmWkuKh8>h|$xN?DIqh$_ z<^SffP(c_xpWaLJ16%FBA>JWLbC$*5pr*#^e3hlwH%ZYo=am`==Ke_rcy9}bYHn5hRX&E~ZJACmdmP_A0FW z;y&4nDsC-UaLww65NW4IlVp{%($%O~i8a7KAbfij?DnW3SZC zda~LN#|{%F5s>Pn`qVK%O+z26gvSQ>s7Fgxw-Sy&e+}nzh^N5`OeH{S33q$AUnApB z?84J^GDX29loIfBp3Yr!HKGnJkZV*L)iVffvaUVq0fldFC_17Sykdy}r8>^E1ftkx zfS-C0%hB*sUD(~q28XFsMb)e-5n%-v{YzJ&=+!4NWG}!zm!uPcV2GeMUl`ig{kl`SjZ+Tc4{&s?9IaA`T+-DRROuI-N!OuqirRT~%v9*@BiZ%ET zR=Q}dt_V?j0)UiMYToVF-$I<@X~Qp??AC>n1SBGb41l9Q>Cb9nH+ilc;S_1o){<7* zU0-%sZ%1sTPXCrks33TY=j8GqMIVAZL(jFd1(7zamd1! zA_XVYW>UcDx_5^RdE@M(K|@#PJO($)E^4LXz|&DELYl$ zB?;NUKEEC^-@{0^3yuF7EQvn1{O04X7;n-w=qGggLB~Ql5yD}$CWde>fjuaQPv9{> zAPRX!EfHNkmRj?D$oii5r`8r51#_qwDoi}N4M`sv>ID1Zv|A(%(42m;qwuKWc5Zv7d zcZXoX-QC@Td$8bcGq}6E1PJah5C~3iC%C)aN#1kLt@`fyeoocQJWuWF-Lh8q>gFz^ zRo;B*sM-q6p|ISR%5J!u!?IW={9%%r)RZ(I*_|oh->Z=-N|J&~+0qeR_-cb6Oll|` z7Q1{{?4G$(uB?83jM1XiHouG~mFlf1Yc8lARIY|B1qXM-nlDxZoT$@dU|P zW@uu!*H+~??X+>77vfCS_e;`Ryjh;U#<+B_=q6$?-K<6un%nB{c^{mA@)LkTs!ji@ z6??@%C&1;myJpI{_7NLw(%;|>2=vL9S!UK|_aVTzXJ*uERGAa;m7M5w? zBkQ}-ZwUsU>$1<>>v8vbQu)vxb3S9*HuX7OgjaWm~6+}M%F8@*zm!;a63Z$7lM+`VG(>k5aGrS;YCD17C=9GDf&d};;zU?#lsa;PCdN268ztC?PTxv6siLqoq9rek+@k$cVq|do|C6Pl zLMfF!sF&q^c)ZE#`^;u(y;3KH5O+2!syI?^!LU&9!h@OEsjOrBty=Bk1jg@bPgc%Xu2nrS$h=oTGx2?{Arc7a=M!j!$>0E=)k718PO=6M8la zzsHOin|0TBDU(lunfND8<8K0CsM+yIoGM@DTgm;Bk+n_|-f<6?S!i{OIcBIl8dLEI z98SwutB+!Ujn)h@Y}bBZ4*3v128~!ffQk;yGCneB)k?HX5VM7bY?#5Zp{as_i8cp# zvF3%Ty4oLz))fL&<_^BS6i+XuguWU-3V}>RY0)m7?_R~gSX$;?X`{@UEm#j%kF-HY zOx{XNU!)#!Pu2q9E!i^6a7}3ka?$+Ntj5*5DxkzD@yUIJ!70pxY$kXF-w!H=$kE&bnLyKVYl3nW$*er1@eV zU=pAz2$D}nM%T3vNEUMX#7hJ!@rz#*{89GIK5w{>$8EdCiFM z=tk~}5zp~G&T6;j)3<*7k&@vfS|aNd6N3a_zrR1X*mCv`GH=rZ-|aU?ZP@=x>6FmR zJirxtvATGDg+tWtf|vqG8?^e8s(3OL=f7>vVK)V|SahX>hs`>f<1ZmdzR%6iz>ubM zUh8GkEsc+O*Dex;WmLAoQjr@)$I6C-JdyW=%9_|KZ0>5j^GgI=y3O(9{N7}q=!>wQ z1f;VGvxHoYntwJQ>A6DIf(v(9*0f`LkyN?tSnH3#*9;()X_S74Ho62^G~M-Gqovtu z;0(QLO&Vq8Fj$B=6I!y3yvXZ&kOLm4Y1U;konM20=Tg*cib7amlH-byhWJ-i&|Z8* z9JB0SrioNy1Mw`#;?*hiE~-!YA0DtC+ml+po)-LEp5k)*2=pJHp6*U`R-s-P+tKrQ z8+@?87gQYLJMi4NV}&zf7bTj__f$e;o__*^jcOW3;U z3#eXn;Cn7qv!`vNC`e1pFsVs0h)b{1zK5nLP{|Uq63^T~QK&Df!L9vJ_^p=5QZcg( zhqe2ZKc=JWcex7MH@TMnO&iCbDgJ?KfhM#k+ln*rxZ)BLW~*`AP96YY@ZTmU6e3;bSz1mVY9lRU7q4VGr+tUq{`^rN~H_ijleGxkDk~*2_x*yub3!hhV5rbU^2&JCM^d5jW$up!{Us%SVEy*ue3c8sI_ez7@&Tc*!hOMh7RCILi3-Ao-+OxFr? zW~ATERIu5CX?TgdmD1Z8O;<=v%jCmZ%qY?Df2!h0>ok*Z z>J_y05u`fU5C_fs0Hi}lVz)Z|@8o$G+ZJ`oucjprTDGoERjsi)IF`vAYre?xJIv*C zj3mXO8&oqHE4_tauyG4~LJ9V9Hzo^*NBToGV9fi7O!A&QzWc?2w}f$}rvryJTR1h! ztvv;%f)v_n#Lk)L6VAl!J8Y&FX(02oCjOh!+WpJo0dOz_<$0#^G1KLEeD|OaxkVek z+*7X3&C@iuMf}%^a`F4GRENFGXG3w(BSv6M^emc{NS~0f;!6ev#f!P!EnIyI z@dq{WK))Q=??wi^gU3XfFm^i9c&sdQf&TQ=!_%l1n43gTosGr# z6*5igx^T~5H{j!py2ZNV(`9VC`@|+vAzf1iGJ^#xtWLdic06^aN8~%VZps!TL|4bn ztm=#zAaierT@2&(L=dkJN08r;$OBAL7p;u6-3L9p;^Yi_l{Rv#2-0lirQ!bKJzKc)D zKGY*AqRU|Xig9l&ow-Qu?o^GIzrdtwul8(>V6#{~{@taG5pGHV0QsfsgKtzF*-|5S zwseRHtagi(sB=vx0MRPtwf`!m(#UTexsq+SH3VRO60rv-CeDl$@9y4YOI1x?#VN%mkyc}z0| z9poFuC_fgF+kj8u9g=`va0*8W67o8S-*byQmDWk(3CE>q^G56YiJ%6yoVrc;R8)e5 z&4(j}PYaWcusg>v&k#eM0wl7Yrn^gkZCVk72mCfpvY3;~+#>8i7P>Cr1ko2Yq2shV9^JX>+bY^o* z0$nl|bzLM?qjwBt08+jj620B9!y!9XHWeUOPHS$A z_t@yc`)pN$1+(6x9mfm4j+c`>4t+Wh8qRTBXW(&Zc?5y z(avDvGB=43Rj;xVOk4u5xb6bjJ z;>k2FklMRcqUPfi^IfWNx)%6n-@|LzB?&jad}8RGas%UUA5atHi@`tS0v?J&iW9x` zDd;agT8e(pAHiE5!YC92$|_n1X)04I9}=YsH$)^pAL^2%BBT*EFO2 zq_%DqMTAl3RPkh38I)~P^<8hKN+f#>1`O`m{q~l}{}ZZgl~`MgDTM zS>}kbLI2qU+?EY+B>Di9hI|Ny%6aaR*o(igH6uqx4*yv@!8fK%P?DBbvkMFJKnmJ}Y zS&8&X5b81p=ISj8c&BFO-)m@|lyp(!okFBUe#Z6mtwWf8BjRFfK#5KdobV$eOMzYr zWSJ~PNzDJ?ZI-LzZr%;@w>zmiM z5^u2lh;xF9%h6Y9YE&u27$phWihUj38n2BR>gq0xa}Rl#%=mVT_aVeYKaYbn}2(eB3EEcA5j z93+msYQk`$-?j8rA}+u``=CQm{r22y#%e?X63bPHPU}y(?b7+_-k@|6j24h zZZLM)ecJ&eJe4C)o)IRiq*zeQju&pp^+u5Q)==3*^qeZFyh`-9@Kz!}*NWgJ=DJZ5 zsp7Ev`}%V5wE40|6#t6Se^^)Sw{r=K8TUs!czCz1E2Y0F_2+;03G!}l@OW8F&u?Q7 zT1jO5#iT&MdidT;!sRRNq_B6ba7DOKDFiY6iz5Cj`v3c9|MT(uO>Co)Yzqi7JfkR$ z83yjZD0>aCmGWA@B@`9h`QllXW}&{#eUdRqGMh#PzzY|hev1E}hyTyN{6!`I>lr-( z9|BR$a$fn})jX5`@J`}Q>y9Jrw2Sw9U*Fe9rGbL-S7V2%t|-GCa3^Fh{j4!4VIy-e z2NM=>9mQ#laNi=$+VL<36At6&+(|8W&Q02Y3pz^}ZY(G!NOZLnd{mQp0d_D#=rFG{ z{8y3cYFc9dbrAp0yO1IWb~oU>7WoYzSIdJ8p|7^B%dE2u(;)5hY z3e8W*Fd*nv_SwyOB8V`s<(z8t)75oJRMiuun*<^=a-r1&DN0X5qQQm69Q~hFM>420 z@tzgVYVk;|qMQUj40^qY`mw;o#WsJEzW)AS9nR1@Osa74e>lWKS1kPz+?nFOA4QMq}#VL6Z^&is#e#Hv&3S{3K1Rd8_^_;dwKJf!Nla zklrf$U)u^sOYqKf;*Xr^Mg1|9b^rowVHJ+R8SuG6yJz~xg6HfuhVf?-*fr?g4(5qH zZqv(NZsEI67vmI`J;=KqQD$q_-*Mw}P(;D3rYIbt<9T5oOc(pn;L~94H35c9NvO%_ znRB#5`l#$Qcg7Q;OKfH~$RoM<84y>Zy}$8qvW*z0qv+UIBZ=!Ct9kkOu(_zyhvp-U zdEY+6GKWiU&duODc{YXF5MT;Od&Q(lDb@SDsAgTyi&#zB5h8(EZv24QZ&@U_+3Wp(}r129n$WiIS z5Tl9*0)HnC(|oY3^$A;S<<003(jc(Mu~8_C^-##JRWG6KqdWz!@JA7~Ws6IoD3vGQ zK0ml>d=WrsiGFRF%2VMhqPl2_*ZmTRKIPQJEphU^@69Z_Mx z4I3JC7N#xM8zJa0P@ zyaZkm{<-W|@F*We`})J3?!a(98Kt@jA?;qLj8KZCJvJ;#vo^obB*wI)w5@5Om<`15 zxdH};2FNrHXDeRLC~88G_r=B3}!RXusI*1 zTCY~LRfI=IB6yrG#by}IK^^O3LbKvM|0-4h!M(ofZESAVG@X&ubqPNC1}}SxvYH(N zGV)43clkb`xta3lHl60di@kB^ZEl`gjwm%Q`9ps&8-Cqx4S0J0{f(WJN)Ag)+$jFk zAR*-@5+3#K*M9kx4a_S8yfwyYH&lC9`Ty?Mo^jFd8SFMu<89ATx#UO0FII1N4%Ei8 zSh4me?koADCz}Ql_e|fq1P?tgfW>I;EFbr4^oO&&5v`*LBI<=1gVZ#wi1_MAQ*?+f z2>qI=6jV=NK3vdhSG@b=@kMExS@R)ex#_{GxQ*I>xxl+haXZC&;X5|7erp6h7oqV@ zhI^TQIYr4t8q`WB($s6>J5=CM91#=`k5&vZ4?5GbM;~HFP8=~G7R|)^K;D_;#zCVM zuip}>f}VR%juDF4Mb)U2tGhQScPXGS47v9ubD!09-Z+Uxi-6zbRCKte6Ac?1c7YM) zBceOUOYZ-E1pn>Yt)hEqqz3R4u{7Blr5w^SGAhJG{j<9;q!sE@(^!4z#>B}LPkl~dliZh*(Oz)$#R_( znoL67ma~x!SOf?W0SKyD6?qM6Q||O2K3@5bEoY@9;&lUOz4n8tLq9vY&4wVOfB=Ed ztPYwd2V|C?Zvq&m-@1PloE3`ZPOxJk?FUZ-2MlGe4}L4817!`TQhdhAP=Grv`SLVH z1>J3sJtk&*O)_999KKKDQtRP<)IR1;Kg%d&N{SvT0mJg&&&FBqm=h*(Q&KeHkr6b! z$290-SC`|qPFJPzYPBHi$^ZA@|KDLdmkKHul9r=UHQp4bC8=^$xwm`Fx;;T52I%Jb zR(X4S_q-i@YqQsh++;N4$=tBft5?LdE^4o!AOzz#?*dD3BO&feqy@_+CDQyic<@Rc z$Bxi$cM@5;7>Y-L{9wL9;lm0Fa_PbkmW@+jD0QQ0oUI&43mDwjx`_fU*D){(qESht zE~XmO>JPpj9y#3&yV9!Z9UTICcNsf{mmZRscowR^&Xg*3k4_q+cKzMNb`-eqDFyw1EqLQCxF^-t&^}F1j<5F5kOX(C(aUXA?isUA4SMb{ z`;FhjCH55oPtkg|(Z4M2zw(HUc=UU71-0U(t#Ad-t(?RW7q@XF213G&fRa+97RbK6 z4ywahN0_IW`Ng`g5V@1IJhc`QdgcCpL8hz_I)~PDHj_IN4ql% z&OY;cZ<}k26CDKs+eQ2=-?q2egG(#|ldw7Eldmz}1nf%hYO7aK{<`JG>a)<+gjeV5 z?Ej6o8>{fXn+A2`KDXUSxE)_Df9-v}+YLBVNq-k{VD)%i5)mIC-qyxPnc`pGw6&A< zJUBSC>3<9z+59oJDh|HY8PUc@)7He{cBzWXhRFixe7oo!L%@SZ%-^KM$>$f8mzU`@ zkpY|Ceha8?!2*>|ijU#^)WE0X``zksLh$v5FeJEL@q$FkiT@slK8Y!|ubAF!?>f@y z&PgjIA1z!ig;}arWhSs}IIY{5^KhZEu<4k9%O-L;>->v${kzkc40@RDqv!q-C?!La z;XQ9!Mboq2SNpB5G~(&sB_*V0jPy-*7<6$i;&TA@aS+#WPYdrW1KB`af(yu0D`A&E zeY3?6*mobI<)&aU=P}auc*qf;`kDH?@^ z`kpEWeZ|VehCWo0$&H?0^O66gvytv#rrgTK!Kn4OUkJu|1iK-4Hnib%t-5E&WN4FM}X0 zl~#GfjP$P?Yn?ue9Uhcb`hv!py-0Dl_!^wE?Njwpc!-FR62VIr#-hNpkT*VkK0hqJ zD_jlC^vae(=IjA%TI_b6(D}WX?DA9eQQ`hrD)luhd?GDDvP?J{-qo zR2F_YbFg)2UV>9I!^4l0Guteo67>6M_~&f#EO~I~JKWrOV9fekWEbNdHzV4>u_WD( zin8&B9_b9={?np6=80kXFk;4aL}dFj<_yWh%s&jE10BtqoP8%AXboYuLBGyVUAr!? z+osuNzs4pd(p!8i+RKvUd^GCt&6kcV^1k+r?J*Mhya%>oSsjY-ubwJ$7E4P{XS@IH z+T2!MrL-8{-`96BHnDnN;>lxv%^XqnU6U8viBM2)RpPv>$K(n? zGvGP^ocAk-JP*1nP-jXT(7cDH6yk~tyDfhk`0Kp=q%R{_yWhx2Jp0TNe@{SZ?v=F40p$zoz(nM+GW6oZLzDpa zILs7Ir}`F6QJvW<>Xr<7Prv@fh81h@$uaTR9 zf1ZH|SaVE>0N3^6qO15_UvbbpZ*FX!h(Z%a!J2YQ!Oq@N$Raa_ zcQxZ}31hzv>0mp~1Ou|~0`A91M@0e$Ija$vS0V>ydS*tp^sp?|vsfPqlDK}m>mm&r z2`5M7Vf#kyaa6TSe;?fo8K+`tB*h2mtzkrQVvZF=EQUv*@R|rD@62H3JX|q6(08Hj z3MuiisPCGO$LW)xL~-IkDUBS4ANPw`p>%bvF$CB&2VRzAj?^+#b^j|vG+dU|R3-u*wcMXIMmhLwJO*MY9m zZ^3WrJEQQ+;5=N#XaJ`fJ$849Nk%EI$;rs9zaM2-!1ELXy`|WxIt4l>`#as%rWJZ@ z$zd^nlNi(NrIW)^x0@lJz@KKg%pBaXEh1mXJgrNqn1ky&y$$nBXX&1mQdPw#>)y?& zUDrq?kG%XmAp;C;KCU8JZ^V|cuoVcz(vB6!4L+#vnk5U32b zi!j=d$+l2A(6=sc2q*z3H=fagX6Hc~Lf8xvX9j0U0G-!>`nRKeg#ug?cH-RlM``K~ z1XuVLaVqmN_(SFbG~Me!@2p%r9`rSh4|H;;52~ZTS}ao0qP#D1%dtGiZNxA5|y`KgRAF1PsLP+%sCF}NzGk@RK_&jPYI zcJ7BwrynyZs$wt&*=W25gc%eUchWjQ&iW&k8%2stzS<7gg)kk`0yN%$Hf745nK8up zvQB#O^qo#$p0H>M=&9R7?DbA{x&$&IN8U(Wg(SpH71n+6$=}q_XZ=1GFIbo#WfmyU zwnwj0&mqLcdeVxGMu`w7OCg-RI1)`~CcH&m`8i ze%Q(t7%o3yNT8JR7!Z!p%KTumxf#(FK$?;i-tfDWQ?!f=2y7UQy_DwUJr$Pj;Q#{& zIn+Axh_HerrQ;T*l#-t@?0sp#bL(FEoFW+a%L3Vv)2!$%r0-Rn&l|pzAzZ?mmLp%L zQJaWLpVO!|zT%YFh(D_;BGkjdtaK`EokO%P_9FM{E~$ndlmx2ZFW0Um4>6oFj|{w%z@z6 zQ__8jB{7wt+462X*D%S{JJ5IOhiT^CEpq1sXK7%Jon=Aukbp}-`tqPM|I!j;HJBkS z9YmDiEICwgw89G>u=^R=HcnWJVTa$pQvGvI&|E)3h$?MpKzJ`~p4z;jr5F~ro|8*s zU`8#4?ge1*sedu8$C=eWqp89_o!!FJKy~9+J?5-a;qO(-Es5%)Bs$ft3o{$JkIl}E zeogjQVSlFTk=Zz6RAPC`=zGZeXQL6Orbtp z-kaVm-PGNVM_h&18XqUUTF*6+yEHgXX8b^-(?7l#lnXA7HU>nt3SAxxz^35e_knh!aJ=Q7d zV7Ww*egRQm2)=yF41p|rww`KYn?Cms3NPYZVN_@7Da)5WG2G<}EK@vu6M}y~1ux`7 zCvH#AS*+8CwJGulV$ReR2I_b=gWMx$=Ag;6MmN_>j-c%JuQy}MPSIUlzrNlyZsY> z^g)Hm%q%M|J5}`H$GgA}(Ay`1UNDs5zQNaqMw`0U09&lda2rg?e(pkK$fQMYJYfke zZDpqWpTLna>*jjftad-;es}8sfGVVDtmBNYV$^=R{}Rk+d>Hc#yR@UBLOiZ1bJN`b z;}5bpOh^bqF2NuYTs^rRr=LO5oNp5+r!kKf(N0#J*e*Vn8YP=q{FQ@Ic6%|lL5$Hv z>7U28*f?IO_b%KtshL$-(vA0>+*M1;Y@>cgHrGmNwz{aUckT1w@wzotbls=>f=Ao3 z%Oj(naYiLH3#VZd#@0UCx8F%k2KF1CC4k*hKrv4S9i#xqfH;IZ2F~rYT~cZq!mNX z7aK{d##=#Dm#hwB!lGN~vbR&OHh?L;dkaDkdn_YU?0iHPKd3$I?VW&ofV7)q!{s&9 zOV>8Bi^|Az7GzaPw7JF>4gMxHIUmnJXrntam3c%=r#6pI_Wp_mK7}K_W2DUyfN};5_UY81$>RVdFTH+rP|t<{E<8LwYx)p2CsM$KGrWb6VX4w5nQJk(Fdlv) z9}ZEMHS}0~`A};B6S0D;Ix*Ob<1LW2?!X|Eg&|}ZB@Mc@q!yvWi+~MUQF{ff7BT>@ z_8kgkHwCO!%0qtK=}5jy{*K@cndM{RUD7RdZ)UGxvO`n7=yxiqlO-~36t=$T_QY(Jm_b+fYxb=kmnb;`-cYz!R!Ouf`iV|T(tX~} zh2>70q7{m!-*rpeY!=POM}C^lC%(bRk<$&dxTLceo0ueuBvTfNeg9%8y`im9X8its z_i9_)#;KlSAnaL&P-09T>@|9!eX3C}>YXDqD`Zyl(L#2ML?$}j&a;dy=XP-xhADsP z>^KV>S#MsFby$`>hHCf9WzhZmY8G$vYekhUWQf#rwvi=k3^To&qs&eX_|A9yRkx3a zuvtPOG4VB{I6xKygE_x`@@%ny5e$4uG+|B{|Q!1T| zinrv9)r6!Z1F8D`521>A+iwUnR$}s4?sZK&WiR{dvWO?Y6srtbajNyWN=ugcZtJC@ zdiLZ@$t1IxrfL)zuxD$$l0TihxDI8w6RLDri^vnLLtCMo4nQ;)5*gZY!(#D*k8^77 zZ`(~qwss=s6%HFQ*tENMaeXyP(QRiPqaetCf4#NDN^Feyub+C0-wyzhYx`w7Kj8h- zJ|+6y!HS+q(rxMJl9_*^a4>*6@I!*MTZI8Wp%$|$dW?k&ArV1-P)dYw2C-eL@pDo6 zO8Rl^2YoU=UUSibRcXa<`^FEq8#E|w(G{`>4XIGi=-+?-do=0V!#%XD9cI#-67I!)dlw16UJ)0n|k!d`oQe& zAQ#e`CBa9Re6==BGh0!C;R{T=#MR|YL>e*Hj9eUMk+a^Jgkc&6@&Ay^8&{$|ttQ`C z8uCOR8sHJ)#oHtej2+FQ7yu`dYF0(jDfXE%rJ0p`Ti%h7ppDM zXc!nl%}3nE#~pLm-N}JnF(S8mqS7-p8c__(fkLufd&?Mv7+ z&TT19$Qtizez)G;9)9?^fFK2iq3wwi2q$8IQMs4B$@O{j+2}$5@%a6w z6w)!ryHIfQ@U6Lp=gA8)N-nB0wF$ewzK|axvYn=i2sCw_f#cE-P?3tzto;&!@XuEg zsW>9BliJD)p<8V)(@F}>j}N;d2NmvheuKW?FFX>mk(Oh}d{-AIsL_EN+VYuvXg@Q! zRqa7+$BP%3e`W*lsN?Y8^DoC7whCz3NPW>~q@S}7#D`!T+&`Bgpk}DsbpUsVj3B4u zG(2)fvnG4M#j}p~4^!-~ZUk{yr|C`}4+4ve+LRzAx){=5=n!o`kuh%KdW%(;tOB|m zO57I%pF1ghpcKbL6^BKG)*bei>GUjqnt&yF#T-LmF$YcW`Z_5S!NPE#1%pb3xeOMI zzH%pku;oj5X(=xF7ZSlpO#4)7o$t-37!vk(9Q(`z3q+~1zi^2*zKuMkvYOrcGT!@R zOc-=UIc02VRBMy8-;sQ?dAz0_77}wOUupM)VwkTQUd)G{6O4IN$l}ICCYY5F*+S&K zVBruDSV8w?z-x8Ii6;hc9EAZiI|S;SuQY8#QzU;LCf9G3>HuWvdw?Mq;|G}N5CxU* z-@hZNN`iDQUFA}%Kks`~v+nko?10lTt3SC7PFCWY9YN-y>OT9M)2pLLOI>S-o4!z2 z*E>F;yPf_f+J30IJz1|^V!cW=IP9>#lR;}qThx|M6Cf6@dV_^EAf9zq9{@PgE_tZT zswaJt{+4uwTbrji^D(MSr`ZXY-MlehvmAEV`Zp5icf$aGcqq7NGE88n5}V%;W7S@V z!@|&RnA}WhA3kuRlW=as$0S(Cb#L>qesx5vfI=6UAll`aMMA>}mV%OE@dJ->UxUIq zohr(VrsH58`FbCiJ(ymOL=L{$U`Ea7o?YSluzUs_4Gj$v{wLta*jnjZKpc4_>PyR1GJO&T{0H99EkLmetbA{9VCoy!xRI$e5Vp}PI0TGvF z&;Y~H(sHk^(B|iKa%Hlsb^1;eT|^oy zOfxJqYQa%-&7ZCJA%)TJgkwR~Vsl46i9PXnb_?+=lYnq+kXtlVzoG zmD1=egoGH(rw28E%t4&#;0$n{2y>_m!iCjwE_zk zJ+HC2#7IErEf(!rttKYxu4eJOLURxvHPnVTh0Y}h^nfq@XB52hZM6! zg=G)^j?TmoHe<2S&VUooA#oWIHwL1Bmj70`3{lR&N?|sFh`L3Y4aHwclL;zc-e`Jp zGJi2BF*i#US`YI0cHM1Wa9C$|N0L;thSq*}thN_hU~xx3lFRN0_(^ucW`0lWcDSZB zIy)ui&7@{)>mKd`z?<8KE$Ye&qF3+yY8KS0r zjHB#ga1yjh5Jk9`6n5;>bYJC05pN4wTe(XmAm0m$tRG@ZwPf|U%dG4Z1`1N^J8jo- zi+AiAT9Vu}&Q$KH>MfEgb1532Xq1Y%)VkCvQ#P~$%+xr~p(BB9SqpOrY>1~JfZNun0Y)xO3I5yas9 z%*YcNL;3S?0v~rp$5!X)*>pGy8j#2TvU-b{^MOdik0TZ#`(;cZ(EVt>7@A8^t&>`7 zKwaz6=jzs=O@=#$SEs;4(A4dSG0vzp64R$;3&4fAdo{beoTpGfT?Z86%ji0{oJ=c- zIB3Lep`hU+C7pKDXyS8!bcL)_8q+A^)eYbw@%!N^x<*6-p4KXVF5UM%ns>k78Lw%Y zjmzdmUOo<;?KmEtV~`EADB`w~orL-$6%ZVII49~{%XrPzLUDEi0D3z0xI=vnuU^k!-@ z50tn|9bnerz{RJ>P%baz8&u-pu{pzeIBJJoa&}|nazd_!Na}Z{wt{of@I9l-$Yzru z^R)8_>U8H1mpm!uHQR%E{Fv2(Xo@4JTD>MMAD(9IA!Ga;< zmB>;MO6hWpy>Pu%C^s?Fnv;of;C#En7a>_w6trt=d^X=)rc=$%358kY=drW# zUcfF;ujP!kF`%PE_QCa|zxUOpm*#9a<6fA)o5ON%ZrfC|7@@=xm#s3XR;`iNoxXr8 z1<&W>UzKlUwikv$m7rMNv?$*Wmm30R(%-!jHh#N~GEm1mbSg6F5~XeV7&y-HCk2zG zSGY-GAXKi*fe;nd*a~c$Fzt|rq$vcM!w9}2KrwPw>o;PEX2Mw4@MkOx)^Gm2g3n2k z=jAh{>-6pV>8fR#^r&(A9dIAOzH%9qIb_Z*Q3MR$pl0`{I0Ht8*l7nG4*7mrVbNHY zp-=LUCd@-W)1tq)nHW1bl;Au_AgXbUQ*eQ=3=3XX^6Yt5c)dyF_T{C1-Iov0zkiBz zB8s%Ai#0Tu<-NWF!#a&B*mGbcseXrztc&aH%J+68@KP0ffGEQ zY}X*Y$=vtOR-f8xmpigMug|eV*MMviWjjS+VPPS`J{n1BQ^HPK8kHP-jAQpy>YCZs zLx>rM-*tUF)D(DFAE596kXenfM@#)ENxl}zPAVD-K=RwVfIr_V7cPz6f$a-8{f*RT;eFc7 zO&2Mp*vJ|>1qJ#5j!*njq2y1Txxfk?-9m1SC=^@l;)*7Zlg_&sK@4_0j+IQV^7=N`W$MA2&Kwj@ltSD@js1*l?V<~Xwy;Io! z(xTr>`9J^l+6`|_arzdWC-irOzcv0Fty+Pe%rRHwZ4LtX-j2Zr zQ@P+Jyr&mNE;^0+8R-!39d~uvw0d?1SGb}*7!B%bS*0x$okW}?M7oA0oiF!9cyAN#XAN%_ss7@7kSFy zB<`G&hjpYO59>+azwkGwH3OD_hIsL(y*yvc0{PogbnClnLP> zAg9;uc~G|vPHrLPyb3a1iRXZYBk$4|RyEKCx4PDgXWC`gzsiXTtll<{jdMmtC-Y|q}y-$2zRel57R?qce)c}*bH9yE0F-aU; zmI_QXDpTShSW-4vSK~rvAs%ZpUG`F%F*gJrWGaCxQ=cQdW#08A_7Y$w&uadTBd|fe zd>N~qJ4;OlNt~HOkaioSLm3*TExLnsc{BJcFqckX;D_8$%~~)~BfL|j2F?7m)kKb( zLlrXN{09z8+9STv5junz6e7Mj^k8ABYDuO#i+#En{Ap}bw}YapkHxh>CiXz_G`cgo z4|0fbUEQBed$8g{$LM&>oda+d6v)Ru{!)Efd5AtPp;``bLm$h(d7r9|#MEY~!H}#4 znJoFpB(avB9{miwJFUm0NtXuT%ca&1(C7>3^&o9D+s-G96-EUds5XUk$7LU-_SY-( zxgD(n#ltFH_>x1}BjspbYJT#%fVxQrFW|l9f$=agNfkG;iGO4@S_uqLgpY0zDzs|D zrU#?))^WvjYAMAu^)>I3w9`@XrV*QM=X&ncN~WeS?YlCOX9(K9E5=YgQLctFNvq2O zBO8Md<;sCnGOsx5^%sD03C*s_a)Ywt4B?mWNZ~vmdfMGcp4+@gKB|S&^**h&=rbjc zivhpl;lgQHJWh5~kllp~d{B`myGrdVqAQ9^J_WR70h*RvKmE#!?gg5Q$H|1|LMd;VZ&qiS`ezDp$xFh zFicbS%^Rw)-}k?{02-7j@@5Z`#YN1hsmvH}?;IPEd5G@|l`7YALYcsKl1zH2h7c=R${Ej^VvVaXnkV)#dSX?YZaBDGw!cuIY_2n4M8Z5OMlzp}OPOqy^;VkvF*v9_R}P))i0@I$i`^*@Ee{ znsA=6awN~t4Ei4u=mYME)?8vQQ=LIBZn|eg{BV8K->M=TWG8TFdQ%Qe+zcA6Lj;@- z*wy4#xe^tK>Mch|BDg?<9a7o_!{4oTWDA-qxGshBX(S2MsU%uMj4y}ddgA#d9GEQc z^U3p4Y4%_FcndRrd_^PtYMe=8&!?JzMkS=V5HLa87l)X1dRU5p4`EQsD+x%n_u)!o7SEnr>4P zf+sidKDhmhj!QhrBS9;_6-1p19Xol@dr&F7@fwm9Mzk%e!11K!Y1wSDC*vbu9`4O$ za;^z;F0uVAhrZ3D)UIj5fZsyn{>%H^oEeT-5ikf$1xlT+~mBoorkd?|%k8 zIcRJcMmSXUEUt!LleRf{}PB2pa3WWWV57+f1AeJp7+;_K#&Z-Pv$vS5p~wjZA#Q>csLKhqiM zzjlCH1}wBWI^Cx5Jy~YycN^Gh5UUPr;Ndx{ICCYU8b0*nDMiS=>kICDP01Smxp|Ok zjR+J5w~n!W*^UnY9cc$XEc;}>!tiROW@{w({ugJqfe>U|S$iHdL){Ua5q#r0CBybs zGV6&X&VHG3#r^nh@km`vdq)VWQ)?orvBXVKzY~J-`uw}*!-#>9IYCmfI)G$qZ{4uC z9uv~B^5rtdnR7Uu-j`Y=afxYSJtXUUF4(#!-h4#?>L;AtxuTL1Y8*zJOg(tKg4$$P zf#s8+I+tBjkx2T~Yl0OQ_YmeOpI&jzx}-w#ITg)CiMZ=9a@Ly4=crXz4ENlT(y33V zZo}r*MkA3lEZhOYDY0r#fA)LxH&cH8$a|*0v->vr{emW?gF&dfH{N7yy4ZwOI*T&} z$7!)Fh}ixE!>TF_-tO}9vZfM_T-5T3l$XQHkf4ibI${|R)8EZ+Q@QLcPOcj3TCJ{5 zlOH9)yhlK~gaZRO?!G2CIya8$FPSWWts;Z_LjZ~qu6LjImJh$(bUWW80Bp{`gx0}7 z5O9T+Vt>J*Sl!nX)&^UdtIDt3T(sQ~MVj5YqSJ{=7B)$W(3=&#o7$?kZR~dV?a_i!Gk&<|oms@TeZ{;xx3k=#H&n_C zi|H~>LHBmbW)~Q)XLdx(zJ?b7aCbQ9)EufIPKe!8Otv*tVSF&6!#dqDUv~`{D2`=R zi~~@X>6JjLzCB{F{yHF?e*1En0Cd7Z>M`LB_|_^uP-<0@KINN@`I)zW$Ww}Ul#UFk z3KQ$M4h+m42~0IeSU%#(%~vdoU!lE~QvH}PZtv4jdJ-d{7o$3`LPV>ogc#L-1f!R$ zvVKByYDY8bQ0c*-!g=(jv#@Ml9Jqphi1%drf1< zN+}bcCsWx6asvIn%%)P9h(IfG+K+bMUPUd1lip_I(gv=1Q1{jgMwCP3mB8G36SQ5Vl7xyhloVTRNm;z6a0N?@*Q8&fN% z*|1uL)qO%L0A&zYZMLC}M(&d>fGG|PQYESq8;)M>HlroxH$_xyXK`5E+|@*9>%sST zSZuQJJFy3+N+|)i{e!xx$-M`*F1L5jgNhsGXl1Zcp@-Hv2P2&zAFw<`Mp`&EfGSKx zK01$Fklq_|)Tzy>Ds+36rHaZ)hUTdiINu9DEJ2mG1|7_>Q@Pxn*Ne-C@E7y+_Y40v6r~+I2Zlx9z%n*dK9olHe1N zI(TUXf3zqynxU=<@6GxIB|ME=CjO+E`e%&vYr!~-iK5>!oJE~+JOO5inj^p-lawMtvjLmg2{|kU z&wR6rQI$S5i1A30kwqF4^I-Z}dM6?bBOaMw&|}}ZRuCHImsTcrL`M?ob(^91Lgb?* zsZl+Ka$rcy4&K89c^u1b`?YVG*3Nbo9K2ANpu2jC0SXy88v;b=U?xNa=fY5MovcnU zY7!i&%wWNoKg&EGG37duYbBrl5!NU(bwnzs^4^`3>2ad;vZ2c1-q9y^GpO-r15z|< zbmN7&`&3Ca(Ylch8QQEoG#`Ds$r}!72fC;`s+*s6N6I9h$g3e4D1Md(N$l4x0UqzA3lo~J9l6flT>^E~iUoLH3XB>jH1U@$84_9t)P zM@!i_b$a+}Wa!VKdm0`|bfVV6RDJzjDxQSe9luhcuL7c}N~c^~w}_w~>%<$N>MDw5 zpfMHiEl5!`Yq&bSg32&|Qno4BCaDNd?O?snO>Ia(X@q(sc>(8{ZA5o-kzj=_d3x7I z)|G2wz`X!!!#vzjB^tk-bW+=IZykh`55n4YSUei7e+!O1sx~YuwiA%0gCZ9YLO`vP z!DRys$&Y3ZoO|pp5QK*ky!>`mZ@yJ)AiiE}QTXEa3}Oy$mN{7`zU*B!d3%+-l-1!p zOI&V(LbV1o<7x?OpyhkFDC2*c$pz;tTgcsW32D5A*^t;II3OWhd_;v$uTuyXKQO?%c3Sn!1H+K ziZ#cm49=Rq25Lxsi+@2B%s7xCh7q!vn$7pkA++os_`~J#%3)f)B%MtckJ&{wpMT(H zsuJ9I`4*_>durm+*7oNeQ%Z;0LGL-a%1^+El9@ArR+G89Zp|$I(fICJ=7`yeC0wms zpl|X@`n8I;Q{bPzh?@*d12(C`-S*_^2=}6t{~%_gr1{^c2L+~4HpOi5mr!mh+Pa`($L3$1v{%@Lz~A;ajn_Y|iV0fGT~_756?6Z&UjT zHbBL_m@25P{U7}j*_^DT6uX+zs4ELqr8_VL-1d-W_gQ8xOnQjhh_VnOxd!v`nR}cD ztM6o%f(KcP7>5BhB0)NXn|lomGFG;}wMf1q=izde1JZ{t(h6mB++vDGHqK?U<{D zg{sxK{8rx-Wa}hftmJ^kDV?7O1*PlRQY*vj_LbWx| zX}V7u7RAr`MK)hxFdzD$UAVFk3`|hI)zARFP%Ayw$x4mzXGjhlU6=+<7_}> zB`X@Cuhvk{Lp`>PQWO}7eu7w?mOwrT?9<3_1=;U|B0UDVN^udO=B7iw7-5}ZKN>gZOw zI~%uVYq%sq9Q#hJ6RQeJLwE;j#Mccz#7f7z_#_U;?Z9w*^%wpa`XtJ$*+POc#-Vm2Eg*ODP4 zN&U*}T0R4S#J9=#eTLPMAnOu_-o-R#)mfIKzGH>S3VnBv50g2I9l8A42H2wO{v9^EdX^B@QxA7mEdb$xP2uL`yTpvgi?@(kEALM*ii}Cb{Xq|n+*}S_+_R*t zLEN&1Y6Ewr>drE7H$HI=w}MO(2bo^U7b8_~&7=?c0=y<$n#z$5GD&Sa>T2fl@GDK_ zpD)35X?EW(|1{^?wR%0|#C59_v;Xta`bk4`TgqlyJZyGZl3v)x9wFfRu_O5%$_&8x z=Q|B5OxGU_NKug3E4S^p+wTw$4>$zrv1mvD-8pr`sWcTbopfX5f}0bf6r18(4K3Vl z!BU8w<4592?X}{|+*TC?SgHKrJts!X$aNBhr!mRLV;u-b2CWriDt*R$6gq@S;I@V5 z{CX>jmTEV4+t~#Z`Rlg++*d+d5vi%D>3Ui@cc>$T?ojQ6p)ri?wfxM&sC2uKk&PdS z7uLo$_NPstB{GdkXxdq})gnyjlTf31u^!|Vm$yp`)eVgdAbZ)ev-Zf$rsg(HnEX@0XB>_1dh_HOKS`b>%i!gPBX%@e}_3W0G00ak? zbtFQdnlU8Z2%ccIQbj-<=I_jr1~(V5ncy0wLx3B-&&2`5)xjdvJlnsV(Me-`M#-{E z*dAfol6?YH*UzZ3U=dZ)jf>A1lZ_gN3#~319s}or>kXDcuy%~9(GCo7)j2WztBB62 zp}Bm)QMe&GVsd?9#se1d_=+j)RkaTB`f=}J(lUi-zerF_G9M}8vC;gcDrfiFbIDk= zcf4UKZg^|=o*W_p-Ek0+j)^5{rJkAoku00&4%yTG zB0FNEPZDRtqqGXxS6;(88t<^=n&4lEY24FW-?1H+uC zOPXEmBA;a!A}FAH8EbIzw#zGns@@QX5j7z4hemho_pr*@;M9@FARXvviJ#;z+rTEH zBNq34QBv+RI+YA|an%(N4#5}b$%_3_ zeu&m)BoX>36DNrGQyd~&RI4P|4a+EM5W|nG8nqV;&Ws5i)Ee|=G8;l4PkVq1_F)dv zaDj91qsJ-hcmT8pI?Q%0=HBW-_tVx%P-QRTWT?{jUhsG(($=*oNnv}3G1Fe&Q!S9d z*$4&$+clZsqoe{Wk|2C%B9<*u-7Z`Co zLuP8v#LO(eroCUhgy9tfaCSMm39$6e;TICtY9ju%Dv#`c5iqvU$8UdIl}+SFJ!Jct z(6}>Ha>_rhMo@%}zumGOqTt5&qlt6_9S`z1 zR?OoN=`Llh%y9|krWd=MB41Jb!szi`CCU@NzduJR>gNcAdl!+~9w*k0Fwdso6*oX` zPdh-Gd4wtsLX^|2Qf&g`i9*CUTH07{>e=OB>D_MfJlM8)qOUBX7YLN4$XCp1pmoN1 zN!92YaYac-RgxYkQm+c*?x6gx!nH$@<1k4+Hq{Mvj%Si>8RBemQ8V(@LWyp;Q1w%mT$i5doQ0LfXAkCXqUwo*QC2H$3N><#7Am1tuFRY;Up~M1*@TH<>hu;+s1njEHq zx^aX<_Q1RN=cw)|Y?$q*(mL_ZhfB_5yqd;1PIT0Hj_h%7_c_D(sq%{#sxwB#wn0);C?(GC3ZuwCWN|AqGZTX5Gc{$;E_QfvgvKO85uteegSW zjjzaQr8V!}d1ViO0Y0Ljcg0Lt(D(1dmz!TmV}+n4eH^A+f^ zkptcV(3xBRJL1=60nc^o&${%Soq(%cD#~#(Oa$wuX;DMUwj- zG?g%V9l*OU*Co#Rc#>P_Sq@r-c1%EX)1#F9DmHDy6NZV!8tuj?sJ{XX zzia7ubpPFLIFJxeHiH_C7Tiv4@7d1($kWn5c3kr7gppnzM--idoUcR$tBb5^@Az_F zght-c>(eSGf?FEm9Gp5AI@F?_nr~CS|J&bw1xc(AeSaN~bi0k!@cLe2jb!UPlZRs{ z$c=-KnnLba3TAW`OKB}d^#9=`9gtiZkI%vli1;@auaDstgPpVdJouf6juE%br-iC=e4U?duCi?;Nx&;5vr)A=JJqO}f~niv;`lj=muTfE|G>alda zbyhciP!wFrZpQYT{r!b81lsl z#YDOW0RWP(q=XBqoUx0esztru3boZrj`?u?7GXcT7k}M;)znes!TzT-`oA9kBNliz zxoeM<*ubbUFB#bv)#T(ykCR2a$pkf-tC|q8o!x330~70jx&qOBosj!djS|B5M|a%x zZdOH&9`6m`Pr+WC;I{L&p^Uu8VfiLfB2%k;i-1b9C%y`kM_X@O4 z6OIWzcY2r;Yg9iwSEvXFA_vgk_w_HrU{Z5>U>y>=PEQ%JNbKHq6-k@4IF%&E*R~02Ch`EBBpP>21`Qv*6 z_TBv9(MD&50}sLbzP?D4iSZhRM)DW6Ny0w%O<9qY2d;7-kVD>JxA?bPyS-x%Cttnk zSTH)OO~J5?aK^VwsvW!67UrcUZ>aRIr4eb42L&o)n33$RP^M>NaRtLuXPp7}6^rh5E~l`xm^s z|H@}+BhB64I`uHQYx%olq!i}A&w%5Or#ic*yX774A zg0~|O=V3c#(x81;f3$FJ6z^hx#IBF5TK$a1yP-=np#|UE_+t}?$or93Hk&&L(zdzx z(w9!;&BGZa4Nve3Wi9W+2b=|aO(TsZ*T{E^Md5^8C;0G#B1NWh1$Z){q4|HQ63n&k zkEIj@=~7}SO{|UY350+Oc#oC;&uPqwt!$d^TfQj2E~s0cE8NX5lA|86y>DG-?aexR zFN=>Wrw67-c1Y%&r8y99bVr_B2;ETd&p868yT7lL_lwAOB0EzATHRClCo(zb zi|GK<(U}N^Qb*R(sdb-{8=|%9_FR_^>VH<5$byhSTgpB5!XkkM(yceKyF^XgtZcud zV_8F{@-4hmXMyKq+XYNkC&o^1xo9qDOJ$WFF4kN@#Ze~-iDS`86^nUpD0)9O8yo}B9NXVH!l zIZZ=%*g(G~-T17FmJI*T2r#lMI>+slbPP!Yx^G7HLAMQG6L0SXQ)1ewe|6UkM~Y4b zRa`Nuq04AC3=B0|)#?u!2eMfNMCNo-?#C>&)tIsmp*&v- zC{&jP$0a^U83+dxkNNT(_HR<3cj<0%nNd`v9`%2R^C@d{&d0w0PY?gUfJ&mIS2JzJ zzvC|U)*7ndc*nHw=k>DL9Dj$hxYq{7}mpFkH+%x^z>kw#&DzglRMzF z>(Z3KNWw4y!D@3~2Pf-v`QU0LjUtMRMm=_JM1<7OAQ4%wR|Co(ASkJ~xOP&Wp^7ro^-` zOm*R`Eg>5skfV*PW|OhRPpa$F)^dBN>e>~nA9iNKl1)z z7XIfqt%Dibm|D5|;7dZC82rlwPv0?0`+I_nU`vsiF@HRMVNp@Vw@a13e;b*p5g7tuIr#3L4sot<`iJ zvIO5_VA-VdnAY_Q^7CsgM`4%tAA%F2$k%l6?IRW(w-wl99YEo-k#K6=UiL{-+e@v) z!oz?(lK`5A#4*1osc(4>7DV$URw&Hn=Pz%*%20RTqI%%?iD~I2bIPvV+p^-K@f%>- zOrzY3J>CNn3Vnyn82>zkWcaLgg~WKTY(hrDGgBaJvT=SiV4v)=fidENk*Y;b{ux_n zKAg`PuRnF4FFXBvhUyZ5Z-8??K5NtSp#HrbyMx%$O#7xR^(JQTpGXR_b31N@Gb07i zdwfx^>et;Oy>NXiqCT#~qDKu(!|`AwT5vI;*v+h#H1>ssf8+!EXqM74(ugS-VDjRw zYa70XltgrrJ^&`P`+h5@GPwr?wU*USC73dZto;;{bzG_^^m`XnhY#zAN1xaYEkv)& z9?OsRjenQ$ZUaF;rNG})gfqFj4*=sz61roK`j6S~!jmU|`uI&ai%XHMG)azSu#s*i z*11}|4{J`5XVfrY_b<9Pf*hG+CB(QsX-!OG4A~$7|biRo9iT=*|_2>e}5IP-|R z*>+Q;C)@F=;;0}y0AUp&bFBh9&#d+}O=8t8DxFv`vdg$FJ?Kq|+8ddTUx=F70p znI!!i%P_&fDjmW7yh?e~1b(t06dpQE#=kQNXI!S9K)NcCSZ z=_4)J%@00qjYKkG*VxF@_@m>gJbvQiwNA87`SS9Z8NPv?4~4_`#vrVZ)9+XmRV|O^ zgQ?A$Del*cLJy#32=r&EIH^|a9k-PZq{UqE4O0qKn*StK!kTH(GE5b3ruaHkBHLm0 z=cBk@5aYw9goGr}v#^NAD!CRhBlcJmc$l13T?5T&caH zj>x}|6(>)l?0X!zm|Y|6_1*bQ3Rl&jDd5tAMO*lsKhknocv)xoPr%6z|2fV;PHLdD zfqCaW!isWF)3gGM1fB(}|9xBwev>taObgs?7Z^QqHQsIVVSTYELUvV5LYyEKc4uys z1jl+9J+!l55f>jHp2in57>`i_%IT_7W!z6g5zLf}u9r_Z99D-1Gz+kzo;HrZ>|r;y zjjoF-zHV>k&*{WO3$4?bT<`b1tGAxW-9F!V;xFCOA`)?i%kkm|-86<%amGrIsi>Jj zRla)@x?FDb1L!*yj;rchM&W@&ypk_Sd>=ChyU&ct0EZ2Pc)u|-8NsW4eJ9IJT&Nfb z;-qWpitfcPo$DN&{Gj@m4QJj-kw5YI|vSC*v2fXu)-4reypT`}Kq7V?OSpzhhj z38S;yC#GH2OJk$KE4=1;z)Wn~Tg_zm2!C2&xVm!0&j;gJfp56R5egJxnLtvwpgC({ zic#jFprLVSNuK)4xEpDThR8u$w8QoXOuT^n-8$nH@SzTb{&s6#k^Q#)M7Hoc9i#P` z;t?j!2Sh9qB5{XZ6wvPhUDOkhkx?1qB+lX`iht`cLFi^bA>(1ih)AjJH2;2)um0KI ztL5*|zcz;sRTG%!S^-I==fZSPz!9kTz6LKx$wgiP*xr~XIA!uW15XmS9a_^%!bjzi zgYK-*rhBwmIi$K!3xs*HG3ZY|PyOnN^9pyUvVqKc=p%weDFqx@?EojGM|fVV=6R{} zITYYnmi%#jcnNb&p<~OV`nMz=BfAxZwUh)%5>85ic(*i4WF1`NpWzEm8hipnUBxA=6l>s zl{+(hg$E;UN++R33kPbmBIqPx3Smbis;Rt}-C|>%XGEdxV;!}zBWBc_rBna4Rl4-x zbBsBn$ZU#8h<-=MMYA#@eK))LM>?4i=K`=1$kB zt*9BPk)eSZcJ<@EwfKo^ljG z9r5z`C)ze_W}Vlg)X=-eOUbI;(Pss>5-0Qj#7%#Y<>5Zx3bqIt`p!@C<+_2i++C>Q zqyS*KY8m`P4W(h(^WrmmfRgOKdd^85X%0@cnm{K z_MT=H?z~+>1V~d+HP||qH@7W~f{qq*_oKi|!x!A4Zk9%kLAgp5!={?yvGP!@bhQ%I za=!92mzL(zdNBseM*_qbE`~tkv2=uAKB-~c&Xzj|)+j92&DCzBE?~B!%xIqd9(2>&xGgO$n$?F7u8yMF7?)0~{3&LrGH3 zc>ZnyG~cR01taZ`zTl2T$?^7gD3a12txvN1QySTUfvev)V7ommtgtHvKhPRbLV`9G z8o1ZZ{gFauG;im~wbB>H!jN8HD{*&oHIah%@{LI8LiM+$GJhNxA#`p=G9B@raXpRR z1)m!WHaVQT!2RYvfFu6eR>TIl)6*-pxOIHte~(~^y{AEprfgp)VElV@N}Sejg? zhC){hBVv#+X?UQnEUFzTV?u&$Iq!U_IwS=565=EVr!BAzNxP%?c{%znnNImrNc9wi z^jNjpaq_(PJT$i&xG*H)YAaO#vx|^Kyq%IyW*FELw|tcWEdVgETY&UEj4M#n4#TSD zPl$`*-qt(3Q?^Xp8_fWcC*XOnHC34I%^er`l+wHGh-Gf?sTKEHABrQoUaJyybH%8H z3zDY`8(hBHF)Ba_2nd*ORKmn=;waSFA3aW&m~Rx5eRsN$Y+j^UYOsKFuGd=Xu%^s> z3#ZAtZUxJw{Tq`G8YcMvYzlqB`g!};p#~~Re7!3`MUA8&?QTS= zYDH7CZ)tp3ACbIYAZIb=U^b(Pej8Qdc(KzyE7+fGm=TZGF@2@vWOw7&TUuSER<)Hk z1_VbxuSILvoIXMrRFr%D(lp{$Ip( zj_=b}kr%T7dT{Sn-~&8LbYp8$n4$=p-C6BUX*Y#XlFHQihWH2syHmm_48 zcys*yy}@c8zyGNIP9Y_l!||S5avDL^Se}kD_e*eO&$QB^-L^cHPdYo;<<$~ABo#X| z^B~b!^6p!9!4j61*Ge18^Zm(ZM@L6;GAIbhypJsuXkLs$imrBxh68#yh^{Lj~b|7#dGT{XDc=I&cy8t9SNkuol-Uv>fpr)!PLWJM2>2p}3 zQY#8t#?Ou2;#rn+0uW*py4~ec1cWS|uD7(vY<0sCLxeC`D;%W~&|rUhp+0)F;oAR9 zLKZuKnAGwU*MSW^M}e@>VjBDD&mozgS!hj~=(^k}9k39uE*Jt{IFooe1)7V?vt=Q~U3V#x^ z#HVh0i(MT2Y-(znnTzZHXi8ajq*jO=jn5S=_cHCV|LWec+J*4!wUVB&f*eV}o0?4J zd;XD1@@DU`X$t?*fdx#YsfO-)4+z;oOcJU4AyLqtif$qHE@83R2C+UY&1|5i6Z?DDB1c9}p&PB%9q3OA%=iNKC9U*xo>NLbE#*G{(@NC! z^cIZyqYqzT4t$@d_2$#+pUpfSD>l`#vGqX|ka9iu5=)PiCSA4tvZ=Sx%HU|Bf={qU zM<_uQLB3WSK~{ZM{9w7jF3K0=aGQUK~sM(MNeLrV7Qij21KIi2r~rTJ0C-`Q-r+iu@!uYYP`vi&Nl&9M$b#)@vW zlIVX8jf#p=gDboPVk!VKziu5Ii00G7dBN@t<7jR?CjrGsy8}pvAn)-iLfMQmwWs^z zTg{hiPY;)B&(zeJCo<;fFN#RzcrwqgR-Xx$>YedcI%ZKbnlJnO$cDF1Po;DmN%{nh zN--n-k{kxsV`QWm0o%3*+jykbF&Tc?#<3S(d;N^82!#8H|0 za`L+a2?L41pl*d;`}f@lbt)0--$V3QAdpmGnz4+tNhW)pJnd-GqKEB#Z;loF)_E{d zuHBQrWGfn}g5l_Ar&&@?mov9dnrv3!&kV#{At*%m%941mvAA9ZJpZ;S_r zP)q1SVTL01k4G?&?6xpVm_uPohjrfT^`3xyyMW+S+4#oOv1v4AL`M6vmNAvREn1Sw zRS0*CuXb2tg*(<6a?yuxmg`6bA;nr)GXSSRUzx$i zEeLG>6I1UkZAw6Ui`s@g|CXKN_?C zf|Mul)^^3H6F;n5d}?Ly9Q|*6*ue=MN}l0)4OFl5;3;R-UCkuw9_)Hpp<)d7hwx4$ zJUH6}*GAH>Y`HulwDrHpK?oKRKsphNzZxWF53)mB)>vm}Y=Y8JbdddV4tOqnE|gAM zO4B&@$eAUjgk{F8Q6IsHU$}y%bqshPs4>J)D>fycE7L)PLLwOITUJU-te^&Q0r4(= zx3b=egXTg=VCkWr?$tpTVWL|}Vn2uQ!e7HpxC=0>zVRp0*IlH4J&-bfv&5!z*z4J= z2`-Rrgbghb1e7ZWcsNhhzH3?f)1WPa)PTxWdI(0{Pl)}HKB92Z3+9_m2n;%HfiRmg zgBSgjB=!kwXV$fb!wMLV)v0<0NeHK}PjC$!?GTKNjK(FaDuRZjKI<(=^yP{ATKV-R zemH7sgW%@I+)>`tqx>Yui)Ntr{nB)A$-75#U zQB3N1UpBXNKgf&28kQS$soR8?c3y-2a4(`dm!q3h7J&n1WK>aQu_H*NEFo9AXVK3& zW$h=Ee&}JsE8^pzh`deDxt0Foee05d?=2^Xm5yLL@=huO6#A;ZhRr8qv}DgT~74-u}JJKS}(1##0kpknLxN^2gXeMn z9jII*a>@he#pCeZ6B%|)5F?4!Qc*GgM5(u)O@+%cf86E#N9FvI$HP%ESdJ93Q=H?B zt=rQq8(Y@lX{loU1rrmKu-!0yUYMLUQ(!O_Sd7^)iw~xOjtT7nk*ualeR#k_ z0&|TPvrBc8m{-5q2C-p%*`VDPdrE}hY(+j*E9we=qJ=|^?+8}Lo#&XIfhR9%#X+>@ zistP03TU=e=|_qbT70R%k^Y(hCuJ%t{1C%8aN!+de*hkC$zW?agGhbm~&Un2?`0NF^?hH zM>e};)q#?H;_crJbL;O@S#3Xy%4USjJguv^@UagOp$19qcqUSB$IP%<7M!mlTr*ek z@)qG8*O;Uew~9mNC$##zpL2{>!(A0%WPy|Fbr~i}!e1MtOaS`M(A`5~3&tVUla+)t z_&Ot)hFIh0XMQ^wjbzhv@t)s*t_}(^crCRspUOYzEP!7W59KTq1BybbFv?=7OVNab z#_GgBnN#ZJTgTUY!=mX=-T z^mF=kzVZS`(WovH5&wneT?v1E72gqu;(EyOw&b5w05pM}nwSC8a?KGzW_`pcasJ`w z;9P6HiQ{(Y+Bew0T9>Ixs@g#D;PZ;=fjJT&@z^^*%*|gg*W|b zTKgqVPXgZ1l^to-5O<${Lws{IzW=4?HC7#zW!mJ`1$Yum9> z*W{t>t_aX+*i>u-_qnJ;3Pp!P5qC8Lr)iYdu*A*>3+&*z$ z@ZNi_0I51e%-#yqjfo^sx)_d2b8{@4#_V5b+d&OI($}**1%12gUmhZ+`CvngZxbq2+O6eS?BLRoCst1I+oZwsYH=`2gRe}a`f_ww_ibL zC(b;|z;cY+pS~v~I3siWzGt+@sDdCM=34qqGMUa#@rUdFMFCK$z$*)~RA*Kn;bAq? zVg*;0f9~I4z~HZ0JxHJiZvnc)2YPKbO5Y|P0kk|(Wsqb64mDt}`aY)C-lBl zaaGi)^JgMQ|1_G%$+L8*O{vai3{kU?yg=+BW_$1Gr;2a2$>{>nL55D$ScNShEAi5h zb1_#z23!Yb@bK+<>ne;jG%P|b8NZ&$PH|a9Dw%2sV~+3ouuTR4);={e0p5O3*t4?# zg8bC{{-9`-&h7{+n+eT7J9Nh!Q+Mrhwj3kJcj1q(%rNLI9o}GIPAM>`$HL4efH>rs z{>_s?a+drKmtLtd4MO4Xu6S!+ke?##WO>2?YOIChikyB*l~I4ZX_Su&uIU=&E&3Jg zTxWSMnz`(#tKlWC4IH2JRd;K_ZBQS;*$e#qCC(t2_=p9CT%yDWFB%0*-ly4mJwAdE z#%Z`M07QZ*Cq7I;B<$%52$4@prZnn4i?Q|1jFKhLEKvE=%0vaJ|~td0KX zD7?Bt6EjG`n&K~ce4k`}XQ4Y#phGjmr6ffV8Ak3OAH7ix2bo?gB#!=U|M+UL_Q~^s zfPefrLjC>lJI0VuDA%YPW4+znlplxB4geCK2#WGl+v!k_?8)@!?^3QX4i@#%{r(^+ zKTu3fhvrr)=OLCd-TaqzzO%|ouP?GWBFso;nZ!8F4!^-tr^96p`}Y}C({9u$`#i{O zM0vzP>a}o?0!eoUuAsdL#Z3hTnKl%B=qruVuefl~N?<{U=^=>Kh!C`7&A6V0E#Roy zlm!nmd`zuTY3i{Zik6p5Zt;7sw_^^uh~!H<%KPJaX}G<19F7l1De$m6-U^Ee(&2`O z{uwdJ*YqI}W6&0m7UmuhX=K_BtX!=pSm+AE4rM23P90&S_*qm~ zxDn#|DS_;j!=0NV64xKe6M^_CIo!#<3=Bh%CKwgaslCins#VhDK%i4=heGjj#nmCz zX0&uPR>GBJ`0MKUM9DIWge1<9kI1{0NI~PQ?r-dwoyD)s&J)WP* z(tpf)$Yj2^NTpnjEm$q0|4|kp$sR5#9h4y>3;$ZlZg`a4?fRyuT)irN%nwP+LD7-N z%?#`@Zre<$Iu0S+EvC8*YQ}!4lH3oMG?OlodQG%ot@XNOMt5}N2XQ3iyR$W#4+ZAI}|LVqjez= zt*5em(yqJQGeLhCjba&ut?;gR!1u#h&&_Q8ZaEb8L$D6ja&4C_$-_$@M zA?)@8%%$q>%U{DiT|)6=gRkC@7HscIftkYh8Z@!k{kS)|ZQ*uhT{B1sSy6DKeYR@l zbhMo_^;&l#8uSqa$9oJ#J$JCjVzSq|EQWFx&WQB=JdV)nerm zm#jK?-tm7=-5g@#8-`!6tlwxb@l6~2d9aZqfhJz^Rirw5t=Gh?&(<3*@eFs6?sgm; zV2u$xudjy!#9^Lc2xbCfOY%VYx?&6s6C;sjQ7rj7!qHK0hUojuA1t&d!{^f#VrowB zEtK>EEU5x3I4{xJ#ts=7b`5pL%@;dY`*w4*I?zw%*Ax+06)A-LBp=+hg+Vu<$s@Xp zqi|V%7Y*h?{`zq2Y9IPUq}buOPocdb9*R(ffo7XHrXZzFJ0VVi@wUNGM}+{+fl?0| z>ZZ+c@Rx@2UnLPX6ECVgqT2ttR6LVh$vMb}z7$XjP1UMWCo*V6rsWthS?^<_>*KTI z1#h&-u)qA6J6FydN0(y9(p-U+!0caYGYp|cQ+Y}pl;8sLS;hGoD4b~9>$i-32LZaG z+mc?l^vc2Gv143cHiS52-PM9UQU=Nzw(KvoSrXM=G)N6@in68e89Ay4jrgh~jKe}PdlF!p4@?RG5V2JNP)%-l{`|3m z*3FK#staSv`?hatCW2M^gQh|sQTA3C1ujNPSu*`B!5A6`&^E*QSw<>Fq7*sQm82q{ zLb<$XO3Pz;?Z-lKk5C+tt!T6@| zSYO+fW%b@OnouHNp=t7%Trt{&AgPagnsUuk|G_PH68kPLvdC%wJ2yUcu{YgYQZ2mT zR_K`y)(jEVQE+bX*PC6uE65z0>~U#C`DNzct-}HZSwlb3D(0jx_VOC=KB;R22FZI1 zk@-V^5(N4-M01eA#3a0XmAbc(aEld9GfT`eG$Fkq$AaOwF`#)*Zz;@1gde`z$TBBb z`{E4=KOom_{Zq?EYl&4gYmU)cf=HxkEjKglfYdK52nS!HKCC0rEy*EsWQ_^9i_bj~ z#G)3MitH2rC}3O^DKbPG0X>?S(V8khq;AW78fk3)Neal0wLoh$rr$|=_^+ISuHuB0 zP$EsjN-1#gF(#zgj*l%XwCmEhtzx6H)4l_E{1XWtQ6kYQEU^v>dj)k=I0L&zRI3ak zc7DmsOx{#xCV7IlpyJuA5d%Q|MgBzTp?}~Jb$0|Z;s5>4DxpKA-Os}~P@3{TNyc$} z){Nyn1>v=3p}G0Dd<6z1&WFsDvuDRQi5h;`IV!%CCWp1F9GGbuMv6xQVAQkSb4EUE znEb}KjiTIe++>-f9>#u<^2~RW7zcosq%Q$Yg#=G>(f^CCcMOkwi{1shgAO{j)3I%* zW81dTv2Ayfik*tlvDvY0+eyX7^{W%SwgN$4Q#(!;OBP?}d6~m>0wNPuZIHl6A|el57Dm zCa_)}xptK&8nsq*pEOp<^t3WkM4>rT`hmuR&3t*&jTd?52SX1sU=oulLD*|@XQ`*A zo0(>Ii_gCy!tQTKj~rZ-j^ab!TyoIQgTqip%F{dL&$s%m<4?keJu}z`4e^EQfXd!@4eH-& z$A-YTs18_I+SPymWmd3H@-hCN=;${~fsh58Lj=80{eCcNxr~JvW__}MztW>V@g?)n z-;e!A=k@henryw8Z1daer&)NtC&Oz6c997`KIZ@PPDa&Zj?Gr`A#1j2V;?n^=G(Ov-@fp-6S!2chZZvU(RO=LIC z&pw#rWY1HK$P~KZwF{5bq7@_^PI)~EXR}yu+ha4_5TcfwfL-WS9*L270Cj*vL&zrk?<0)72MW zoRf(^w3o~e%8;UFx;y077CftOjfDj0cwA0=GaLjoAD^NJ09>NcG6mU<{Zn>}gn%k8 zFaHg{F(ES{jjcKVOHTE$_dls%|Nm^U&&}-)LBJ>#CvXvRF)o%*#Yqd>FAea|>HzQg zqc6PFV142>tVHHO#+we28W8>56pI5>wCc>kVn)hp>Us9KhnATiqVuVSOrqBwq9&?J zP*PHCG~joBdj!9m++0MB7?U$2?b%7NBPQ8Juq!m(Qc=J%+DV*$nn85%Vk?#|pK~~w zVS^A`WBlzBDFS{fX^L~VJpnJUG3i}jJ0InA&zBMrX8 zF1m7)Ciujv*BAy*@%WITr~{4A^DM^n$d9}13kz%fwmH3GG-~KcJW||ml5yhVGf@|l zkD3c!(x&>z9u${80}DRs*Iv+AIW+L$pC#`9L%aTG$u`g+vQ6nT$EVkw9(TXAdcEB5 zx_(f+1S5TqlNCHorrSC@iR|EU`$0_&2KgO*0RP^8_5lBvYc+X|>tW3s^3M3*8L&gJ zpc;=+cG}HmR6V^7&PLFe&r5G4_x#A;l^(pdCY!(pAWMa#=GIyKIu@@xqZEMhPk2j( z8|X8Bsr2&l`X@?6ePFi7@81BKKsp|r#Xk@%TAAmY1Yg8Ca8uJw#JdBWF7N-5!O{@N z0r6dJI79Io4@RiRS(?7XRJB2qJuNmGx-4n$_g#!bZSPz_eB?tq(6FJykgko%)Fnzt zi^J$MEY*JWbhq#)5? z{Ho~wA&57os3HF22<*-bA%ch&M2;MW&03w%{!N}6+xLF?A@T0~!v_FIOGXB{ANfq| zmdz_6CDWHSTZ~Sn*0G?yb`VfY21~@voxWfqQ}kPt#<#@9p#?4`R=i=xH)6OKW67s?jFNi|=t`ctr5(zHdbQcJZ#wmCF6k)qmt z11|ND(R|hHm#d@7+q26yvGXa%HwlTpM@iw!pDA)DfvM@KTXp;t(+!8_q(_a$D@{DV zIpp=e1x*Utk>?D_?by9N!&}_BqE`m0^kXB^T3M=d4+l zoXBzY_SvYY40@1otQg8Lz^LLC1UalJ2K-=qy}VPC;=7iIsE z6fnU2vo}aFh1~w(__;Gdhdaz<74M4bc@oR5$um7IA`pB?NOKNa62}$`io`Jqvz@A{*TGsRO`!+AH>K>*66znaICg7 zNJi%@b_Z1wCV0&kqzfjilXXRoetM=MZqK(K&L>mJU0-WxPgmP09QVf}2H;N;Y`|Nc z&m(%s1GLDOZ^+Nq*69nW{Cc^NDTMXVG0&Hh8*!z2M|L|kO>`muOHmDVXh2_iywPfw#6H|6TR#pUYSv4~+A+U@LB zc;C;YphTbJ17MXh~;g;zR60(xQ-Yh1*-({CbN_Ano^@s;5bH z%%(OjK`S^H@LgYTFaNg|=HJJ?8#lbWZY%`8NP&S$gIW`8w<^i>l^s)^qkw5!@F_Bh z$>MWhOTZ6nN~5;Zs2z=a80uIUNlf)r~)OvuJKOthmlJJA#Ue0OSOb}KGsezAc{ww3C>EfJLyqp%!pP+wjDPWtnw9u}=u z@$#DPs4^D*`*-o>GW#oUbsa=R^#%6}-~Hw6JgFz^#90Fvij1)%D=!;RvbX_Dk|YTV z+Qg(|FQ0|%CzAJn?w9_(K9~_*Dxo?gApHo|{29fpUDhnUfssEvR5hrbuC*nx@0(A{3K{8IkfV45m0eRL zRvW6>BPsiitLMe5sAzgiYxCCDL<}z-oZTxu`CeY}{nbsZqes$npd5ZMFsMU{*C7g` z_;(O$(fG@EcG(Q}0rU0E!4_Z}5w`082%!GIwe-L4`SWPgvszRkTGm-Ri{C8wCvI!B zUGeYma_t=$5R0Lwr$<1iQ5EYLklh|WE4Is)8*0wV8>$>j)8NUH+nmA5Fk7qS+@o z)&kWp7{2f7U~<+p@BVULl*;$U`G@-%(fQ4o-yZ2v{q|onjtnb@u+>yW&<#$b=vP6ig23VO|-*eu_~#3 zzPXx+<94wk+1jIWxs6H19XYWZdz6ee#SEm+;-Pl?6!4TQ6hRcprpeeWnD-nY>?EP* z#6&g~Q&0ad9p(SAS$jthNLe73tJpTOc+e@yBnLsenW*mw{|^PQ?p-G#b_h~ zax=33=AYPwBy5)xH&j)WFyXK`=EhY{E**Tg(*-);(V}PKWV@y%1_yh#QC+N4HQIr+ zI&(v-1bFL(KjN{xz9+m6d`UMZcuF&~%jABzwEMJxq+F!%$b_gif62ryW%`Cq#_wf) z&24B{;quSk2u=^gsqEJDU_tai^SBGFk9o>Iq|ww=EYY1ecKZq8CFkzYm<%SPHCl2< z$^rhVNcsPyXaB9`JNnm80$sU}X~%n&_$m1RcHHZ!L)4;CtIt}f-wiQss$y8B%JdMUZ)i9J=e~^Z2 z#LeZ=tyvU<`POuiuu-FM9blSeQpQ?iv}fR1IsWLun|e2zUfxuJ*-y4Z!$%3q=nxNo zqTmKNk_=wB(tEBhJt5sloIFd@%x4JiaUJFURkhEXh#m0kSd0sOSV7ZVt=c_;f{sEkcCn#m68;htB915Y8`(x|Q4*URYJ)*SAzo))mlR>ufftA}xK z*@lSNZRf)C)v?pM=Zf+d?I!*OB>F|Q0Vdi>wUOP22FT@8WB7wEXDZ#bX>r%E-vF%v zK?}x(+zfDFJY>0V`+S1|@*@M$rQ1W_A{lZqWO+f`_1;UXX39ol>wE9%>g+ zdH&VXkDe2S5{LqK(s0*)|>H zYgBh3N$!;>=;bN!Vt8)gO1T(Wu@EdlIo9B%GiYGqN4?cnMLJ9HQ18euJBmQsY`i& z`jlOrNSAg)EBt3PpC?)IP@gBOd+=$sgD#xV=feLX-;J^;U6ERwX4XhuCx~ah%3z| zuq(mCu^95-)rsc8947|VzdDwsmg$J{^8I4u8vnGak6sI#H1m#SBB99D>Nwy`Cz^>P zkd?$6Q4G9R(DIN><+?%Rb8Yl(MKp=$RDK;HdZ(qLn@KyA%hKeGAT80U4UV8On#wT! z4ahmkLdErILd(=z2$dEUd{cazeA45p!Z-0EXikKaOQ}nsK_k^I6D??jcrJ(UiAeN2 zZ+IqGb?gv`Jb#Y8Sr9-_3Q*^|?c_P`jdZ`F}M!XqLMVREcoYiD9^uDLaBmA;ixlk2uvFR?Yj|8CRCdTM0IQ4V3aI-8(5%6Ze@8f_j~iP$Hc1yo6X3 z|MN5K!|v%O!zWf^{pOrwZ3$7O6Nnc43w}iS0T3G){Ed`z8;70^anby&;opMMtOc!; zg5pqC@lB;PMXFX-B=Iaa!oZ21G1H$i`z9NjJd0bplFgjg{Ppy$Y9uR84$rlpocMIh z45JCUY`)TtoNj*pTU7VF;FgtSU+LsY)>@|{myoq6R=($IWFh6KwHtkN{qL1FS2`pm zf`IG`N+HK*klk&jdul()&1&}VKvXJCXqbEQVIQ1$ab|_Q=3;DRw8g8~{l5d?FgO+p zGpoJ7_y6F+nWv}RsN=?vO7#ygZbrV}CS)eZ9j z;V3(H&aOB`d2iM;Bu|WRcEGQRCTFz!Y%TrF)uDc0D1zZu()cEd4dZsuP>^%Jo{ z7mD)yj4oEUUI#MU$;*iTA}XEJd&a6nA5GqGd&2f;+=dOwQ`$GEYviH7j&K>q#uE{w zw<0JYAtD3c2XBuLySjb3!^qtA!9z?Osl%sdE2%WOHt9X_F@clIhn7tSP-^_hsBfQB zdH3V}TiQvX;(}Dq!i8`8-3trsROuyb1_^n`n_XaWOM6!dY3ul#y_FW{OU2M{h;&9v z)hhC9OiKSWKU$%*9X;=4C^`CZtCd6n(qDSf(xW1fgjG(BFCSGgyO?*#$iJ-BL0@Ow z%*01yV3B2G7D5p+CgGa%@qTpLXsQrxj?cbF&mTZl6oAH6ZeX5T;hbQ-knDfkEbD=hJ7? zizAs%Kfp*d%4p?KpQzrMQC7+%wMNi5%t>e3eG5-trz(q*Gm8)J$8^`(s^sI%>_hdb zK|=iHypyMxL*`ZH1I227d=e!vhrzj#>wQ`zh$%1H+?t2^@q*Xntr+62H}(A5UtPe z4ylbup6;A=eqb}}#A`>o&3+csJ8KN;yptyMlxTM_^DYsT zcjg3ku5%mSECiVs%&?jd*sa89v6XF~FOqN7Y^-w*e!5|%ZjwPw&A}Xs?%eK#hcTI- zXfMg%Q1qyqD*8^ee_SivVpc<>)z@S(jJc6+yh)TWr#--eYnhV zOKi-gUD?T!{;uunCrK{CKCgRIxN-&Ua9%HH{JQ^hqdQf1*sDD$<>_`abLVb$>7#Ky zPC-scvOIbCo^Zchz~D}XA{Q3#W%+!#Tgp;xaHZtUajMQ*t_*Lzc+Tddq-0}_dC_f4 zhqD!y&iDIeR3NhtdSm>GD2Kh8)owj~GI?!7#$ z@a!_#ZyUXGd(nTpYfd}1<4|(M93?UL(yvy!FZ%B+5Q5gx2)ltbb!-_$ zvXq}}_D{YIj~H3u5B@i-9&gwT$V?Sn)=mot+(c)HFeMg+$OJ^u4%l?rGFJ~J0p<4y zPjf_j;WQ;O#TKLH6u&?o%F>dyiVWG7v#@_U z@m(SV_MJ7(V!J5eL43;(XJU<6W~sY9Lz(De{&I_`tUJXUDB|BRPhRM@bvFeg@~sduZMLm>vUonUqawl) z1iU$iss=aO6&5ve(GN>>Bo!|sy2Ta?;t5Hm2+fb6$4F_z4Vv0d21@KKwWMfxD2`XM z?n)(SiUbUp$KI>A;%6yZ8 z5>r}rLSk;IW+1|*(xgyWX+s*%bl+hlLyq#q+^*YKY)|F5KO46_sAIEU{7SVdDG?%J zGEuKRPR_=(_cv^+`6nOCWE}ooqb1a$OWTTA9^7i3cgVFwu~nIe_>#bVZAYfZ=tWJq z{-TG%o5y;(&*Rmi{8Xb0&F(F27KCfB82ogZsT1$@5+&_MH9iqpyDZN^pV)bM?p>gV z{n7+)k9ph8x=|L{b}{F(e)O9)!?|d24M@#k6J>cn@}=d#Z?V=YV~1B~RbD1hGV<9m zOQF;VaxdSiEO4Wrl>jy}Q)M&3*^qgWH-XiK>?n9ru@h5D-^a<=IdB^1(!5J%m+0VN ziS)7NtkZ^}l#OP{Miy)nF|CPe!YiO3Hse9zNvGt4Cwtr)fz(A1mh?xx4YtZb8wHs3 zQ-rhJUb8b5C~QWV(Z?UO88O?!^l^izkhQzjDZw3Au7WL_!7B7?OWNJ#*$rYJO^k0I zkt7sel>0_XT3Q&q?6Mr>+U|v91wiaW?zGm7ei%LTu$a$Ji+H$mtE=`^FF2lTGfrgk zI}n*G#|>Eby6>5-L%Oku?EWGENBH!ud8yTaV1DGuybDxxbl>~mg@^|F^4-BIUyo(;Vn%18|#(kKgQ z4U4aa731Y7(IvzSVDL?g5=#3{Xh&nSgajTiok zCTL-?>=ZQ7{+ZxZ&LQ6ON%ebivkO^3uGqY{AkjP*0~3Ox!bK$CGV}BYEth>|zldaW z5C-#(W_9jo{`qb2HF~v4m?ie%uSV0pe_AnYbu`Z8+Y$oK=BYYW+#OH82H4_CDlvAx z=z~>ASia9q=)qt;P&|eT{p%;2+a_E@qx4Inn(2O>LsuG2W~pTdHFLc(P>b?N5vlI* zJtE-Yo=IdZl&>aKK@r>DRtE}~%bYC!2U={x`7Eyr&>Q-qqlK+U8p?2e0X(#uboL9f zuBot4s+}qThRSf|6l;}^p9!WRU2X9E4AV}9XkA80T*=O=?r!uUPxYMIX0+&%tdYhI zPlTY!d)eub$%(S!Jm)k`%FL^|pIuEu zUp@>tIM)>y)q6&D-1^&r3U?vl(!l>}9;HsbQEoTw8giXPlsEJC|j zEe@#b`-Mdsnj)WVe(}0L`+MHBAvD$DAH*|}Ar5tbEbFKZ(E|U8J$+sxHbu5tlfz#) z0;)YUKYpa=& zG?%)b+X!yX)Iy@4}LxWR8P8JfpEVL2Sk+c2*OWF{lvh!oG{3Bi?;kzh$c2BihL?m)gW?M=_rK+sgh8jZlV%d=)S#R{BZLY>gw3chWIbG1 zlQO(&1nXeo<3@QAVu#|#D^gyl4iT}5qsS%j-nnC4O_VcWNtPMQ*q<)gh9)Kydm0d) z%-h6a=HLerhkPgD@Wbu&LiFTgOhqVUhCeh8QNw#H{r z5FQK^GyA5`R>hQiaL1rs$xv*k3!}Rcg*&7lZJIU zXD#@u*#5eMbbC5G3TXHuS+;3D*%3}ujg3!P5b=o`UL>dzW$qmnBUCz4*q%l=7$w~sWo&eAE5O_h^_rG6`Z-c~`HyugZ6K zTTRlN=LuiR2D#2A-{Kk|&vd&DMcJ`5A;DhzBs0|SiWI-=98_R^cP^1o)HVKu41coN zKYi=$+)>HW`F!tni+hh(mh^pyJsBj?$J+~D3?y~vrF6I-7iv^@e@ttqkj)Mi&+!Il zD@%5ipvIx9RTcxIp1@mz50jT<01*AW!#Mod>K2?URsJ5cx-i(Yd4QTx>i72`1LP}M z)z+G)xSBEURhMcNuWNVQ$L+qIRz(gpjl#HuDUXX_xDzdo>4n@ZD7gK_TXY zgyV>rju~y&F7{tPCO&dL2t5(l(p6aHmV-l{gPu=o?J0m#=qnt?0^T-5y}z8CMh9Zw zy!MR0O?o!q_H-$!3VZWC%%PH&==b9)cZoLCeifT{?XS(bq@HKBR;jW>xR+8K(L;IF z_nF1MqqyyDQ)3xsRB)Ucd^B$q$mr<7Btph$IJi0#pva zu29%$(4oacAsAhTQ%u*bRsw|RdVNW76IpSGgux9fUQrMzOj8UvVhadO= zmiMG+e*0eKXF#}e@g!q$d3p2Jb0BVFK<|KH89g?@9qW0?0?zZ84~<#{y5F7JFEAsW zX)=1SxW1AUc+lnZfxpxIlZFt(?2GMuMNJKvIz8*0_ve5YnS-7;>0^(^9qqHR)c#El zt#0`l4a-4|5S3CX)L^B0wa^CIy%{ECWRT}4xtGq~l2e2%#+t9{xq4m&?L4LE3I8l& zc|WF*=%M?Y0g4eSMmWFPU);pR0QXXLF~?rsj?NNzu*2@n0Ih! zG^ULH++0DKoG19s_jd~Ko8HL*vvl?gWcVJYt@n2T7WuKj4$SLN5EQ-~+RhTH@Vrnw z=LGx#Q`wEAmD;8#CDo8%=Z%(eD(TM|$xt{v<{msFR$CQ9!f0%xu`lf(E8i(~mZHMG z%kNCb%cq{IXX8o>piGnN)>)EUCT0QxU?%y8uc|5ae0FVR-1p%jpr9PuX)OwN03df1 zCpSm3tfFwX(?;YI&Xg#wQnW^l|$w6hXDs7Oj%I;YhIH|-pcwEV1OctuI%BWR* zJUBdRZxM$%-f(QzD*c!&T|Hadz|;pAKpvoewnmU%Y9m;kGK_3Ayn&n(UkZ z&)#a=7X$zUrV;cMiT2|5uz8MP98a4u0%FX{`U0xc!JDLG0S$qd5#xeFzP#&z)HL4T z>xK-*_YieB#rqdRr|Fk~pMY-+v4f!qLMzj(aDF?M?H}8Nq^6@gSt&UrFdT6v!<1{D zPU2(PEQsJxh=U1*qcJC-sH%>T*mEte17#Ff;ZT79Nfu5_rEyKC%(+Mb@?kPCIg_QM zK|)9)b{rvLr=!8d;@s2qH{@Qfw#9Y6-!uhkjkg&ppNkW#lVldqNK#;FXsRLUGU>iXp&{SV+j5OD z1%ka^U@a+7naWbL&C(0b@7;&helCf0jdo(1VZYl9`-1dhQq~2r1i{M`*c^8T(}+Yi?GO%i$BvmawdGrv&?iACp1{OzT00gnF_O~r#Te(*38qk5oyl< z2dHws4 zKZw762_>6FtKX1ZUc2?(19nP!gJuf2bHkucDVXS5_w>DusND^795VQ2x5)1dB$a>Bfhvsr zFFVq_(h=+-2|BOVhPX<_kXDNob{HXbhYVs3myQ7bNAOS66QR{)S!Cg16gT=KLXTSy zPtnXCia26Q#xHc~9J>Q|Sm(1XX^`3MKL%aJ^3QGgqAP(&W&0OP4TBPlDqCW)JS6l- z*3{PjtVX~G1el$f_YIXA-gpKZ8)-OuUu^)|!`Ud-ToWqCww?P<+9A_!;^ZZ=5~u59 zMh$q4W$35Wy->H<$>|TMxb9r`cx8=3O-kLBTKgU^JbFEh@vXP1+HzG+rCbj^I|(PX z_iL#aeqnkh`%(gDsd^Ja`nn1(4?-W7m!+age^;rbDoM;Md|~=Pc(vh>xX`b;aB*2( zXa6nn;Wlj2cGB~9k3o78CiSc*2sL}51kzrlaL+%C=X#PIvP$xGzHjRVr~|!dk_8IAB!4SLG$x00^pkC7p!iV_PpdWf}BUqv+RbXxJ_5U~v|Hl?Ww(W4p-SNfVL z*YTuKO1S+Vgd56KtG)NFZaZG&0+Efv|ET)CdyB2h#J02BFUM{G5R^-#YfXlF5qlCL zjG<64(!ES5JFP+##`J5!@a?SR^Q$n7i?H%T*1oJ7BHHH77Z%7;r@MBoq?8b-D!^*h;op4yk-2#q$#@JF};=V~l-WI+os9`56!#vjFu7yar?5lTSV z{n4a-Pat;Ht74RoXu5d+J zi+dsPfEzuu>6=5`=<6w@k>Rn^sr?eC9K*{{$indG+l;~cqGfdz(tEOXO6V)ZQkEw7 za%oF@yowqCFsV@JY_AE>417Zv7N?wdkMdwhp7K~+gOpZCSLz_i@s2$0L2FK;HB?!` zou`cw?Xq_@I2w!l9bv<;b8I*uSn3(()IQyM%IYxx_>~Y**i(!Z)a|)9@O1sVQtJOH zc-_lsTbpC^2d4j!F({Cy0@bV~J#1dg7OJ2b2+c^THhizJ_k1lgx$^uTlCCtIwZ(5i z^FdLw-Uo9{O21&HL?SeiqL7?MGjR8Luol>@EUDStr)O;$lyFLv;MPwn3uv zGY*J%0e*sCT+cUb8IvH=xzr{YfJYS3tXg+gs+5YDaV^s7c}g9y0RGjZ?i|zkK%L_0 z+JL%{8F=*52F3w^n}afOQf(}qg?d;935Yo4ikTxyNUY|#j#$^rgt2_32lRRj=U~?Mmzg?NEuDFZ6boS;e_RnsV!o8?A$ zwle4ZNF8)wnfY0@t(H`RY5g+oKwqEZnMF^HF?<#`-oet(z-}6WK{4^)bJ3VE5p^@7 zu{;MXJPF|i!NH_BZ2|#mq>E-B&6rF~OaM+8mc{{*@cI2B9f-gIl9+j=uEl0oZ+*aW zHThI8@XSuK&~Eb`{4y89-lNM157P}Nf&kfHm0Epo-~o-h(LO4cqk(NKyx`b`f{u3y zbM;|14V>c**0BI698U~z{QkY%V1y9U4LZm7o@+pWEu8>?BHh95WkR=PCO3&`sMC6q z|J@btPJ;PJGbypn!J;?&=QUa4o%KvDHO6b}ssswg{dNBM2107WLXK?3lX?0C5weTuJ(=&Wc+b(icod{=gV7|Gl5|Luh@CRBs0D@@QBGD zHe6VPPi%32-6)vus&qCvNtVQB6i!A9C7ajhJCU=!Jfq`<##nDO`8S+6Lw*&QB=8MF z9m?!b4Pqm!_FkQ(#*0}gozywb<^9$+QNC9>OZO-0Xe1OYC*q5NwNqRePHQ@_z9WBJb zI#MygnP<}tGJeegZ~)-0t|R1ru7m!`Agq#;w6rHr4V3=PUA+-vz0?`mWj6 zu8r+XKV9!5JN7|_B5rZG@Dt07YXfn|JMTfb$})B*qvlVhpM$p)7(K)4?ay?O#Y_Q= ztXtoXxYU_TYiSk{=g*hclLz9V&Z53wpSBTK&)T5GOuz5QbSJo$oJsn7_7e)JVY#>% zlYhkl-qx#S*c;x5;Ba}cw1Ke3+x1OXTiq{`6%2zXm2H;ao0mjyhSECU?Ebrsl7ppx zrkDRq{8NYfT1tXmTwD#y~T#zcx`B#)QgZ~YXWMtG6dnxc|5$zq*B z(pnRETZ6EJy;1bswzZL#F+RD9g(m=722ddSDQ&|b(um7fIo0^R83E{o!AE#iv)J5_ zYbA#I9fkKm%DB3qlNfURR)g+d;j0y>#3vpO)3x`XwdGU(x~YkARNMdhC>${FkCvb4S6|Nth|6-SQrmE%)U1Hn zh+b6d1y_TUlPSi7DDN2kG3`x6IIO`Br%{-zu~kU=4}L6km$$O4QrFW#fGic~dVG;_R{)WlTyC+Uo3F7U(w0 zSSkBv=y$AvL-nZ}6e0l#G);2Sr^OKT(+DO%$q29|#4;M5v@J zhC{Q$VNp33IT0+6P-i%?e!bELy$ojpMci+JHw4SoR`aDao|_LpHazW#3)>@cs#Ye< zXJ_MZBKX-6R7DokMNBf}Z3}-T*k(yZ3}R%p0%y z;O<8SmoFpZuhGoxA2Zf}^Qr{}yW&N7>8s1`QUOGVn-;gH zE=`I|V`)tz72p$-VYSD zl%nZQ?_GXdKV;JQQBAE_*&C6+&jMdY=Dp(kggKYVlpk*n*+SvyM50A=sls1X^|XS{ zwCa%fNiXWzr(Xs!NBg*Le&Wp)nX8aP!qvFlHdr{xKg^bt(NL|T@b{0mn%~3T!H-1L zzQe#cNB8Ht%O%V;d`RywGEps8xa__8yjPvdcr$eJ5U-)%`i28Kr(O&r&5Uy1kXkyd z^jTH=b6CUTTuQk0S{%GoDk;4}AjdfC0%wBug}rMY;|MQ@>nCGky3{wlG2{4|zu1{d zlSJO5Wyzssb_})iEn{;-Un|c=Qrbed5h3z@*+lZh*=PXM_k97M9tl}tD|4{nHhP}u zzvk5T{LG#F7aQXTOAc>)##+Ur1^ZxuPVMKgKK(ncl#}iD3)n*Fb$=mhE+r@85Y*%~ zNx*79gJiD=Fa7g7&EU7enlJM(6`iNST+~$Ps-r3WWiC!v6V`FYy9ol-8A|qAeWH%w zQPH+b)s`$hBVjmRqTfs93UVQfB@@fYpUXkYX1;Nl`X2a1g+WYyD{bW2!-ISK8oAa^ z%ZZIXV#=~-SH;=D>0|BckM5smnp`RO{tHSwL|8dKPt=b4H2&sda05K{?STujI9ox- z`nX1y%QYm^puA3Z5OCOHrJ!cnyUjt_YNYphZ@Y*&%SwAC18wIUP;>qoi^ma5^%pD_ zqCt_uzk}z;xF5MpZcEu0|=-lpl3pn%&0E`jzl4DGqgN(XSXEaJf;7&yy zL2aImy@&dK^4G*@rwjIiuc0A$UJv=U8ERP9k4O+ukcP1HlVWl1(Hqc~yOF!O_YGe+ zvL8LRJw0(hHQlUw0{1N`7OQ8PYpYF|Fd=K9(Jxjj;$o5ec4hAO9S!hhO7K^+ahv_U z;GkWsj`gK*Cn@FQD$>k?g(8K&U6jNn1M20T*l5LQlhFbg*pjAcv{*7w@FZLpuEiu7 zZ&3HfFV|e5w8)XFxPZY2fe*Wx$IJ7SX`ncdOeQNld`Uu!SxgY8M)N0Z>SXumG|h}c zs=xKNNby6!R)Gr!=}^Z2Wg9BYi-jF(ihk8i;LXgJfX9kcN@?Xe`=ZHkBn*_dYMWK# zHV$S?GC>rh916ux0~f&haEdt9;bnbOA@eZBN>Rnk9kLY=#5fQwOubus*#l9a;nI$oS$K} zyd00~ngX{He&v2ad_-fAYjcZjkw?Ijn!34Yh6&oz9~u0OB-N@Mmhrf1-RBPXyfzDd z1#|VdUtUVCzD>-0ft()n30R1-)9(c1-IN?Kgw#E9A&!qthJI1Y?9mIE2Kr%6Mv$4~Og!nzNB&9-Uev!)i`y13qw*g6s%wDm{l3pJB;0emuCCuFtg>3E@dvJG z;t}P|>*2>mTG5Zgfg-HrN)6Ks>63c7td-`Mck#`Ay2Gs?+IA90Td&pw8=D*< zkZ(&RRO$Qf8;x3Twn%S}m+)vFS1P<97WMQ7g5F)S$=gsyP|Ev}OvlpS#NT!?U!qBI z8!i@QShWz^arkp3%_LTr4!-i-{_x%gOylAc{vuf4TRt^#d#wY%PyMg+tr?O-RZNW? zA+;I!eaY|l8$IH$igMjgElGEINvmYiIgoI%4lo!pMs(>^7Qmv^&p$h&#uTA2G6RZA z$3!uF%o_*=irAe4i&W7e!QCeGVRgUP>%Cf@=;iF023x%b;IXZKiThx(5_0{H3JG8< zOm3~vssqvWdN-H+<~Pl}sDn8nQZ*YFftTB5WMi?HEsMWl1o|{3fRGHQj|>MJiOW4+ zbj!oCOO^}o;aCXYzb?m@aXajLGOBk#^vfG$z0m#u;`|~~O%GGdc^*~>F`=L&hG)=% zBx#6?Dsmu@Zxm%?zA<;@jD>bBnOdANC|r)KX7ZPOY=Ol;N7k5)G}*tLYGGb%vWj)L zxXFxTDp4zb=GDzCr@t*0YxmLSb@l9b)ek?P(e@G+E`eq4eGcdDW3>_zFv6XslRB@= z8k#?>O{`!amN?L;fc}(27`*NqHy;1I-BQ_ML^_5}s~Pf=;f6+5%E|)T-^;GoKO@Iy z6BGz5`P3?Rlpp7(?1xHY#p5g4+*=yNjZeS~Z#(7JS(P1`#Hj4&m$#c%Qf)ZU_S@c2 zbZZW30;^w`ZJaNe3C~T)Q+g;3f@yZ0h5GR?@N_tc1l*%tJ6fS~UOgt6e*d|u{uY|c?3QEJVtPJ_D@s{m zVZ}ydjyk^D^@zz3bYpF7Esso5z|mI_fD9T#INL}o#-(I#Tk%ua`j(uEDLgM{*3;#5 zG5CS+9o5*pkR{e4-=Zc(#>w>>maKC)O8n4_$7&>al6-kvaSJAJp=&0004FkG?Qo6z zkNNOcf61KX)qC880?{9GD9R&iar%C>H`WbKQu1$A_7`7Uub)#fxekA(2)L%W9TU8;0vf=Z@y##SnnSLc$lK<_gAUz>X*f% zZ3}j{-rioYc}O|$Uj5YI;4>#)mus&|86pfhfw};(kO;-vl9JjTSWIEn%)yztX)JLQ z0n!v#l+Fv01%QC4C`7K`m?oEK!6#d?9y&4CCU|rEG^GmaiK9$ml`J1n|P?y#^=AtS6mq*b#g zp`;=S>4EpnCv8Z>0$d4S?qCKteUeD|L#MS&0DD7hlKB?JxIsK406LYeHwbwB!NEdA z)fTEboXo;ALGRtPkB!HWJUK{}CJosw6(8$c4r~77$%>UTokNWVX5xp7f%R`w;I>|# zy$`~!yw{uG3h-OpbWTXN&TFkrFS&2pqITXkJ!m1+P!4ZNlG{%R#Xfs|mNHY}hHGZkZd0Vvwq2YUsBtcPo}1@w!RpgI#2nB3xIP1QV| z;W;zl;j&EMLOdLiG{3xSb z2)L|dx$2l`gcKQ)>n*n73AVQRG19qF_lK$=fc5Nri!$!rYFeg%dxu6wbpkA4Cm2oY z%-px?D@wG@&zII z>}s|ns5>_F#__0xvjr`K}U?W9=B{apaUg9yI1)r>m_*VM+Wb999pd^zKm5ZO2- zZaQN3xk7Nz<{2M*DbQgwQ_oF{zJ1#*Y;rYT{DP0Z8f?S+viWew9cGNWLhL~NR|=$M#G+eVDOZNhzTM?IaS zmssGe;WzW>d}V>V9RJ&*ZiFxtM=M_MZqgM!-3e4zxy}+>p@)O~@p;D0%tH!~bh@Zn zyx2=Q`Y!*;hupaYYsG%%L!CICTR9iP+*GrqaGC~9{txL7^c)oU4`WZq{klY5iqP+s z=+2f&kNotSA`X80aFmQ{Njt7cTz-oq_R2Ry0pYbC@7_Zt3kPNQslfL0h9{D7XI*1!gCVSzBmuPIYP16kXg%zt;VG?O+Z&X zU^C9Cc&F77%{G~$B7`MbiI?Rvuh)?-G$4c)ro&t)^IcFVhYsU_S#LW<(8E$Jy!qgn zmoZ?qSgAaQsGKjg2KKA&d4GFq^!XB!@BNqsrh#P1nD^+$Z)&t}a4vOJE$xn!yz`zlZczKAW50*MTl`OBFOh{v`ZLUpG|WNV?qZB6I>?Cr)arzhB#AHq%sp z<&-M>S9r0+RwCznXJE-pFGYZL)zuFkYH;)qZ#RNX%^!XxH(rYT(X~s>{@e=Lx>!zT zQ;Qt^>M}z$dL0@8n-*VhgN}agpN$=KiN;>`zu5;-VuTIHA#?w5K5f#v&vHx;c}(b? z+&$9UOc<}-S}b6mBb8E+tRw_zHX>W$bEQ%miPzHsA31wYRFx7rQ1S%b{Dg1bcUe}a zax#b>3&v9--OA@|$GqtnO^;rn=g8bxn1qucAHwQtJ3cy|5ecfC z*{69`VU8Hgi&<&a1yfu+F-6SF6(HDh{oW6!!C@UsYUD_nQxAt$ewig_C=zCziJX}G zV5PQR#Sc$_qsA%ssl#b75VIo2R)g=eS@vYHOuK0#pp6w$nZIi1UO9uMY(;EpaXXT& zZOZ>j9JUiGEd6?FTB3qs(xJMNUSLt%U~cN@I#id{Pbtxkm($#0g@m0G(}7>;2uHwm zdA+Y`ucw?gq!r`hFk0m-LaOOh=HDDA9_RjO<<7HSzeQLwOKH6Qq$h~)&hS5_w8YTP zru$wD$&&z*ZI(f9k=C(IOR+JSy2>_Fq()NE3Kpb7>AK&t`#H+>N_omrqcTwyb^6Ma zEwl_O1C3d;1#%m8>T)}_4y={x&vePUJ8Hp|_V%SO zTq22G4i|rHOs-Of`^wqeLrTGnQQ4E3aA_j}15IC6Ywo{fcwu?C2owwyf>Du+L=ay(-uP zPy?lBt{rF%lmisjt94ZTqm0r!S*NZt&$o^{=FKB`5BhKCZ`(BEWV9BIO9m7AF04p;j(;tEO0)`VYiDXJBIlYT&zm|N#!CxNF` zZTpWUK>(`|J^K-K(Qyv|`x#p1;T@@%8z*z7a>4zJ^nq-t(Z2 zwC+S0b?V*7BgxIEj{j2fz&C3Ds*7)x$^(2_BaVe9De<~#1=Dw0875+x?}4`nWb zkI9Jzbtl4aADY)2Z$(LJP`h6}_muUHkIcK>jdnT6;0QTL_mev^=$#vJ?NNRo=X~~D zG&|?*K6oH6AdXrDYFXJpW&xWa42Dgar~C^jo7deC0NFipe*n6aeYJSMbQxGa2)KRy zbJIvIp7^Rt7A1Hhs@qrxN;8P}V4zXzgM<3dPA|ZMjj`^T%YTmhtAVdjhQ9TeWhWJ_ zOO#{O@@*sc>mMx#&RzIz3EMao2J+8dWD3e?yt+i8K91k3Ue%WqvZXcH@-c> z%S`fZmPyH!{-d9g7FNx_;3pHmC3*D&@dlg0duRzkjs_+Ejp#n^Vmg)S4|U&XJl9@Cx$`W?uTZ{NwEW4D_*OP&GK1E}3$z%}`r!VOp|p zwdO1^>iv%3bh~*e`7F+VPBcaP6gZu)OFgJVEqUKHA>8~Ot>ohZwwn?*k~7~dfyK8~ zOvuBxxkkGR{eF_OlnHn;@)U85137F2VLS1X2Di49BE3TNg3-s}s25_|FbGM$JuY3d z9_k_bXu1vP8|7CCngD^IjH;}VoKCgKy45oX~l zpc=Jlz5fV~ex5SuaDR2d{DQ;@A`E?6;xg~*H86TDd5Egn4`?A zU91Y=hpf$V{E5+I{hoZ0mN%V7PL}2+Vr*ya$a~S-SLZoyIj7)8MTy^$^01MdVb_S; z>d=lLsdjd(iEfIAcTv9XtpuxN>nv!B1*=MbR;oU3n(j4F$I!U$lJ}-|>a5pM8g>AK zEti)}_~Jusq_Z*}tTt|{qPyVQoQVRlU>X-;xQcvFO0=Xpe(7>;$7lGV`45xc;oIpA zG$~mlWM}W&>MavRkGs9ZFwzk4y|iz4eLrSEchaKh(H|5qXk_AdPOH3&<(6H69nL`D zHSux3tO;lsAdtPyQKim8&S=F^w7xt{4je9^=X* zm5RJ23GUcK&ECs=rpR>|A6TGi7uvA-U?>ge!cRdK%3^!m51GYV9a_;ejPUO*Wc5v- zZR$YuP3Y&`VmVx_Jc%{y%mV1wE?A1Z?w(AOxNU)?8AdOiTiCCPn@$Yg@D|5AiX$=U4DPp2!oH;^gmRQwif6+J(A1(@18ok2NB0aaeGB&CTi7+KiaM6 zROq_JOvpQtMxm~HEKoH?bKA5&zYy!sWjal;CFVgrE5aM5F|zC3udJ*V9Xg|I#$BIj zKy0LxTbmdV_?A0uby57sqOU|-SFj$o^Zzn_u9Ic>?^%qz7=w)!%Qa*+@6gT!_N_n@YPs$%eEWM< zL26@x+CUP^hDtYGR-hwAbY|)A;E7T6b&*MBOU^m;-$ZR|(+=ui-Y0>&r%?V1jafXqP==xcSN^ptx{|KF=nLu5}#JZP9uv$GO+=&YWLVM4`y4xD+dQjc9 zpmPHIko#JzEhjvs-JN0U%XA?buq_vkY9WR5J?K|J{~8i%q4w|lOFfC8m%u)knlk9t zgwCz5H~f3V{0D6Kwu>F(vb7HIn!Ks7DblXyS(_mE=?=eBO+AQ&{X4O)k)@>EW*N`0l~=;Lo60`d z((PkexP@LJcqqV5X?c4`@v%&{FF)mPuxQc*rbk;I_Okf@J>>sm2LF97kbtm*B2jEc zC^?r2x{B;(`zZN>98e(x|ysxjkqzilFzXul$&p0f$t zyTQn|sv>yaa6>s(DgVtXHBXpsfgfWaB_}6lCy&?N zC&r~hxZC7B5unppxbcPOVR!|oFU}_@2_9oM^5e_d!rj?2ya0f#3h;z4)F?4*{dR3l zsYdh z{%7()$ibQH_w)_e#I)Y;AizfiB(B=F(4)N` z`(^Lgnt=PWmCa;El5kCG#a}!1soYp-8cTt(Fbk7Fz1N)u=F1m?nyFk5jMwY8uSS1Y zGcqzLwHvFb79ddgdw1kozZ!{d`9|`yO#h+~e?-dyU2o<85H94#kK@2Kk#-T&4?4blh3UjATY zyP0D9dOBdaKK*5?UWYqI$-mn01t`kVkUVYCG->ZmDmbF{BRr+Oov?2vkuE0!+c zC+>Mu3k5y_8)j}McHny3dH=NW$kjPSX4c3Uo}E@=>ebeX-1a)(gZwlDgP_pJ=7}w! zbCn-MUvF37(ZfSga@_rcOK)X)1IYMUSe)P`VNkqKpTzdbcmRY~1W_BUU>_+J|kEk6&}R0zZQ64Fa*V$zLTi@B6J`aPP}DafOxnNGgAk9 zNAL{{OpTUrv7?c<2t+b!kvYoX^Mb|)qZAp~0P3^(ixGH(tU*M8#4suR)wZ84NU?bL ze4-+1Nb=KiyKSt&F_C^ke0cCcce~l|0XDjaVb7ILWvocV_ERAnODYi&iloawlx6Ubn*3R`Gmr?4K2UT?FQ$i75nXf$No2m`rkkZpJR=?-Nb==FYZ zkZrX_CtCd7eFok~+Io=U$Pr}7P;|i@aDs_BEgFUmS4vPIXc1pr8R3z4t z&I$f60}*&aZQ$YZl|OfwzuonG(P5b(V6vQYH%w)n#_#R@PH{z3_mF2Jnz>_T1HqrpC#ZGs~IaAUgdnETBg}D z#1YIf@|#*mY&oV~X$iM$gLYnIkw}ja8jaddC~#YV4S{Y&xhE|XftZ{e9N2m_6oHV3 zC&Ro@*CV-g8OUszKc05Vt#eFaDt*|tOyY_4L{vcA$Zcvt3#Uvg9 zwj&eXrM9$i^Oj6|P`735w`DyjF1R_-im$8mTVZnJ^8>TXM*QxUI@-Pn& zn#@ASdwRZYjHLxw1NHtKU*>~CBD$q^G6k*-M8YX^X2jb_A03QQ%pxo)EDs6yP%9nM zHT%y~DR^_nwKz~3TrB-3I-b$D&_)cOuF(n%U+g6zwMsS351XKma~?2)G?=X|M*ge z*5RwuuPXZ|^f1?7pvUYMA#*edTT7-CSr++!O?h&MiMj(=xfy@GQqC6P`Sjg<^hz1( z3C1^Xk^YUytm#y|iF>vvCgjJJ;w^F4SLYaE}dNyJ=`ml^g@L{;7-T}_{ucRta*`yW;yBT)+_ z1sOGDXOU{iKC8+t?PY{_b|va99;htS*@^K1UW5Of*J>x^YoF*UoHl}%(|>Ng33Ac? z(jmHL;?v+iz(85Rhc$%oxo&q09%D-41}KU~l`ulwG~vPD_XyF(QR0|rkkmogotv@1 zR-rd1IK2ta|J-#d{#Teep%25rlLAw375BPF7cP z{^mm;UYoB~R~F=UVDhDV>*BsU6hg z$;seyM&^6922R^-v6k~9J*##VDh33jB7qz_lh_~gqxLB?*%=l{(6*&EI}&B|O#du( z6=FJoLPWkjwldShv%~Ymcoa3Me!eS|I4SBw|s=vbI8(;jF zUY@}Dz=Zo^U*yq|&hS7FMA+lTd08hCD)As|Mv4FFmq>oKhtuS4+MAHm+GuJ5M);P-ujCM8{Z8-6^FUozP2^wNoZG2p4x=0iS|iEfuT zHZtc6hQ5yoG_fzvX5K9=sM7RY6Vk8ng9LW=SIRqgGHYfFZLXMTe`Wf6kR;pDEtcLq z0wGs>x!m*fxRf@Na3kjWPaGl_h-NA6lOfh?i63#1IuekVIYK~<*D=-SpHVh~Lz*ol z1X>bCn~j**ZZ+r>pyK~TDni~p;mF$aN`BX<=Z0Qv=vR=;90rEuEPoqKm|t33-Q0c) zn7ZAV-n=WI6l$OCQ+D5_Z10kBy0r5!xk9EzvbF8RDph;0k@KB!p#FNL3n+mr#TEm6 zcn2O)hP>7O*M$n>bCM9Gf;9zg>(9YGIOO0LBxH!*hQH9LZ_h=Pl6cqEq8Ci)4PT!^hh6=yHX6Lp@{Eg6ocZ|HCJ$eDmE^4Hhb$r5uR&-=ZG zBBd5|_q7kmn4>)nMW&LL-Cd_)ns~SW`cL6Oun9gH*d#LBeda6|42R2_iV)>jlk(*0 zIOCG8M(g@DM>qyh!^y_8rUZ_fz{8!f5%6|U`D-9b(ss6++A}R-9mgh;BbluaTwWIb z3x;98hBIiy0l1^m0@&Aad@SN=h-heFfM{csO28v4aY;hId4*fT*wP;bxd)+<0^20s zjTp4j%ibD-9My2e!9JE8lo+H#E!<219~hbMH>Auczlru|cz$}FKy0$NLum$IH(Xv* z{JYY?%^$BnYb|ko3L+7hEQR8=UixouVLf@yVDhFT8fq)k3=jH_jW7O-#%O#xoq>Fq ze-gPUbuC(HzNY;10*xFXPmM(yEx#B<`P>nj*Ve%@J-JZEu;+vp=o_{JGXX*tCJ9Ss z?omOKvQlF1^Y(_OjNvfENKV8vwf52zsyKFqhG=IDcd4XnPSDgIA?^=#j53C;f=-J$ z7tfh#2~ZlgJQ7+l#En26O4{J-y^$Tvolrj|cL)?EOsR1-S?uj9#F6w2w^7=8QjM4(?Z^GbvNsZodTrkqi7ag<=Dab-*6Y zE)=y7N|v?yyquw=q{)7=FKF~N(9`r+_V_DDL{cb>q8I4ROJM(xU4LYyQDiH96N`2!J^>~Cn9A_0FnkRDA-o^cV;C6 zJZ_laXF;V>SI3o)6+Tq8tOu#$1MvEI=eW=KIwrwUXGy>nf#WZWw?cGuiBuCYi~XII z4vx9wDf*;N*+MNi=;h_ER_?!;ene#Rl<{P*KtHqQpEuw{L=4Uo!MtOf%Cf=3Eim0Q zb_59SfICMgB^mL6LrhMEpsT$g6cY20VxFZ6W4_>R8UGmtSBA+!i7SHHr!te^Coo)) zAzg(+)@$!;Rj9VQ#KIwYGJnX`9{Lh^Mi%c6nmB!%z1-c?li8bOk8)e8U_H2NUWUP5 zAR!h(OhxzY3};r@?uyveZV?5O&x9wW%xx?FEd+$O;xT@gg*bA&r_%hrUvOY-a-zqyEpq^nd zCFO>XNUvn|pX$FA>G9$=)LDri(CMnTQ!E#0EZ$}Lj01oT73!cYpdo|Aa{(qY*a{XT zF!@pn#1HHBL0kLEI08ZyQ_P<6lQBg~jI*zmSonXwWafw($g+h~$I}HbZy75zIB>@8 zx;7ffg-NjUvqy}$ovBQI(^+8=ja^pZpzmYOl=juQBGxUEC!fSc#^p>2_9Hnm)2hS{ zxq9#ljH#mF1GDCMcivnqWwcU4>I4`8`MdeW8dC(DmOYcrL3@^BMfiu?Rurvz8(-UU zRZ2W%w2hDNFlQ?bUAYgxx>CS8Hn(2fNjNAWg~WJ#B=;;gqq9lmXImhfH|DWgR6l~p z$Br}EBR9MK)u&xi_n$2mGQ=O1ZGGm`Te4@)c&A?41CbJ47tP4kt^Gn-irTD!!Iic> zXoW{`BaN3ch|@VFtTMB|My%e+3#$8W-WX!;Pr_AtvV)cAD;W^c6fVp7!nBqZEsQHFnR~|01@?dC` z+Fr{i_rJIROn~iH0z41hG_G%brr+J3F&fY2QyJg;)GoVca<8W1jvDWPOck^5zdT=u zT}h5qKPV)#qq0VX%z8n+>Q-jBZ+>eno6vfKiymV1tj!QN!H*>$n=?IjMD40A#peK8$9P7*f}0V zw7MW8Ahc#aai69UTF_;rJ@Ooj3TNHmwMAXH>Xtp)Q+M~!*v)vYig(F8^WRr|qc1)7 z=I30Z!~r#e1PKZG_Mq$Cke^>rpvpEoJ32d^md`noZa%JvGp^ml4jn>B2n`PkDvWCP zCF1nMb7I7Dcj7*VX8@u7!Rw;U6i6@SIN>&a^9D%t_90Tj$SP5~$LqeXC?kuQt_VDUibsODqO&64DdRQLNGD zcOzwimBE?a1LTtt+Wo2@@#v+52Ss)-ipKJx|eURrD<%Ext#2zE|3x&qZ3 zup#Qp`xZ{xMS8&RrrJkff~1CoSACpMLqmc52hX`|Lv`i^Vg5BWvBY;ad%OC_VaXg0 zv>-L6(mqfx911n!UU?Hda%55sYT!Msd9XO%OFDxa;6>x=Krs$V9!NVfL0WKW={yE1 zK{CyP?DIqtd!XFQ;_|XjDLBxYb}r<8zBQyaJ-Z{^Y0mZ?4%JK+s7QTs7T|VQ^Ya84 zvYhC#9FX995VQ!A5Zubd98DzrG#ud@LCoqJUBx{*Qv2QNQG|l)w-e3C(MW+kXW>e% z@^?2k_Mf29BM?Uz(&&wBt&_{(|gvA-)7C6n} zbjv}J-lxz}Az)ALmqa#JU;&|~1PKwf*bI2%GC=T#P*PF?2V@hlkt>u!Ety#BL8q!R z?B~^EuDg1mKJ+>esv-B?M8>iW4qMYx6#A|MFjDKSgnXOpzMrN01o1rL6?k9bxAO2Z zIzJD%-h~rAW?16_8|Jqo(>wj9yyBnAYu!4vE;etIIUW;ka)`$flcoMl4wACpDuX=p zY(TBAHW)2I^L%Wdh)c+*(DcmlrpLRBdh#FT0WAzw7F3$!YpdNhVTSpFBprwo0UHvK zv^>t?+kJdoGX^_D*V2@LM9{{E9kzQ1pL(*chK5G5Xy|eS$4i)qlv>tZ4KEUgH~Lz$ zQ-Cd&2Mby9{KcJGl>HrbKpS_$+p(l_8RBpI3wV_S$xL!DJz^EAsh0LXa1{AFH7z8m;M84pyqZrKnRD9DQ#-bK%CTFPe`ydJ>rr;dPRM~qHCXda?( zaMT^$ZZb~gK%o*JZ}6{INT+*7p(-o)KNrq<51+M7Pggy@h((HZPD~(-Ea`n+5^s;o zneF-tXusV>3ie%D$aRiwW{GFW;Qv+An~?s(Xg=!c$?|~W+2YLot5PFigmlityV=jD zGsW<3o;Q+K=($%(87lR8VXJWjb#!KNlxJ~h%ZMStwRC76(_-#ihwUcFo8i{kJ*v#sMjM=-Ibzg~H=Dzu7F zH09JTwtRfi^=`sYz2FnP4(E#%{}XIzC$!xg`4mhoq$*8jt7YkUy3s5mW45-o`ej4Y zj&A4ABTVq+^)(1C^%OxW&Un-pX7ZjVZLxg!aw7>8+?6Wrxl-c;U2$nylo z$KNO&&gn1h!TFXh+f9WlME9c_$`z=CZMu+oJzIxKn7vb@U15A{-4U7kc*BH6_Pn!s zIkK-2EJw_cBMWE3FREimF83fKtdD9jY^x;pYArb!Cf@g)nog$yUV#yvUGo7Yv7sy^ zG}&p$8+n;r$+#!~42%yntey5J$`3FrxZVsC?>IYHk9z_?yv#cy2LAB8+fu7VICTZq zG%3@am3#dD`N~A0S@V8+d2xoLM5C$Bxg~+S4~UeU-MegOzXFR$jm=%6Ze7^nq21&-JXn$ zND-uZxuq123w+NQ{Mxfm)h?gh5Pzx{I$1QkemBIH?G6?w*aSB%1#$Cu2OPp zGR<8FZQ7>==f!6t)CZ)=n-r&K+C<`c+#8PE>tJ#{!bL!EqSMfa7Ugg5TOmA@=w9Ak z37i4nWJMH2se17CQ-xh)HQ56gkW%mvJa*9}-AxiAtOk7n%#D z+ScrR`97i(&t)?egXZdc9M$E;Rpb!ihp(nVbr-<3u}L!CRl%~$z;ujiAggQi1&TlI zX=_z(I6EXb%I2zJ)%7>8s1r1igkHXJ$ zL=Bo($tBbg^X<9T7e?lyTuOr^A`~p=K3<f?5K39U^^sl!caevcopK2@-EF@ z;fLp-g!k1p%-U2aAkM0cuG-_{Cw}7iCUb>kxJPUk8umO?XFKmhbeGwm0`h{YO2jhh zlQB+(a@8i{`m@#0z8gNlQqzoq`AMZDmPe62+%}GQLO11;6oq^m8<1YHv{WziRpMxh zf?|m9MEmqm6fW~mQ81s4KfggsI9o|TL)AuwLkr*-J=O~`dCb42STR4hI|IVqQ8_cE#=<=a?J7ZG8Q8riczt#lh;Y0#kT@TFyY-aflG zZpCUSG48K59BKB(A{DEBSYbg3bt3|vX?0KKuFr;5(mFiD+mEi5cjuh-=*3n(ECWM6CDf` zl+ISv|59I`9@wT;|0Qj8pCBg!g=sXQ1kF)au>mi5-8gUAk~ z8|!J{q9^PxVsr;voMV-oNirYn(^vHRQ974D8a#)zZ>O->h!dnqRXLwY&fYVGChDGw z#LmMyoi5{y!@?51B}1vSARlQ+4?A4Qr;pmqEHYrL^!b1F&=LDox!(}`@n>rAk55Bk zAn_H%gEyKke1bT46M-e$BmwRKAfEcUr==DwVbmQV)|A1oG&b-reIl=mfb)0NC(o5C3&scngus?mng=Z=$cWSs`nMvc66KQ4_fve)Hr6j~-1Xa4 zOh^g$3DkN=n|U3%iJUzyZyo;yQ4mOz!mIp{_4JY?mwIBI9Ofu`f^E$%*-c8w^pis_ z+@c+l5^ba68W@vB06f^tddPDYRzzf3B>O?bU4A820W{NX9d7>eKUih2CcMLrbxNo#14uxb)WpbXc~Afx=R3Y1v%xOH;$zbuk^4&#yKsP@SVXdzGUoNxs5Y zG2&%C;17j*&8!w+&Rf$WJnR7|%Q;WZTeFY!6a%Cv9q&%y?)A1|3=1}XdQT2P99a3s zNN8xj&CV8v)3?c0+obN~-$cU*c#+5BOLyc46`tE`Iy3SKay?Q57ORZ8S2pvn<4zdP zRPKhF!#!xlj!6Y**Krv^fy}##FK|gg(DD?(eaWgKB9vrOhMl$meiMl|83GF~ z0q9mW)TrziC!i;&zt4fA?HWJl*}E2d(2PDgF=De>{T>uQDqP%{nk^w!q0-UC83G(Z z>Yl9NpcDeaqVTeZ)e2}`lcuy!ayc_g+$B3GsL_Rx$ue$LnhAy>guOq{f5*44Se9Qa zoW|_oRdMCYS8Cz*c(tc?_P!p9F}rK%hx0jO2eaQy=D4Ahy%->Y19LolF9eUdH0A=oSbDZWvpr^r5Jl^u#60R%pXj4 zos(*lRSs^_8Q&%M=nPpRvwnaNG?%uYeEcUS$M63mn?;6ITG2RvKFi^$uycCv-ykD? zr}(W8i`$<@S6V7i8ie^ zS#uAlvyI#fqo$M{+?gQbp`7(cdZ+&0ozOy~CNSYY75%8e!Odx~62Z$;fEoPPY>%Hb z1AZ*p1E;E{*IJY~4yu4OSducN?)UQ8RLzOLtA|_8NKN|TkH;v*8LSu7tAk%h4gpM9 z>AY`3#_%4Omq@Lw=IGQ5);QFghBMFclJQ&x?$|;ad<;bvDHV6t1?n?@u zK{aZy-7wK}#DOb)$&t9huxIk*)jfJ4jtCHA`Z0QN_@Sv+rh6UCBq5W%(S&^Z0P96K z#2ypu&|Va$GQAf472=;)xJ*SH%+Fjo4yXRv8R&>f*$+;a!m8kvL5n!ZVK)8>w&=)% zoxi9jo6ZPh+rf!=5s8;y24$4S=JH;+=eu)j`FeH>AW$$^iZ5B97vw$j~60kybJ7J&rjc`hR@XAK^ey|yUlFV*H z{bAc(E;jjE%c@4Rrk#$2j+7hW;#@1j6kP4WGUbqsrDC87E-i&BWKIL4K1EAI^u&Z_ zEzL?YkQ5o1%IC?8&xsT~??1kVPIO+Hh;x z&4;ZmRfwKKJhT5!D+ou+tCMa=PlYC1pMO}ph?;mba)jz7{`A*+j^<^Cg$~6MK`ckd z?u#P5p2{6f(9uOZ+cW6rr9&X_<2muqM2S^s$imXP>K(#WFcnK7b@ns((TmZfu_V8} z&3J@5hb{U2>00CixvZGJn{E6AMvIXB;|Q4DZnv*X8~mX-f4spyStu@h<6!lfI4lUy z>Cdir>RV50if`Za_jpu46`1;|p`|CN{=Pedt={K=2bN-*Z?+@=2 z_|gRd7saTc4Xrs}9)zt@S|l>j7TAMmPXssDsuOq27844MpHDYcZ_bOh=7jO4j~^~4 z2*-dCF?PPg^-@`Lq;a@RiutaL2m{q%HSn`hE2XPF4(z4aBj~k>X}F1Uqc3qWl2I6x ztDy)%3<3g|6Fq|Ng+yU(Lk6sDYIy@fP1^QzB;H@X9({9kJp7qRFT|1bBEyMFO)Lv% zv$TY+Hoh#P;UH!$)#LnWvhN%q-mFLztkDP8h)opk8yZj~` zp;G>06Bs668m|uufPdp_Tvn$Z*lQvk9hHH#2-i|}9RyX5fo=DbMyXL{h1|7eZ_*>r zR*bmybEZ}?*FZyFQM(~4k_U;u9OZM z(=d^!=S>W0mRhLjNikm0OlH=!tXNh&-h1H(4=kv$0dTnZr?ip z;*fdMRXXU#GA&Nh1Ig{siGBTx_HV8`a~Q?5)&1hg;*0zrxoP>#9 z>B6rxUF~AeP(y0y*u4t{QC7vLw$e=#{%_&8`G4gikm3VxS*J~hp3F7a%f}8 z_#>0kHf+R7Ooi9u!6y`-iM8*21()Xk+i*!yw99?CNhJP*^hm0==~?b2_K$TGuOgff z4nWhiI2H-V5HI{Jn(O#8m*-iB6z?E>>*^U=?BF?eQ=MqNH9W0ctj2sUc!edE4;5C6 zzag~?MSKB^y$uEPz}XuKf6IXlYd7ULIxWPf-*{YeZJ!l*COGO)2b&9k@S=BAdf$3% zxt02#97<0RDBUKo!^uRBY+Q*;0h5C8Kr@X240sCtzI(Vz=RY$x#kEZ2oUgHqs`qI?@|PV1=6({cq1o zz@0$c9{Q04g$~vbzWBOIc<2q7$b=a7y!4R84wryCgqB9$s|uW;<5*N$0o#uQn@Ak0 z#;V7j-wgC~mrCPkN{g9N2n#*xV}Fc?n-nR8l+}e~H*gSMo5Joc zfW?O&**`Y7OxyJH!{4?L((HyYx3>=XF4pm!8>4nWesaK%#~yWJpKc zn8j#vE)p1Puf^@S5Y4Twju2PHM24J_4ZLb1e|$AnWS%Thu+|P*^^e>j_>*J^5tj*q z1siHxVu1TN4DE#PC9yP3YJGJbf4JC+pJNbnnAeu4SUI@sc~22~srq3P#OEa;q8xYK zS0-~UzmNMA&nTG{?)*}B(Z6&1X;haNgoG2Mn?Ika% zuo4`~UVjgC-G^wXGtK`o31~1RitGv$#mX#)cc>XQXhd>TiH zclN-I*;Ff)-41SUOfoeHLqari?nEJ#qng67#5@6>1zP~W8qo1!Yp;08k{QyiJVOSE1J^DNZDKRek-5J3Kb6|zrF zjTe3^`D*u#*?P_On66W4Xxxx(kYdP(o(nk^!l5b>^agQQO@Qg4r>`azpH9jtu<$jIp%mIPh);dhRt{Tr#HxAt|2o*g}j8A$^37DW8p7% zzgw3m+)vC?R?A|U#i)Vw@^F~GevaPZ(yXyP?z88|Fhz|n8?;3-pMd3Mjo)@N=(5@Z zL8`e7gEP~NzpXaGqqUlcBTOUfoR8>Tu4Zu-PT7@<^mclxUEp$9!K0++D5^KwyMQ&} z|5&T*eUfYO!@k%qk{XSABThdi1l3B_pyXsWdNK-P012cxNkM#;JBr%kB6Nio!cpQ} zxxt&M-VN2P`EIP}grKZc2BXNbvcqF#%;l7u>VBH8rM1ll(0VW^Xr^A8#zem-0F_nw zwL^nwMHjYoP&-15+yO-Ba|LELwslC8@VTRrAGgBv;a{R>W z>)TspM}ugim`xA_kl*>dfzlLM**`Il4uu%<4O5Sr`7F`v?KB0;3_Pm-5uQJKKl5`A z``Alx3+!vhOOWg5+f3|QP&dPuo6p5p|B_zM);*8j7T7N@!Ka{C&FQJYZm67VuRq6A z>SBC793RZi4_?l1criUwf1pzqF|TiLRIOTyB4H?}!hX#j?+=J1-97B(GE7X!s33>x z*YqJtjSl5ED%E^k_xq$J7e&NSc%;xpwv-v2&0gtlYY~pUuI4xN#E#VfVoz2}1?2Eo z0}kN$g)g?P#&{R7g*%*z1w}+fN!dT~X(uL8aDT!R@cb^^tp*boj@fNer(q&OQYjVa z0GcqS-K8~WXArlFsjjy=qUqaiMmufBjIBSUp4C2CffKTTOGu30C8nm9=7bMd{cPv{ zzgIaA<#`Pm*-k3nF$?c&@m>SXQ51Xh{%}$o8WA=)u@UZay)8(~^)z2?;}vPu?dY#9 zr<497J8QI^1rIdzQ_`1jQ0hb#5 zc_;HB^X69C=X&R!IO_KFmu|;X9?&-D_;G#C!O2MsG)sW)9?K%H>;q6*M!6BITuQ!^wd5?dUC_Y21vG>~ZLY7o>zj0=x0qvcbz_(qVRTC8H;GVGico;Bk3G z>Hf;CCFc_rmfys?3&gq({!F2p{`r>QH-goc!TlSs_@s;y$faL-@a)*vr{kQMm@#^M zUX-e9PoO`|cniyYW9#Y|Iak)U+wAye8f?uw`QCMd#YIS5zTAKbO%T=EO?fyKIQ*X` zFl#O-szHIT*lk|97GKqy+Udp??Rir*H#T+j8G{ z2fm17qxrB~8mAq6vN_9q?z3UymPu}D3z7~mfyL&FG>Qs>naeIsjYNDwgrHZu5TjA- z#qe0|qQOV|N2@zi{QjnoiMhVGB%~j*pA)1(xr1@=0>`E&77(zjW2AfWN8`o82=RP4 ztuR1Lxz~Ie%{ocrJ@E-5;?|o~t}jJ7)+fYKuO(r(VR*a{h-x4-wH&hgg^=0YJK92R zx7GC~UfaPh|9`*B|D1Yx$ltG*Q5_3@jZch;MOz6uIJgKOHd%8HSd?&BRAi*2)O^-# zwT8s?6OZijLy3#4RA>Z%VA>^cdbuBmtOOI{5{zCtK5sZSflqVj#PZ6#MQc} zR4i`^j<3#VSPs><(9m96>!rIrQ$9gcTWfnc;HvJrk!+;njW391T{+c#pv*BWGIBUC z0Ibet#@pS)bGx#(r#GjNsm`S~OxNpiM{Sc}V0gH4?}@-9o2Aj=@*Tov%LV@DeSq6n zw_>$A^d+vY=>Ju1|G(oUU@Q8M@+Vv7I|-OeF8}gpv`T@9qX+?C$PU`C!k=BgI0-$$ z(a2?lieWbyQH=*ZCPuH_qtc)011q5Rrn|e+xXq|RtzYMI*9$SscKZb<)W_Soi9f8! zYfN>ALyi@G+brSK)Bt08lVqH1(7dVDMPXijof-DRXn5%(i}X=Yy~%*11@uW%ZIvf+ zxl3D}wv-S&E)NOGzrss$vfl#;g!@TRym-Metmg-HGpaYubjQ5f$}K9>SA@|)O|#A& zX(+HPaWok@sPYTiKJhrunAt!B1>;f4z8I08E;ohhcga4lUtUH2^9>ZzP^P=%Veb~^ zT$ld!WU$~a0?+@8BfzqN_PELE%x^9@pEIyMhx+xprKbgMcDNxSn&MBl>p~@&)CG5S z{M>f(#QmsB$0o@+RDXRViK-a;h5&*=(;NJ@J2gz5s0?#|T?X)w1}xsC&fb8@DoCf$ zA}%#-aym0XgvMkW{Rz&zet1wFNJ|^Sjiu46DjT~boN}$NA9&e!L3Q+qm}fEnz(239 z7L z@!F01yh_pDMt0+T?F=37{%0fq5s32dT>8(&oE21_8MK|qJE6AQuH#=X=yf#U=l%GB zWI#98f{$v<#ns|)uEBY3BlQ6wsbF2q=?VEo@MGgwblVl-g3}ZB;C*T)83XK_5~Q;N z6U-zj@u9*hsvNsogn!}{2^y_?R@J>kLVj~v(W+MF9#oE3ZjW>x1Q+|C1-*-}oSq|Q zq&+yvF~*Es+`D>q4pSW7+_(Q3asB_E6<5H%C!+*5ob0f;z=lQ)BR$>#*i#NklMdrN zNyb4a)`x4J^$+93F69FM;?Eb!wLY6{vH|dtg&po1o{}|H{skT!IQ(HUtbaWyID$9E zmT=eZ_X_8~pa}$y(7^XS*!kCDC6fs?5z38GZW71qX9coNP?U$98lEYAJ>g0U@Q*$g z;1Xx$UY;B@A;v#g)@E}GL?a!D7pGH<)-iRy&SxtOh)=F6TE^bp-k+}d?$nza4KO~( z*ekulrwVDqu;^Z&Ho;~gL!hqAan!M2zu93!;>)k|7S-JcL0s|u2gd>KL@99JTFcYI z3y!Zb+>hSQq90hovXIU%4N8Q3rTy^rrQ=FJO-XnT`DVzClIy_& z#c|@S-t1@3oMZf+bXR=A;bgbB8jaud;dS>;H|#f6Z*FxM4fW}x+)7s??du2UODnq? zC%e@f82WpsR>F@Qt9*)}J-khLYiQ`+e2pqN8RklwMk_=D?`w=5&SWVp8(bVMaKZs8 z%*1aLN75r4mz7-9q^vamGup&LYn@>KV03INZh}`ory1*I3Jqn?sK<~up-zjq zKdqhLK4%*}5=9%4iT-GAx;npi=jT6`5^z1}!3niRv+8??7!~|sR-sXvrSu_PIn9; zjExiOJ$je`B6Q-zgUbkKti&XwHE|SlKh=+7e&!Vnzn=I|qjXzlAoV4zM(oyMTLWNI zMZ5g9QCb;$wWHh|rNlU>FQexAPWMq@7$|=mB=p;)R}I}{HizeakfugxtgY=&l8?{+ ziF2K)=B|26D_AbxChxn8UM@(51upAo$e&c$AtoxZdZ|U$vSfK{juk8xO8H7pb7zlquKtnZB=s;r*du_7TYMJ{^a<4auA?g(uV$< zG;!AYllvZpEVIx%8rMBysXyuIBz$1JlaAyzwo!0!wMnIi>?2#JgC##hd`|_9&0RPSM!VZwPZ{V*CC?0mKE;jo?nV^5kfteKu!=48sI9pf=yBz0;~)=2yuQBQwMWy^Tk zA*MQ%Gj(LRy9S2rXG0WYrxVkaJ2kwrZ*BFUvr74pEHJA44`JNy3HOguEYe3V{^cD} zarnf0Gb;d{j^6$CodhvQ6y_0ULWxY2zV0iE(;IW9Ts_`{2203y6Io zGn_9t)$3=kxH5kpcQt^b@F~gYs3niiCqu}`sI28){Y6yZ)@6mlmbaTB3b4-A$>IA= zP9(eU97lQk;vistz}kvfEJ>t*IFrqo+4JW`FdPLUr8nVUpvFf@ehfo3YF{_0N0$QR zCo@UBE&Sl8ZAZ4r`lTYZ-wp~%N);f=#jJN*JHW*+KC zH!F1{Cu~o9RL863d`g8s7(EBH-L1FJB({CXY!gfXJGiRVVXm3YMh>~9^^a?G$14pW zN(ZZayPI)mq}Yy({!Mrsdy%z^2{OE~d9Vi_NZ!zXL=0JQTd}s|=7w84vdeWI{4t_7 z#SB(7>m{|;+owhiHX0x1*SAir_niKQc~)X1@gNOGq#KWD!320FIPoxkG_B|7;yLH z#gQlkMg%B}%NZ|w)#i^oXJ4N*Fiih8)a6}jLtAv*&XiOJC{pW;b_YbJa~N8yq1M2s zImxRDw4pMPlNWR1wD#_yTH@{vtIDU3jJV;TYdaaxftld=Z>0#e!vB8JZJyHaLc~J# zOPKPhpa;CXy*T|bP5VA-LQl4b`M1>MmXoJO!*6Pv#z{v}Ozy8L8sLTxCpQNI3)0MS zTSV!aOe9rOnya&VP}k`2fC8m%t>9NVB{oScODVGFC?a~}C+>sqt-T$0 zs{{vRgYyO|Y<8mu8E6{b)enY+^F=%9s)U(AANRc#0}C?AkcU1y*f?ahu92A~t5B|}EvNkC3)#a7VIP+d*A~nQ;tLDh z6hu;@wwgzMXSK=t`16%g<;G3*sy=X7s(yAc5vyEGDh4#G3|Tr-thjx0!SE2!B9b3^ zzGh<{u5WAJ+Zp2DEgf^UpI;#RJ>tK6drL5!FQH(xAABk0o)@8dKw=51Mz zOpNtaYjc&U*CJM^l}EL2=mk`UPGr8tt4HQ(oGXO5E0U zc`>~uY94=`N{{4+-!5i*ApUB1u`afI zpVCL(5d3#w)c*i7Ph&U8W-x$U0$*^GxPdmXIobJ2f~biJ+6vh2{%5yPlD$*atONg% z$br>*A9IBcO;Ofbyee|;txEwA`P5k}LDe=L&8)B<=v-z*3%y#%J`$tjJ2EW(nwYVq z9i>-Q)3H4hKfXJ0wRUIP=Qy*I5ttI# zAmbm0?lCzYf1<{Acto0^KI|NvMxi;vu#PefO50{`lT1ecB#hZT?ULpnO_^L+NjUCL zUS4alH$I3#ZcSni;u~$3c?C#y{saSB;i`4hsMD?oXRYI#=8#!*K3rmp-X|O58B6ZZ zC)4LArubxc|(~b3c5x%-z zN?MKf1hfY{l^+<&Xfnod(No>=4#){o@7ZnKt9`3uxCcHy9>+e9np_bhsJGp$FxC8B zUeu`OTk*ykUGF^b1PT7}SuU4+o}h}XFA3qbO!9_)QTt1}8P@z+=pMqLJJjLeevhR@ zF*=bxp~4EKhZ6Xl-r#sYiGCGE@Y_46*z@YIIvY7wfF4S~pvcAM! z70(e8tR`1t^>vT4Bnz#j^j|zs$6k!qwgEkFW#rnb;<)|IYoz@ z^#sXxQ`u2jTd$i4T*>q?Q1s9>9N+M@RMW5xYzKVI)Cu`CVWT-yV_I7+h^IqFqldp3 zw+&&ISy6@HChX0$DuSuNR)rm>X@IMiixexP8%avR#x#tOUU4;5OGuu~_+@~LTT0_6 zM@+HTgqWfIBHfpuJY66NWmvKibU)X8xQr7eCAJQBVACUQLz);!iWvx^k|cYfm%0^) zaEkL8y4&ZDoZ@59woW3S+21!nnkEpYmIcp#_C;qang^=X@ELS_w=XXgd;PEr!sQ?G zm^24SpWg#J~I z$>M2@8R&NoWQDQVeV6;QdDt^`hP{CqC4?#lJ*lAN;t(y5&9+CldjdvqXUr&(*rN_d zg^w;ROC$l5fJX*|K)moK%{hVnZ|_he0BOkO+IynlX#MIx;%LnUW|Iak=d*C)ZuWg7 z8BE}6RsJ!$UOUr|2+v7d1Ox;#C2P{l?~}0S>s?SA+yd3;pbf$_Cxd@&ZyvRS99dc6 zheKm~7uoa;fB_WlpDX5d51A$=rsn+_2dgWq4VUtU3#s~h$^4|-YNKG2R%*TXl6xv$ z5f0xu&euKePoBu_95B@9ox~UEM88wm3BPuI9O5zNGxw*N$!3PFSuF-#Zln@9R^>C6D-C zi(!R2YJAH2MoqZ}9b-*~p%P1|nW^_)=_)RG6}pTWba;Q`==-okeRglEJf@ZTP9I;< zMjio_2++6f#I>db1tkfJWE2wz29(K6wb1GCTJwG0njK0>CZ=k6b#%uKX0mXuZPvBo z>BOp2DyS>1r3*y7%}jMh{Us&QCSFYlfG9`r;3gKcHFQ6VRB zUnLci%O=kPiOy_d#wfZ8nFRZ-L9X0JmwNG6q%KuNMTiX=N!Tfe5hdenS2+o2h)##Y zE-fO#2PWqI4f($D>Q1%$UiHp00d6=m;CO%|H%{A#n$EYaLfr=mn3x^#Hd6?IIr~m_ z!Q5P>S-!bJt(?pOH)?;x=jw+wd~-*oLZv4N(LGnxB^)(cI!{cz3yK(OCz8%=dfi6@ z7U#JerIqx0o5{8;&88%5#JskjR};WwOdNWJ{FWA%?Y*_A$bY(zo4rS(dbwhJzpPI< z5)Wr1_ocs(Tq}e_6qeplZwJAyqKoeE@-_Mpzem%yY`C!da{RISof;Sl=ug}qCoLmY z??Wa@p1UKgxw*23+?n0B3{@H-6;2&|-mLrP5t}Xk_N(_a5Q{hlr{2;%-8dthwXvzG zSwum>RK<27+7t(r3mt$9w|FGEj0vAG$FcxgLTODQn+wB1FA+uf#GjcPltGA5_a3Rr z`7>~yL6TEay?i$bP*g>G9D%D!Sks6_Um$+x**Y3B_fYB9Pd1IguF@j49fF=gYOhiM zt%Y;w)q6qRJ*eaa(&-V(Bp&^D!|#Ddf{y8OAL}fBuN@GSQrOwA|8nO2KtN;%KH&1s zNZ3HXOu>Uy?kH`AatF(o>m1Q?u3l+L3Jc23pa;)tJA)G` zc3ti-7bVjPqmI|WNW~_C(=b<7R=2HdCWxT9BJL~Gv+|SsspFrqv|X}*|BryE8i%hn1& z$5DHJ{Mw6fL?+Z=`u0yl_d2De*DL0ow65riJeTvvYML_4?V{ zs_1?ba>?457PD)rJySHysHnp8;0K&%2<8h5PHgo*+4sMTvSyMfK04K2rrqA~+N`z{ zy!DeP)Owm`g~f!pX?mG99Gh5HtWro7RJPnIWT7cYCjcM^kyZjG@Xt5I_0@%|rqS;X zqcc^O4k=CJkR2CUF9Zg@Lpztq2%=^W+pk~=Z%NDNe`S)^Ih}x3mAp#5st}h3ceV-b zj>QV0sxf4Rhe)d#oHYBC9otkw{~33|Qphc-W}*1mzaM+JJy+Y3AlMcOO#SQ|jsN7c z3_Owv+y$L9q?0g8PhotnOcdlOS9wk@ezkg=Nj!c!bfDE`#Jf>kLtZbTkxIxybGoI7 zpiiKMNPMp4x!&-keeq^XrkgSxnpkmnJN|CkKm`I8tbw{a(Za22MQrY|o~kq9AxEe4 z?CxQ(-j@u~sJzw>iS*}p?9nuU{abaWo7h47f{FqZvG{-qGf7JD^n&*L##6va31vec zlE_YAEe+_{+xCi@EHJX56!2QXEUy=vtM=wMhAvDMePOFXhs@&TqFUnigZ~|zsdR6_ zwL+Io0Y#C20oULr>twmhjcAAw**{K0On5W`ydNZE9yT60b$4{U(HJ~?zDONc@z@R1 zyH29a=W@I1Pw5RS(t_2mpm4k6)6OY6*aQnL+h_jaImEU}K@Ll!NNzfV5cE7zsnLNm zB$FNePgt~4gX~hRuiMpAj9h3Pn#-CNvV6PAOPJ`-;VFP%Z_gl;(}RBK^6@=5Y`yWc zyLj!_-VO!&j(he2cq@;=g4>(qu5aJd9{u9gH{qYY~h1Sh{C!4us=!gQeA^KpRtKp`k!Bvg#= zlG--UbV-{76c!fTR);lSPjot08cF%J?h&ZpaAfM?RIbBUlLlpOx@aQA&~HSSj<_qm zJYXfqPgx;Sd~NQp!=dez(DZ95JY*IOfesb*_FqEY%^pbg)F9^KP-zOfZ7#j|8cvSt z5Ue<;A*oOZ>Q6e$n~XAm%VZiTqxm!#?;2@-h4;6_e;2(^3fy>xUnwq@2G;rciX#+h1Tf_$Sfv3hW_$NR)85n4gXtv#DXb(j0)|1k) z1)Q*z81#icnx_osgGaCPxGdIJ?sQ;!er!>1{>gSf@FLqEQM^C>#P41sDv;ss0@JCp z9M;Py+RyjB$T(geFj!1aN{VV%No!TFtRc68LLxelvcxzbl|V;M7^n>^Ev4Yk;YNw4 zC{!RVqqF%qPs$LHk&YUM(Zu{K^yw_$(mytsIdj*l(S!_vhzF%Q=OP@B zgDan0C@t=fWDVLR-Mh7;QSy@j&C4m^zvRtj((fh_y+=hDr%Sb^ukhu+9cZoHh>XC8 zcb7dVqo`|m$~22X*%>k`Wp+c*Be+mwag2W%_SJE;5+GWm$}`W{FBUPsA_1xW8&HO8 zmqG6Bx-{@-OHgy}RU-LG#$$6=igb@b2xkjoxTM01kB-A7wao^b6uAYacO_-Il_j^v zsu|rhAyG84%muI?jBFKCzW04FqEaxUt_SmqymQ?M?_+NyDW`|H2OISb{bsHB)c&e zrZ@t+U#bI%28A^hJcuGWgXjr<1Zyq*4`(YXD6lsWv=jl#9iAAF2FH=l!2r=BzAq)b z-?kg5H0plU-Z4#9l09ZHsPS``r7BTPM7+yS90eX$bIsB5Ig=wLfH4`1M;wo9bbjm^ ztUJsYTy;d{{t4+ zs$VT!hD87-J=2#OGD!bC_@+?YXxf^=a8&$?<+{&96n-oowX3-x6>Zbqwj^6U^MAbn zusu7OqAG;3nxGGU=<7acHLE=7>Q)>BBr~H=$djev%w-2@y1WdA_F*X1hfpj5f5)bi z9>}+nN5jMK&(v@?I)LT~Q&>hz{0VI1m_jO+!ct>D%sNW}Q-MiY$ii;V{LSROF9!)8 zDmChV7O$Es&4r;k#b1Lk1`k;P!J9-+gxqd6k>zN^1^62pl%kp1rU@}QWtxS!)UTf9 zjsZ!AwDKdwczAfc!*?R1s_8GJd+50sL8$sMKB$Qf7rPW!0|7y-%0!@8=H^v-J$y;D zr4q)(;*}g#AXo|Ssue0i4$-G$424lE?;btZc1j`*w4A|&JTXW5-UM4!y0Kbp6-MWE zLhuC*2%k+s(;enW!M_NDkiB=86o(2pByUair&Q}QZ_=w~!k%<)K*?X_cOm{eswKn& zXGM8h)`8VUi5A=u7lbSl1uoKM%}swN*&yE%RgArz04X^|-)T&^}-7n@5SL0&BanOJ@3lvJ?2pv&_#d&Cy>sEEjR!5 z4_OQW2k{sf-08rQ1W0G>MWU)Jy*AmT)e-SX=IW%@98nrke^_CTy(RTyB3 zwU&JdJAiZ41fcwY+!Yy+6sv1ifCJe%N{I922Rl=v6ZL-A#E!)R9WFz3G7z9TSuIBn z7S&f6YYDbqiWfnp$#9j5;{3)yJ*_idmy=$q%4%f|DGz$UNI+a!WhZR`(#6rQBgg>FSkBOkyAv3N|NNEb?_%5 zOtw|HaX3gt6pC^yx8d56n3#BLUZ$?(i=ufjk9>dWoL5ZpT^NO{MkQ7E@2Ij!hSKr( zbbu*~N&ze`XRSnrgI?%Lu8dG^&W7W$1N2b$7cs@%N|_>CSUm|MN|MIw=Y(n`2a*h8 z$W46DJK#XNp`}sLM6G1o{g#(j3bWrbgRf+UmNO>6Gv@LYb2yeLl+C9NXO6F%17Hgu zF4*7RB$2cz+BAm2u9bwUcR7HzdK4~m!|-n~vaZ0e|=KpR=72g{}8JDnQc*zVg7C^K{93Hi|sy zwY%4%9M)?M`<>Lc?mk)BN=*GBPBg=(e zh_Z**1Z`FS5D@{a*|av3^s{zjg?7#=LnvVL8nGr{s$pL%$*)sL@_M}M9L(lB2DL+9 zKMWmKZ&3-Cg49##ck}*bdA0IZsvvn&VMJZP{iL8(@TFEOqTHRWQH<6S6}^(}@Yd{_ z60N5;*~dXmLj6!lvNuxOIBV)1;~RXtf)_i^ne;BRH$Lo}iqH1Zjkn==)`^_`#Jr-A zv|h9ogh|Lpz-3jqa?+en1ehEqX6>)SmP1xHxc@3yDLvtEI)yS;s8-`D{yRbZ;%T#` zW+Q+ZZLWU|w+4%#k~>pjstkYeR1j!)dM>Y6Dk_dTUQ2qZ1L}E~juqJEM5YN2K_2ZH z`4r98onKHR*>PlGZe9t!8PU0A4e5^iPIAi~w#BW$SMgu>40n9w=abp}Jq1!-sDH?i z0_k_f%k#3<5wm+5Y3e5QzbDtvr%Q`0>sEnFpfGUsc4Fjs3R zFLpmY6YMZ))^<|yeuo%-F;CaCsQL6eMi&Kv-^bHresy-7a^$~?+bZZRpB|x~XOd7*F7(4R0{bs=0EX4hHLTsS;$&A zZZPmCC9T5&#^)zNKi_LlLqW!3Z!aOg9x3{Xp^=f);I+YdwKF!MUCn*aVLo~CofXrZx-%06u{q{*wZgZ^>0kE~o# z-*=QV0vwhnm2fmrDM%BNs9WtWw6HnP`9$e6X2E3{t8jF{I-br889F!1RCTVI-F!58jad%KDlXBuhp@~b|d3N3c7!~I+7@=%b@TJ=fH4% z@Abn6JEPvE06hK{7EBh6cN{W%}oHj)e}k0eixRu0;U zHU&}qHLhNZvua^z*ehCwe;_P8ZzcqQ^oNncs8X9K1kfSK_@t!&?kj|R_lK>N4OrzA2= zlR+EB7RFj9yegQl+K^*#w3=-4!`2h;ktjc554guavXY!MQS3inAJtsP^AkQAi)psU zMcGE*@F!J3IMyfCd-2Zhi+m#bRKl7y;oHA2xh<~0{3d@?oN5&@0rF9T+jyP~HijlO z7_D&?nj4(XX5K;wDUUA7w>|U6=X6Ax#E*185XOeRkxe_v%cG=m9$qf%h)>&4=B$bP z#bdA|{2%bHL3IPb=SocxvHn-P84Cv*k`R`dnpXrx6CVqg(N2gPK!Fdk$6_gVSIB=K zNlMO|u-al5j{N#Fk+Lw_f}PBAwd!S?S1BEzwL*oE^aM(xARZUPk=dV}|BM2aVXv{x zSb^ye5uYAGk#`Hp3vvAFh=B^~90-Du;`7F(Feaqw8cZY!XFx!xCRc5ZIw?uFhe zj_xQLpC3Gxp>*$V>X0-rvANVT;&-qa7SQfwX#2Bv)(>cHTsB%w2_wQp`OwynvFkNP zyO{K;aOjwz*QKReeCeFefbpMN(--M~AGFVNyuE>;U-b#wkAr=foa(hVsK>Xk>wlgq zaIyy|lm7V=bKv~?ON(7Ox?9XOOZH^e-F!JSq7Ic!64J-y=hY)E27%l02rt^z%v5n% za*@2(%b?yVZ#XLTi`{Ai2KlCH{mxvhLVR#kVGu#k%sE;5{4f47l9`v2$K&amYNe=A zk)L;qw!A&?7@b>{67!#I)<&zK70XsCg#v$njm4(a|1Drf3yFpAXJ%oMq~ta@FGD$f zv#pS=(d!bMEyh^Al|LmVj}x}b=0^)HEd5&$74h53qKA29e6$4YA=~DMeHgQr*%8pI z>b2{&0sK@1*h(b}%f(FQTTy+CVZ6qiMbS_FBxm2Ef%r}NWm5lxq>$ZqHO+_i;~b^7 zfY%%vOZ##brbmHk)@?YuLy)K>g;%{`h(BbURXg!)ThvK$_OCtC-Iw$~n+E=zcm7QR ze)NvzRQtljr_+T>!-rw`-S=ifkkF4~nk zFt2W{D8h~Q?HBr|6)`bdQxTs){?YYaefQy=j@s9@b7}x|f=Exvv1p(en2N zFsO3ObJ+z0i0;&b4=O7c4J>unl;buhUGI}&yPXEr&Ynyqp(j@EdS1nM3{tAs$&4oI zFP>R`(coF$l&h;#6~c(oN^lYs=P6UTBkEn*%MLsayXlX9$AeH+P-0nJfsCo8%!sU{ zn?AAxHz-8BA%ceEdEe)Uuc~eFM}gxPPKzT`N;{|Fx8d=D)L~k)s~4ED@Lw^aNQ<=%a?4A z`gJN-3Ydo^cEg6eUqGAk9d2AruJ?Cau3_))LB#Wi=cH%I>Aj|Na-b4)t2LnVD|X<8 zg<#G2b=W!SrNRC|jQU$NciNIhj>k*l0QG-VjzBUD1~@DM3I&7K9&DIP0kg!+KP9HS zH*#EnIwk?Vdc~U33ojfO30f30b;5~n2CicpnrnvoP_T!zVyy3`$?mmF=%a+JwCWgS zp+_vT5%Wxv%bQJ!6L&SqBiG8DzG9iuO8DJPU6{ppHdu4c8Rx@@mlT2y)nFDTp^-iR zH#=WK<~Ei`0v|~D!Hk34S#ZjdBkeF#Zou zoDvG0mjo;CQa*-{JjSpVJtk|lK2{Fl<@pbW%J8!a-B87iHCYtXw@0Ep6zom3LJUI!T*)bG|4==Jx|0HdT8#aI^ z*0_7{l}YD~>uIZmAXgMN@E) zK*U*Yb;VlHeFRGM{mExUPZDWBty`}4I)EM(+jZ_MP6^4Zb0+@5pFVG8JqMLNLfWV$ z;<4HOk$AEh9zLY8Ii<7;B>HAX&wr9zC!b#CaT<}gk1E4bH0T2_NH-`uzOHpeAaw{{EkmEuN0h-MQW1u98|gb{o7I5vvUixP{-2N&>!=)=_3uc;^j;fBu$w&dkFLyz8+0ZXD{b?;aHaOPfl=WpZr9Yl+{(=P)tS378oq5jR>UIS9u zdIs8r03#=Z{yerNxlpC#<6>HJ;l7x;D-&L74=y~F4B>VRFDW`$xL#_>DN+phSAl=1 zzP{i#70^Y)Hy#))b#xPdGxkkR1+Y6Mlps@Qs_jI$GWlStPxd0 z0FlU>SYY7b>?DW^HMrZlzMLS1m|}-f`&>$~U5kD7CA2E|8E!_* zZ}*p%JQl5#*7UEXs57B63ri8xZ6*K;&-wuEm|6YJblYA$`3F@^Mvg4tX7D`(`QE`% zr908&_Tb{QD69=$tf6DH!8AdQrH+@tk7CYb3^%=DHa9+GuY;Y0_8fb421)0Om=xKM zA7l+FM;D00wC3X zHFOokbOG@AUhIqr0p*?$)#gBw@^;x9P3|RW)Eo==i_?G8mG8iOqD(<}Hf~pe4@%`6+`gNeNZIN?p-(pHH!8!7LeiCqSOT zw%Va+Y92kyd*pJ=_1mkN_-*`<)r~K}jI}@OVblnJwLZQy45kR9`tE#tPb0#Y9|48U zZ(>|QO%jzKYvsu-8)lE5L2Lw3JrbM8GUfGDx@+cfg^;t;2TUrt76i{(ISVQF$gyCo z0H!CDp%dF;o7ZP*C(t0Sw`bR(S|tdUOw2JFX2(r3$D#JTV1#2YO_A&BC~Y>&4vs&E z6;cRZ=VqI`;Q|PPQ9BH{!DlGO$n8zdm1>hyyijYVmfAC8VZbnF&WI<@mI=}vRY`Cf z6N=#<08=%9sAESUX9i0_5KGl~$}fU76FIhL+r`0P<5g*IeaSqiWPH%=Lmk|h;_4)- z2FjX!K9_`UrO;?x@dGs~QHhA$-c+xye&v0=YmyVKUr!RuEzWHhL2#B)+&Q?_Nxqxj zM@bD0!W};?vGc)eGRXuAj~(~T{+cSBO4x8Z`%ovxa1h090#j^<3%a)3#R{+&wNTJ zVM8OKcM!NzhCI10+l6vY;Bi7Im@4VpC`P9(uR;awG)O2_rHga?0nGTjeJ#3soi=IE zs%uf=6Vuzj%gBPZroL#LiqjZS{cP*!|AX_WhPi&&#>;icI$n}1ax*+ELFip$tGL;n zq;wEji8%TtDG_@LbwG$h@KSo(9FMzA-wL@ARUP zT=e5*%1>sb1l>Ug(;q= zc5 z{}1f{qwnv34<~keptv#$u2`V?Dd4pPqA~aIB@i5kOcD+dnZT}pQj@ZzvQ1WtE@*G6 z0RF6+DpXZ%{d89=5JlZ87fcs6Y1oB=MM`MHKfb*Gjz?1Xbx1iNGF)|gP4VqHUwv#k z^B?hua9doHw2>JJYH6;szU?Z_)<3u440Xz8G>TdfF2^V)Z#Z$?O6iru%bp)NORb^j z{=$!cS6pEpR8AkG;R4vAnI=%>k!M#(HMl-(Vs}ngqKS3igZk!+ZEQsS(P|OZZsaUj z~m?0a|d6m`iRwl778_FLGj zaLliUDfRS*gt*DV8hq&fOm=Qw+#==JZsLK+*XUdJdns;JmQU&#h+!E_`zzfAi(#Jq zf^!-mh7lR1qUbdwvR}M(#q^WX4|0o)ByH0(wio&KhG5W1NjZV3ZcETTI54iW$#TM*T>a*X{hnXubtSJNW~=Z0b;RzRe#cjYHJ1~qm`Nu_q~(Q>0)VfD@4pe zHPmRIw&mXp+?XKn!GJzt57(H}B{p@Urf>A5s$JGg%n_GxT|9;2Pj=)Zp{smnu|3lt zT#)#;bS9@d;}SJjJ*@s2mjo6@7qSQqu5p;$4`2u<<<=!$5>FKgLm+xO7GAFN3Xqd! zR^BMTlp~$yEBCIS5*Et-KvcnZJ8xjy7{06@q&+Ob8y~*8e+4{SZlbEt$`!9tKTW!IYj zcnUgo(_3Ouaof1$&cvU8klojidFl|rV#;3HfC+9>>uf3VXg6gDcY#!gp;kx#Oy8@i zGLKp!4cr?HuAV?U4>|4qOn17!KdTg1NP(c5@0)j*S{h+HQE$LBruyL`U+J3(47CD@ z{Bt0<-A=Y!B!o7Z9I36K*tnLoR?jSkN&6ogpL`TXAIIve=Do(PUss|>^TAtGxqxIj(>N0_jBe6_SH=j;j|-U_UaTa2vvKgDg;u-+mh<0 zoMIqCVDH+N>wh|OUB&jOT6`aqzEvE=vUp8d3T(bS2>I1yz-v>kgo+sd8ch~4j59I} zEmOW}HWYMTl&1Hc<+s+{FAbZuTJQ~RJ^RrYXxdAm+wd9%Vt!4{gER{;QV}ERhJ#tK{b(bs<&DZlM zO8=0cB#$QdvfrxpF3#Z!&Qmxc*)2~wzFUtHsGg>T68oCtH`vPK#w&oEv9;kOpQy2k zByU)IJhly35=3%@e`+OcUB~gee>4F_22=2H+XH#ihb~oG+M+LRvonLnJ-EEg_ zmbo8g;j>Diu!@wu{En6QE7Q?bq1L}1bq=B6Q%P8FO@}!8;K;z?nR}w~)Jhh2DPQn4fU~HusQSy z1zG=Hw=Pxi<6cjA6qlGV^_^#5iPv3yiZA^K39f|b^UVjzUUPb;%5bo4>05n=8C^R{ z#vGBI4d@3o(Zw3zpuNCjgN?Qb67zjcdDo)Cs5>qDI&;xsl?ZrKwk!zksvwVo8%8)y zB=R6DSR9~c>9tX0)}&q;Ur5L^m%}5WDQ~siSzMEWi6Y1~#lgNL5}od!o$i)N?p4O< zio)+WiVX}d3S-S26V{@oKS!5D4<#(=8A)N=h<7pLS5gtpQ2Rp@-|AVnbT3k)QpEuD z1>s-`98j@Kp-{#FwZ=m>1&|NA3$M26v>&Q7GMD+XxHE1lLX^w(Ftm8s}DhI-TMdb2KUqnZG z3>lfmS82)j8D8WOrdavY-z2Pe4R)l~qKmFIjYSHmldr#pOj=B-rY_oUAdWWdI#ks< zzc-5g%gS;_Y^Dq6fOIi$cV{w~OMC~h9_?@{PL z=@vof8PV+8Pm5}E)`L#9t(%h!v5?Kjx96ujl9)0NOKHqYC9qE%e(gg-r{$FYt~Up8 zoOa@D5(O}E49@+kKo}|FNRap-L2-0Y%m|K0c18S&p1|^F;oW#J;}DZQr;=x(vbOr- zz5XZu;nIW9>C7q{`)h$MVznvu95)dE^vK8{ z=~M~UrbaBSE;irr#U_@qYcs+QgzQEXO9vPZ=Q>|8_odL-edH&>0gjs9+Cjg@dw%Uz zP?9lXzQU2r(DWb|fxIJR#Ra6RyiMPC(uEL6nHo_&mmJOR;AUO;qEPh(7FUdr zEcZT=6R~@11fxB&DoiTdv@jJZWNq(uV62tHb3$G#<3`}!)J2f%rUvluEz?G7RVd>Fe+4xXN!kk41V0f*UE@Fc>ro)fcWa$Ny^h(7jLZiu|t8MAMITkOPxJ`S0akV4= zeY$`Av#@czU18@(+#m)UniV+Yj@*k*K_ksYykY#_k8&cXrt|lqycW~we6(~0A zH8bTMtphkXIT(DtvDkC?Mk_xSn^WFoeLnd~x#_?{4t>#p>Wbxa2yyvw&bcc+VsarU!o z*f(o*cjuqKImrOt-X@d3qM-JLF`}vQ!9p*EszYL3$RS)#ec6&ARBJh)^g+7!2utpm zr(V>0v~kWyqt*G%M-M<1QfwvM7)Dv!6TiyM4ZW9(*o zCI!>8fwAfR2$Jn)LC|Qu8p8*FC&nL=csbn&t6HfO4P!BJ?`y(P?E8p3s$v`FB<9@k zfBxos{X`c--72B5SuNLL7Ab1Q*}(4joVHnhfkAIL+#v)w1aJ*p7`r+ z8$%N1eOEivguj5a3$8~GFjgw=oAZr&aIGdQ!48jm`DQ!9b_OdHsc1h505W+DF%=co zy{$#y{-;&<$B|iqD7tnF-~AxS5lq7EKyA^fe^pU=%>OL>0I`Wx&OvhZ4{X z;A=eFh%MYBadUT111fdq1+m;8Gn>_p#E znZC3k+ShsigUize{f*P^Q(bwU2E6{jAcju#W8aKOmMjkuk+C@g%l!HF(Dli}G3#`! zmJS3%g1S;{Li5+_kb!cA3ZD36Ip$$l0u`~6x06lPPTsGl8FV)vJ>T?#hkg*=w(Uvo zm0iGgn;G0o{Y*dxa8VT)?>h;X^LaEqUq(<#wiim5*CVP++>_-T7kk8aFoP9zxaj7c zHIw=p*Ey&H0C|`5hG;wEZClyF$@9w-L7>m3&$A8N&d>lhlPvhbdo5BC1}1=E!dB@B zd@8=M4Lqn?S30`6bw2D?PukTKpQ(O{9*CzD@ndhpljO}IiY5)B!FOe)535}L>o15P zj#Q+#OCCFzF_%+YHe05A{w2GeqJ5Js(1~)i9(F3&SV=`VB@zvYS2-`b4V41+-@kD?m6nm{uZiQlPcNF^FDG9Y0i1Mny zT_uJDiY<8aa>>;*jDk{^_A)-ctUErR3|8oM4;YcL0~yKbECEGDCX{96do(H$SU~rW zr;F36EBTyk7Tyj_`1|wN!;L3|4$t-2RPnB$Z{MMr2!u5nY`mfW*g_c6l90IBbn>)Z z4=&9>Q0c%VQ>zHv9$nb$$I zeevaw^0@Ad|c5 zHG38AFP<9hEBH6rTGRd94&uM?Vr-PJhx6=8I65d$E%a26sN*!&&HaIrrD`Q;Zy?{| zB@!n^qfF9;lopPTFm5GfCHs&PG{N1+`3b%(RH){eo0bz+OwKDGr?%`*8u!kb;!F}? zrb2JZWdx8)rEWjCoFgCu&^kOGx^sNrqZx@q`=(-7EJ?cHnDqkki$fUHdoz$XnMr=y z9`toGNdIC$)FIj4aC=9+^FBkg7&-6n?x9aVaTa{Q4HI>6fYR}HC&BZ&`idN+s%G`G z9wYntHml4Mn15`Q6A=Wg{u=}Sf2i+k5)j&>+8k$I|7B+fo+utP zha0c}7oP-8cQ7SUsuQ+^9_>uss{n8@pRf3tRrnLE^TVAl;@L9~NHmG+)eOV3aW{8q zg?V(c1y3uJeYHgN;=LjA8&aH|t@SiSxoB8O8Or8@=w_ z!>zttu({66fR+|9bNlQ8|HoQvBwIDiP4?;RRFk~IyiC^Iq(|yYj)aP>2 zGU9}>UL->|1o$I$!%HIlq{Vrqa$iB8Q3b)mc>sdX?MbmfxL&*)4g0e^*DO;^Z?)Z5 zWM|g2pp~jp&^-f<TRR$R^sPT_1hZS)72VD*i ze6r{rsvCR}_n@vAy^$eH3U6a-=x0YOl*gy`RIsi7^)>%kjsJy-{x3LgGaKS?JeGaH zEP?__OW|?~d8`&%Pf8ZBf=>QSmLT;N&*)D&?w~(O3@L1$${)A$xZKnomr5d(bj-P|NfF5>`4>^Ynsidh_a^5N-D2;P7Gyi z!#E9ODlXycU*nJPjp#gU#Cf%4>e;)>aFQqWrLrz-!p{hhS9HDS3O@hPp znu~0Lv&eDZ@!+3d?@ksLn6mXrIKdQ;sZl&^?1SG)wg@I78FS%b5BD~o99`UnOpn7a zF>ke0)JjIZ+ohPh9?L=jETfEZF=Z5v{$6Wc6Ii_g<|ALeR^lE^9Zpa4OT+q(pp!-` z?SZI9hC0~V7bPgcS@_>l?B^D+9#;Q6pU5DOy-ud)q)WwHPV`!;Gn6RtjOKAaDoAyo z*KOGx&YrjRj>+0qe`v?~nhxI|CVrV_SK8Z!cgp6k<2)1-=xp3id?oqh%n{7W6K8?X(ZY3<@%WfMrW#J$ z0~@9k&U%HL3yxp=Y#yy^Z^%z*=gRm!drRw1s=HyD2r_wd-tbeNxr$STSOX6j;^I+#T`8 zo^pFThy^AGN%sq16w1v=28ESlF4q=2uo1=HRakOU$f}JjF%=y8-r3X&$&2@vke@t^ z8(DE{+Zat%8hkqe;lt3BsSRA3>PwlW=mx+_|odM}tp}X91 ze?eXWbc=6P1u;4va-=((^(+RRf3kEdv?c3X((hodyHn4opiIlOg!@)8#xnCO)74vs z+&^%mR{Wz@-ya>$9bGd2nqSeaaR>=C3r3ganBj5L1a7a2T%G&bkT~RJTu+AYh?aeB z-L-na6}LcQ$HK%dO-4pt?3bVxciVG$FHgi^vo`YGf2 zoQTXWV@adX!zNMl!w_zN<;02MQk>w^@uBXt@6xLJ!=jpciv?@8n;Q#OW4QrYv3cg% z_m@|LS?=9hyKN%i02fPoL)p~ufcSXa6lOSQTXE(IkJ>9>w!(!x4(9FdymqO>lAZA= zVBhl3-%Y}_9(>`N1MlNhwdVYRW!kj63NPFLZ)FoN?rSfF~uI1Q{ zPS>Jq(y_!v!bu3p#$0i6xF^T=8CC8cH!_4)c%pFLTp^dKeoQ@Hp-r;){>Tp6V)1v3 z`R04jRfhtZLHnp`#%{vg)p#GTH1-Ss2Mb^N+gHuaS*SoAjF<)NrkKzDHxZv=Fm*b0zmp;#buAkLu zH>*a7&Dn??i8#u>q`oQiOCjjGCxIyw8CVc0OkXykt9_$d^GFdgk|O@doaDLBiHT)X z1ZFP?hjN&_IDjD~C@J(;d;u!SVX6bZ*BL2atl9kP(qr_=Zd(G@4S@SvF7bJu`U{%N z%!S|XPVs?2AoBL+t@1#U znJvscb2Cv+NK1=CSvc%n?1q;o3&$hiPtj~&4eMgR8XzMKcnwJOr+KBz^uU&dGL8Gm zCsq#!i99Jv^$t}K#MHndHDfKAJF|MeF4)NEu`)&$n}}ge03z&=G)Nne92hO?5~Q10 zIGha0;+@Nh12~Q{1#GK-*Da|1^`-un;9q&`XGbGoBL}n2SL5yYqw)fEL^hCUqqVyy zQO%rhKjj;(G+;hAO_Qjz$v%CMT+K;h0x$;F6Vq`LXhWG~>UpBVEBbzH>1lF7#g)zy z1xwc;Db6BHdGlpIOM0NP)QQy5g}*}C>ng4go*N6lx8=V?_QEVkom_j|5AT~?H zLKBOJhi7YlKlq6dpCFFu`OAJM1Rp*f25xTqSrOFLJ!OEMj_}ACtL2|>hzfS_ zw8k5fh_&G~=OjPmk;Ut@itU*gQwslZ6TS)XL@3T_EHZG{j?e&2r40rsj9{k$umI>p z;^A~^VAMZjC_WQ3WloWE>LaWxxYxR1#_s{awdRA3gp@e(Bzwb5%DORTSvPivjSQ&QQFcZ5X}YwtGHapNEPprK*F& zqYyE-FHX&v!eG47akHe1w%dwP9`>L&$rgs;&DaMq4Y&Y^JsMCN#MJt#Q0r)5_9q~K zj5Jwd_T3318hc)NrzcRI>s1ZLvxmO?#d!ST;2+}Ug-!R!BuAgBN{2DSGS)vh0#^Xa$Bl2f}j4aL3 zOI`N$MWxnmK$B<5^-J%=nRsBGpoxK3|I=^kpo#hEQdC7GfH1Vd#6i|kpO<7=s2P&N zchX33J*6idH+v>exs2kaHhQf3KURWSdcMr+C?mxq77q`W_iSm2n;n^Z&Q_|6zaiv4 zeCXhcN-`*>T89I5v8=;7BYc9#Mb!NOma4JhvIf^D(Mw}>V=u2ph_PHle>1x+EI|Py zzV{?t;xgwbWVvo7nsh?Dd61zGLkf`qnK}d4SwbfZP3&L^q{`gzKR4F#rJ}bZTVMEz zE%SGf#`FCGlvL<0VnOV~2fJo&WDuJxB~==r;Dq)%Dc)MWkjueeF?(qmtRQKY8=zr@ z#&RXsvD5`PmqM{D5y5xlyWB<3@*z{Dn=Z^|rfC$%H5yZ(K?D>=XGAPoDnCro0xVbz zEpKm{4Zr3IzI#Aeuf^4!F+jQPiO+`snmx~?u5w$vx%Rax4_L1T!Ic=Iv8EL=pTLwt zP>Xrqyx(2!Y}Wf}TyXfjex+N$={kO=;HOTe^M9|sUUQAoG#Qe~;nirIRe15^5;CB} zCtn{SL3=bBGPfi-QKRv9vzTCM--iQ5gYJ*zpXlF}YY%vEogW%^Uah?dr7ZRZT$}}K zSq5R8A#{IFgEF3JM%eti>SC~P@lJ6zc`Eu%K_r_HNuz7UBS{2*tv#;y`*_Bj?wu0FoI3l_(u07I=Hi#$oONLq zK=0+ZfPa|bUwui1S?>ksKOBM)8KFoH$I zz|b=qgvMd_em(paG#!zfTN0{aWVY4{h4%JA-uY~gb~Rqn~$>W z-o|ov-!LH$hrPd=tIEeA_qRV8znEfNt;wT6Ukzzhca8G|%Mb+nZ*L!Q>p_gvQmn~1 z*c>@dbuTUs)p*eN_iq)=RO|1*Oi<`FKzuyEgG(1`2+7MMq_553n$J^Yyj*ir$mf2! zI9dVEP`wyfVdW=Ct|*7+XGYodxrYP_=Q#)4KZDKX@MO*WjoPQ1&|i3_p*P>JkpE0a zZ4XtG?^Z86P6KWOpBN255)u-|31$|jQWLA!ezEOp@x1MsDMQf46g!vepB<%c{d`h> z#Xsi9Fwaz%N;JBdhgYbyKsCf;&HdDo9pz|25d?0wuOOc22S^6C9z72$VemO2ym`Hk zUk8%V4aH>m*>Y^rAa7X?a|~%bRR4|fCkO~GB0$@OD^xViN;IpyUkdSg;e1D9Me$`s z2B1)3zB-*fF?#*bxPR*|sWBSBV$QRYFCiZN11ENxfu%oRbI5ox(R4jXZENuIsGECk zZ4INppS_6dc=#E}jfWA&ZBripxQQ1Vn4~K>5V~ zb;EYTP7_Pk)F?QCwNN@BR>@*fTbb4cdFji9^r(3I{Lt2x=bi}066g*3T96-2lli<# z6J)(&fhMg;NuIG#5e_V6Z6_~o_RIN>4_pAauX^fqk6>Lb4ryP{9hHkA zD;gY~G>W|#lgY{g;|?EG4_Y~@FctLI2b~_DTtHDOLywjC1g^Uc4m8EY@w=IYb$X>6 zZ&NjV^JTNRfP4A`!;wfCoL^vg<{c5R09Aobt?V@;M0N#)xm(a}i!_5vc~clfU6)&k zN6$w}4M7xsO#5%m2oP1CFaMA zC)~xwG_;m09XnGPCeHJjF3>OouMR50w1eM&3YB$)1YMjT!BBd@V%;0hExRyL<&M$C zSQ&GaYVQfa61bF8*U;4nWw3*(_esv9aFaPxT~?%p$(Z3L*k59;4kng&9y>cbyZQd& z0)x*2rrn%rzGx12sn#Mp?P|skdDS(f07LDEWk{01+>auKMrm`D5;2j9(S3z_1@ols z%doHxhgK5_hIl{9c^ih9u|$d9XpF$lSl%G_Pa1*hDi8&xY53>Tyb?+BMFovmY?(}< z+K6JwcQCk~lu9jo-^W*mTwQc4Eku7t;t+=&FPN%x-~9o>rRF``D2z3HpjL?ibTc=P zejAiL;RCpK#w9f2WJbKqGgWpl$zb?O0fWg3MW*ZszDQgxJ;<7=qJ=E+-SBTMyALAn zVx-uekP`*J5n+n&n!sYdG$y_}O0?v{JMNc6S4vYOn|HEmn6T_{gU*c9I*cT#fOEpiUFnX8`57e11 z3A=k(W@wGSIX~xHA31NX53V`Bhrn8q6PFlN$LA2uCf)G?8z4pe?Lux@Odso#N_B5M zBkUQ&+{BL0=V1okNg^72#PBj---zp%f5DpeQX*PQf{uc#%>FTM4o~-}wYPt|XflQ{ zkjFzFL4`Xv7XIy?+VipwKHX+%8k9sGI-=Am(8L#;ND`H=eHr0bt`hx#O{r+QK&M4; zJtB&3alymx!~u3oOq)GJ569-N;=^b$fJ=~eHG9N3_%5^`k~d%4=Ad9-k7dpNPn9OE zN$C;&L-ak0wOb8ex_fnZ$Y8uwU!CPtYraASSHF#DTeUVmo)DEfNN+Qw>v}Xy|JQxr z;?ri3;8I;|52hlsgb(=i3jsf$&n0d9$iONkepe`|uaCcwlP%HQqX)a%kfR~lPMVKg zuE#)K$-NGp0M#`99{w$QV$dmg5~cB8%z`p6EE#aI5U}R~1)(0@T=Au~a+b)BmpFGM zU)s6z^~TVP-51rncRg5PccW)5$9FIgel2#9+{aJ1L~d{w1k5o3!w+-wUBL18H(>QB z)jt1cfhKG3?$v^7T#WmxQwR2(?;Gj|;e>pf1>}5d8EwK5a+&mbM4Y>D>ZEIdMxO^w z0RbEtIrgTdwXM1THT)tM-+|kV|1^JPTkq)mB_;iN@H)4mo0Z7v zrY1gdvQXo2&$Z^OYnm%z{2O{T!o9w8iUe^0Hk-jjp6D&4g9pxmT&KlLBXe)h>oFT5 z1vhMK1yCwuzhq{lKD4=bt7Q1<;!U)rw^8@hr1UmvzMjm<3*wXTWa0Nd!&s^gpgm!m zHe#AK8|G}N$03($+XW?0xygGtjbK>&QwAQ9uvIPJh{9&I7Ii>!0NfcU1b!ejF4Vi} zc#wN3+_9zGf|$Ml#cHHIxDB(mdMo(!`fB*r?|Suegf8RRP&)JaVbfgg%I)nLPq@dq#mk?E}6|Id{lT1PD0Y}pmoG=tYo7UY#8g*r ztPyt}>>_r9*He#kNM4CeX3t(u_^MhjE_ABvBgL?8btcfTf)E7n&;|VEcRMoaaU+Ru zWigzRgW7LO{yQ9x>AQCO(^2*IQ@@!6=Lv%*+JD4|3j*Cp_G%!`S|y@eT%rQVtTI(d zn1pvjx}OZPjjY`sx5@g$?Q=~`pzsXK#tzu+*$`pub8y)r~JnZezYD2Lc3wxkytWE0D z(h_w;zRZCtmC5v#FO41fQ&N@{;{!OBfk08()lItk_jIT7Np9N;SbVP6jaF;wsXL)l zlLZ7%hGh=?KSr}@g~xf}2CnHI&Zhj1bEe^5Nba+blH*^WhZ94>|2oZX)^GuxI?E)+ zm_u!Ugd3w5QinUS8{%9K=JuCA;MyD%ekrtC7}oWRIx$10N*K%T8`WZ9oNk3)cZUbr zto})7R72s1g-yD`!8e3t-|FAeCBcr>zgo;nW`~1{GI|DQ1jP)J$?CvYFA3f=pMw&8 z0mY{M$4atm;xSoKct17Mzd+xj(S&HVw8E_IGH^PlY(G7Spae=enJu79-T8hKjX+_% zQPp(s?f;dFmFD%L(aYGNjk&v6E%@HbB3QY9M11?hvsOBp09isvU5!di?VBhx_N}V< z&JwVUrH>egpqIeIH7c%u6sC54J|0XmN?hu#g)s$>%$;R~S2jktD2Ry*J&+K9{c*+lJP`5aH z(n@#iVUAYpka79=f!4ezzT&bzhtDP`4vq=w} zkrB#Lt$IAowGBvo>6d;4(|0qmR?~AjD?@@%(N$u9Z%auRYhe|OnQ$P9T))769vcTI zN|V(R;|30~BKkR5cz1^+UvW0td=Y5;3_kdwXD+WpuhwdYY78!@LJh5T2o4!!kQwmt zUrG&M9G}I~1zIrqi9_Lv4851on4h^i-r)`R#tg6L$|s^eoZ}8(ZpKmG04(EXHN)cB zbCAuWPSW!~^5m*tVa3k5TngNk6z1U5EO84%g8#YEfM7qeY_gNX8QMW{<6}iqe<&aI z8WZk6XDEoMVY5Y2N`s`k-60q+98^&cO}tSSGPZrtK!r`qhyC5)7Nx#ax$9OHU-wmta zSFPU4R0w(+EIw?P>b&4&UZt=!T0C7r;SxnOo7{84BSsCzjWiXc9xJm$jj2_0$@Wi~ z<@kO$E^xfrzEun#A`#vxOF%j1%d$tplpTj+dJ`sQ8gPsBhCj`LY8oy5LH%Snv_Z)q zDPcKDidr+#nel`QnEPlH*S6yd`6>ro)%OjMzxxP7XYlqug`}Jxk}2&fM;dx(YV`eV z1Ix1EqaDg4&-T-_tV{`s**7b=z5gyOz@mokkUy(B-B_qSi==4f=~dVFHgl!<@dMo6 zwP^$6`lezDV8Hg8uo@RT2~pk(4s7zR4MT7YvilFpgb(r#DzE*7jwgAPoHd{6p6f#n z?%=)Z^&_8tt!ngoBi>S(ANz`(Ls%kq>i6{HCfnOe=1NCO@YK)3DW_%hb@i9%|@GLZQkiJ<%&1B48v_xJMc^2%!j3xSw!%o-gh^0&@z_H7FV+iS!ff`_NUwyIdGg%DTt{8oR{o3F09* z`QyxR>!A2ej`0jgBm9P=W!n(@Vz>!P!ofBuXVLIlgO(Y_A7ImTCpWYUA{=&zB)9 zklb&yPG{3-IbFRl(hUg?(CUK3D(VlKy3-6&S(z&mm6tlXBAHI%_qu`%JnpkD}hF34M9*InTRf zc2g6Jt|N^b<{{Q`pMXio~{8fg-iZUt;10h3#92qXCr7AtwYDcE0r zk~uyxH~Yd@E7X0AAAnSaHWwK$)^jZ$u07b7Vk=RDQ|@#QGK2m|;Trpo>Z=0PvsZj? z>52z==?{z6YyF4*UHIMPcwxjTFZ=6jib(>PzM-zz=!~ZAYjONu@yi(Yt$uP6V}BB6 zSHu@FkYL|^E!mIlLVNO_o7u5yObzz4U_p#GXMDGnJ8_yB*eJvtseubNp92-HM^p{cCzihrHZm9tv zS23dir))I=1}5A4N%P}|BZ=N_@(*h(JV+mVqJ94C3^+ zQu3TKfG7M`Wz**F?Y_V3rpNlQ0DDa6V3a5F_A4Ag5MoS@r+v~jGpEVC)YH!HV{~vI zd5(8XwXd#7myPR|zkc^-wDwQcCUmrT<8XND+onS8|QnAe}t`NytJF>8x6VCjty!%mprUK zsAC0eUOrk1`@c@w%PR65Z4SHpmg1}vLrLlKbi*suLJkVLAv8S>4PrC>OcW6PT%D3M zL`<(`EB`qG6XBy>gJh1wvq1k7MvCoBC>lzzRI8(olg)94DyGbVIM+^a7qsn*H#%;Z z)ZPy>JkTf99>R2cGK?BN!%3SBM;ZWr-llHSf;`V7&u_TO{?jIk~v3F4y5|h3l_u??3Em7i8y6K5)e)#V{nP+Hqpa1DMJfF z6u1Xlult)lP$wDfXTLMT$;3#tay*dyZG-$EAi&(E`pk5;up`Y$8fy~EcYYr=Q?72D z!O;=~-(yCv63x09aM!W1ij5hP?9{omP@OVst6ZtUw0y)702z&Zw+H8(9El`LiYH3! z%# z0WCl;KnkTV0)Q>)g)7cLtbQ34B?jq>Z%F4!z=z14|4e7!W8QaguFiLe9)-n?C->lu z0?)GokKGu8jjUR7P}_+*p92kfNoo3o%xyMV!PZhJ5|L<9yV8S|976fq{Da zD9Um>+Vs;dm!GvLF@Cbv6lvcbY-F4*+TwOJyZ>$f^mvX65io`(te-xCrq@NJ5dst< zU?ci7j|iU6VOWdye^o9XacfhZh!VTF)D~FBLY2yi@Ck|_yS!VM;3jvnnS=QXLsXw6 zi0SicihB|QhRy6u3mSADE?SatnlA>RBAdMi=bsWJeKEW%yp$e6C0!t&d)*QBj?R?Y zeVR1uOD~AwPk;S>jj5J4lT_`FNg3MqACcE5)Yp-mJ7IIXF=5}Pw9JubsCJVujx=5o zMhLb89`f}QHbOcqXzaEoIvzg?ri)x zyRlqmJ69z!sMX}ygZc{=cg5v$`fOymPoeK+FukVf#(M_tpKmX)?||Su72vMh`P`y- z5WaE8Adu$z#+<`47=ZZq^H{kZW=AB_cPOP4oc-K{ihirm6`JMUp73(8nDi&H#CGVL z2d4Mo-pRY+f6_V!s&5J3Wm{Z|*$#GZvxX(6Rv!lDe%z6ard*LOt4t45b|{fH?a7(@ z3NgXw2oQubMDxyaKE&z%c*dP$bd|*bi9``n)C7Zk=Sk9s8SZ|2-A$AM2qrO7Ce?F^ zLldr`U0=#-ln@5-RXNCCIz<~k)LMKcqIovw&8gyuGU;JgY;yM)s#C1oA-{sBh4MnnjA=W^&9i}sM zLOpJ`N7_qz;2~CY3ru*+%0d41Rsqgfp6vIg9Om$05fwT!>F(W`1cM~l4<9Syp^eHP5Wuo>-H=-b;j?qaz@)*#(`+#QQNG) zf|b8RB0#xc0_h?xVu}A;$igb)4nV(DyRsE=P5*RJfBs5M0bs#tcw)!*h17yIK2{32 zyjimKj76T5WEuvm9zOqG%cO>zohDefCH?AfPn{mJ1EwLp&idS-HLg62YTj!mm-Y4e z&~hvo&lSNQt_vL9BZfI=l2_uktF&LZ6axr8=wc?rG=6Izm%294;TvtUw&f{^a(76O z?}l+=MF1SX2yAr`>;y%TIkvjq*{{2Z$~=~Fij7JqvMs(UBJ&|0ta+|5^4fAumEGsw zx*MdVQ~)KCx6LPW_0^Kp^~N?i)dY zr0Bw#e~arde7)9YamKwjOvzcgNlqz8f{QbR;~eS7G_-^V7$Lls9pPL!UpkBnd^u0j zT~W=@-mgA=rc%R4)cCRDlH5}@^fR{1IAN;leZP$7@y@$^4B*B1Gr_mKuO&;RI(|`X z4?BP%Y{mLmdnv5oAv;Wbk?#mc39RcUqw|B7do+&;?<89}ZPhB*1&xyRygj5XU`nId z@>8YcuTh2l%1i74H<@xbE?Fj|!ICq|*U31AW?Xoar65+P_v|u%Thgiw);0MnX^1n0 zODW`Rx0-ps_VwQx=K%tO7N=T~OhaWiMu2v3JJMuCh)RJEMZgbGQ&@pOsT3i=$$m6^ ztRtkaX(Hf9J1Z5OwH8Znm2>^|pRp;u-O=yb-zRKA+f?=j)ZKm*PF)#2a@X1e$&U&%oG+y?hk9D~>FCh95FXd1R=Di+UrY;_Zp~0v z?8ogTlXMRUOHAxUk2t1O3JHh&{wSmt&>T`vX*5v=9-2Fw->yL19j$$({>c1lTlMs( zR1tE__Z4db>rant2!vYNbo zQGrCZL(oCz)q7AGBbMCR?y=Gp-Wfp@^irN~5EYk&h{NmG|h>IvHo9T=bT5 zY(0;61r4xLi^GP9@BN7VW3EuBSaOmlMiIw%YQYDf#MH;A$K$W=n0wq$(g9!x}Z1u2_h4< zHMk04`78C{#1R9t(nN8S5&H-Y=DQ&h5W1%M@gs$m3=2h`4`(rKm73pbSI4)ciYr3R zq0rIrf}aXmg#VQBV$}@Htx02fGvPz7q5>;%SUlxE@%ppBmfQJbKH%Xnn0et}xQF#L zX26xMSEHsd{Zl-y?@fqBO=1w)Qj^DyG+uh)gbN$ z&Vt9L-~GiIJ$6Wx%T{5=4(LT%wgh-ywSf{Dh>_bor2b-3PR=FsVGpZInNkvfun?h{{F^8pmR@8}|*NeZC(xb0AXk zR6nj;PXlC>tk%$s$6aoj6Vft~LajGK;U=(ihkjPZ)fhr&ZI_sGt+?vxbD4btt44;P zdym5jc8k~F8-M`Y?x(G77QJnuHJ{TCF=(2$mSSWoM2D6oup&hIjxVlN!zXKBKJ$yM|Bh;k zRAI%Jiv>Y)ipBIkXcU+qR94Z_oF4-}}}@-M^o%uIf{#_jAtP>$BEbW`<@Wt6Nq27_!x;&r8`- z66_$&BKuLvcyi4z!??cG$G2-MrEY<%vpE85>(GPJOSF)*jH7Y$?vb_`8T4ASQ52WU z7o;Z%iG15-h8yG9(EbPZV@cH+nj~7&z5#b8iwiE%{3z@Xl%F1)1YWsNprBwkYq$>i z)1-=6RSlu94}GtP>r8e7R_Wygy+G^Y+0N(w75RCMI4fde^IEf06*znj(2T#=z3UFq z>|#43$~8NS%fqa>zd0!)ardqMs_oX-Sttlnm>LoY)3=&>?Cf@7y}ja6=>kpYJ(@6AJ;&I~dtC3novYdXB{#LY|No1L8QIgCRsrOhy2q*JMk;;^ zC(;}CTA5yMHsGSmLZ)Aigh2kMt$KSL@8p0UgVdLCDH^3 zOm~Md_!@pgK`bx}Lp)PMWPdzqk=|fxOlI2^`)N)POR+T+r90a$shLn^+_{Ez<080F zp|XXff49x?cy8_C$u(|4qg@Z~wYd$J)%GNkBAG3^7>fc8Pi-GIz<;3%OgK^M)>fDM z+;xb=_5K}XgoE+^7%^37_}6Ep))@7w^G!ijBWL)Pn26}x{!n)?r=j=i1uPKZLll^S zjndgT3Zux7COXRn^hZX*ZFqkLLX&|29v>V|?yHeFeysk}Vdr{9*-RwiHrXuo{86`^ zX@B#5&myc?`q5Jl;m`b2G`ib-K|Z6J36$aQ3VQXbhUI^?^uGhk|0ZDnX@DyNdx|E4 zo!Kf#?&lX4*cSc=ffcpLfXC$&@28loM-mW$ulhz{tJIFu=YB;(d~7V$TnSfV&qeIy zb6oUtIOQ5~d-b&?`CR>Md~jpz;dp{Hkgmy#CZTa=&T1iDMNS>dmIn zfAs`{Alocd2DkdtNi^vkIv!M}J1T@dTyk-0-YUtxpTP17)(TE4d+!BgZ~i>Szr1~= zZNEM8PB^8V#4nPC#MMhsovZb|G}(CY@ZXPN5FqZo-~_vo^LJz}!`7BpqyK(JMBhAy zPP^BDxXu0|I>v83wcS7odfm<1Yt`XeAbp;xEogLf?VqPTPa#EWOqnlt)JQ^vKRT&r zhMXuf=+jZ_d5^L0UTqSLB=_bMze+gw<+yYjT^p5wtkN8*yfqIZS))k8DBU$l2I)L& zxKXGm&>zviCGi1yD4!t%0rhX>L1HXI;^X5Z=Xw8?8AJ1Elb4&4LOK(;Y72ZHg$Wh! z#3diDzEkUD{=awT|5VhU{3JvIBTVv`d2#Ut;aqWc0fWBE&3a)lkxb7ug0&vAB9NFj z9Lv``y@6I6h15UJe^QPm(#GtWzdJ?>OfUE!(>$JHuJvmC*-$|O6Gy!CQ1$iUIrV+# zu*GP5#0mp42k{%c#-L(i4u(-c1@}&}u8#4T=KSq_P51s4?^^z~DIzH~HH>PBzr&xD zp|tm@EEyWXzb~0aL8|!Y!l=`Me3A!i{8`sSTb*%$j}mOI&m~+5+o^!x>@hNe>;5Lh>Ah8}Il+@a08Wn1f2xQHMS431kVoCeqSG5;nBxY!_}9GT}> zI}#H8Pn-YtD>5>0^GbA_HBSSpuMh?(Nsa>E^s6T^E_ZTR0*9W+p7272F4QMTfK=hr zDv1QEd{K8}BwT37N?d_to!KplY5AF=pfynK(EAE+{NQ;cPI5Kyb>8hE!x^tKEFEHw zH^l_L_&i^{vkT?D{~yx3IA#_9aeKUtC9tcSSh0n)Nq81&ov_{CxiEG<^XcZy7k7!n z>;-pJwK~G{y}q(vTgTQDJ562ZXDBNI?w8%1oa#(yXWZM(1?vZ~^9p!+9Rlu4y>*Vy zF#qf`vyb%MIno;jI{IX3X>jSGBL-qHe*}-%;ZKrv|R{!ZzP|yT9F z{cE&FWT75Qktjsw%W(mrW2J_3Zb*ci!A8asBL;agus^RjbF!G4IL5Pew^oaL6fUQ; z5oFqM6dj-G#9?MQUK~~Q{}*$d24s$bLJaIkA_iu7ao_JmBQ?-0Sl@*0l@gUWpp3l{ zzA<}i*(wJuP=P2ys*iB_45a=k#>;oZXGJ&iq|wE3XK~m>Zw^j(m^Pis1vy#vCn}ap zkL(aOR~k#faUdIEK_E2ezT!&t3Omps`GZa$L}HK~CNC3=*qyX-eR6G?{E=#euEC-myQs*mG3hJC;=$;iLZ@r(JX!pK5U9_%vb927)$Kp!(@)~)Yf zLHQ1S!tX;(o2Qvq)-3bih3zJX$mnp#AGCsg;ok>BkXow4xUB8@SG&6%p}&>@w;@00 zj5PZUq`}iT#KYh{+>pW>Og)~f!r8viJl$xv)2efD<)zfSJRO-te|%!}lGn@NSXSHo zrlCcQi2%%V--(tuSUQ0#?s%rXEmZT7A)_X!Di=6;Hnvx>7yG|~f-0CLO9@yy zv3?$i30O2s9yLr94CKqLKUCe+I84d}^#zm`an*yzwGx(%;#6+V)zG2wLagluU|7SqQQ{^Z@_7e$4Z zL{9fIiB(I#bS$_F^fghybeEd~fV_Bh)VZ0iHSi>Izz2Q-&UUPi`ad3HVP|75q}rY7jlVtS-fUWO+yO8w-kRvcU;k2<$K2-d4*Azc z`5@h)fpT_om|U3_w>VyCF3(yvaq^wkpYq=8?%Q|jVsss4(BzeGuO7thXC@WP|KZ@(aG*G)G;7tMfmGaoYYtdx4a1xwsSIQ8no?7?hYzpg z#9GhU#^e+xi1ddQUmQF1q^Qg9ZWE%N@Dm7M^~_+a$uLqX7@P5cJ9cLS`lL0vBSBH) zrQTKHvq?~}>AC5bht%7&{)9R284Xm-*!4L?a^(}v&qPq&*#!F~ZJl~9p9iYw8UC;a z2W=>Qj?7^Ou_^AlS#B>&u4z~5tNI`1hHV%!@h{)=UGwS~-8Y*s9S*{D z|B2%ETI&0QuiASVpxl-Edpcgzexb95v#!GMputS=_^Kg_e9ChU-b*RqkW7>fD`8@$ zA#Xs|?n;n9RN;kWb+3gJF@x`w)blisk@4=pjdh=jzoIQhe}47s=Ml40t0W;o%+IZ%ltE+oIeJjJMylcwNK`TUq;np`oYYv~K-Kt{;TO$P`)X`$ z8)9_qgJJ8zbtRwhB2ItJPhmWayy0`NY95y;@u%wW9sX`hdkgGr9|7F`!tBFwuBCCC z+|B=KavTd2NoEvhms~#8{wLf*p82L*kt7h@dDBZtNHVgG331AL+pj1nGTM``jfxMy z!#+DNMEh2#G~8%NXm6>Qu6MRCx9V@Jf{C3I$svDcy8PRa$#|v!D88di{^Wxo&GCZ9 zO{ruKWVL3JVkJ+#T&pvqpok#|swHpWCLVVI|F1&YffrIY5c43LZh7DogbGH( zdz+R~A6l@9EG0l=Ke;eQ`X7_yNeLXpGD|ESa@_6dvB-C71U;y#Oy91Ll(1f;z{M9! zKlCQjaRZj_?WXr!Zzgi47kn97NwM3Qc|a7md~v%{pXj>)ued&GFZ`G5gv_}Hf@xp;@MDU~b=PmE+dc%qkl!9d=Ofd;_8JpaJC>(- zGoNRuK(JT<#iV|Lj=)+p> z)wCL#_BqGw_y08*R*wHeLMxZUy6w(mxO}Q7eMz?yhlu<4s-INbfzDUZDTo zxwdXCr0DDOK||dPA0cD+C)*PJhIMUZzFk{|IjN6;YlvvGQkPuTEx$na9eRh)>x2!=4!tHF zZ?o+yQ@43+BCa+6=WMZ>4OD~lN`~~uc?0wY5p>wbTB8eWHtrKv+r~N!p`+4bxhLwz zdMm0Q;E*#cmF8Ien>BVQ_iTiu0D>4TI2tieWU9{t7o~`2RI<318lPreulf^-1=9sv z;G7T#H+}!bJh(#tl}kE})uQ2%(W`Ij5u$V*^#+eM+xc&K)o!?L!xgkTh61OMsg!@pWa!2GJ0otE;$;Jt~It{n~xt-wxx4>h9Cy2}U- z#n#T%AL9!&&%PdLb?#2%zK7VL)-(h0r(wW8O*9ssdRnNhS0WRaXij}F)PR+a;}%IS zRO(-jJ_tn;7P*1eMiv#Bo=i^XMra;EPY@WZ9f+h$tgS1Q_(_2hqXb(XAdK+m0-2i@ z`LsdoQ{^hGSjpNlw1Pzg8Iw*lztfPXGyX9v{HZE~l$cw7v~WDp^r*66jv}2Ig2S9= z2hM!cn!yfV$^*d7t0H00*Ch{C>$Yz)ki{QyrO%UZ6R1(S4Y3HTFhEWmTHloz5x9ng ztja7VW;(1TlGEr&AwWd`Tm4SL&2K0p5k$jAmOak$PuR^5z^iwjR&P+KU~Mx{^Z96X=j z8n6`ngfXl^AybT?QB|GwLy6p*XGwa})pFv2Y_yLoCr+yUIm=k7$lTPc+9YE>Fi5!v zx(;k{f&mlTkv_5sg9i@O(2lW)SU#ABd8K8hPz`%U8|%y`>JPNsey#n=_DQ}XD`ly7 zMngUua%;I;{SHEgtB1|aZA14*K}Ot1gF&WldW4J0G$Fu;_BWG2%z`uKj-(v8sQ6PM zf%rN{mD-1%zP@s|fqvz7E%NZ+Ysns(FO`b`c<12o4aN_>S6%EW+_|uTq;%GszlChzNwuM*Z^C@i z1fCjx3w(c#1>+p`?~v^!tQ9$K<1KAP^Ceyj3ga_v^@`IIN!9^=FvX!nJRJRI5D`x)-VifFE2=p_jg>9Jrrf;9$Z5o0LR9L9x^s|)L0U2f!k$zZ3cjc<7y>h z=EL%bj>Iu01?a4Gs>0oM5psWQeEgrnL^QZ^Roun$kX(%tzXc>WjYFI_i#%{$O`U%c z+%C{3^uI)&M_hv0K_Li1(pYgQqOnUg+aco=BmI&xNj%N zI)8mu;pWNq<1FAv3xQ%LY9w2%U?PX6F33Gt8({LMFYTHzV?%LaV{~)G57;#()`YJ4 zrognym2Ps)VovfNW`qtS==n6-p#5gob<4Zn*~MaihFySrGDh3SGyg;SV#kjjH77uD zvR2B|XR*<_t>(yavsX+g;Q6r>~l$dX*Q0;3S(ip>pB z?jh#PU5tNl*yuzR;UMUNxp2X-AYh9|Wn0g%nAI;LEbj_0gTe2E5tv5O+LXoj-ipKd z>XR~wtWimk>+QU1^l%^inaDfT#W^BIGx@Ot@`oy2HVwj3wVk+%BrDUfVC$U1gG7cG zo0xeV_(3i()Ta@Fma0l2$8QLyk?gwe%3bh-4>8>mhh9mJK~7p`^4l1XNf*eT-XVp- z0B%(0o=e-Wfovue4>^))b5oO@SmTq80>c7 z`t-_1^`Pg8DPG{HqZwIesZ=VT2KlzK!iq-S1Xoq+s6`c4HjJ4Olk$Qasd^-qrsxAL z&Z@K<2`5FlN{%RBnj+fEoXG3(BDy~`rl&>$7JSJn*x(~nsTs4s5XN-(;wp@$7lbCq z4uw(#2PQzJDpGJP9<9a0ixY^YOx)7J5O#e~G__b1SS*f_9r^{A*op&^e?)R_d*Px> zaZLE`vj<(TmqO1f*?8(x9!s~4dYqo0fg@5%iq+}+>c$ruVe>9mi9qHkc56H$t5E#H zaj&!M7hloRxzT<@+TSmgugdkS!YPF!+mB1#9>r>O(8Wquujr|3BG$vdtUFc$=j?Qn zYHsRt*nqa3-^Z5oc1Aercq7(9BnaLc5FbkC&rqZ$xujW|g{+Li(fwVdXK7)RKO{2i zcfQgnAQxCp%fgxho`|IHRyjN+x{H^C&)lNwyihH4S+s9bZ|eg_Mk#7g|3c0{hw=Fi zOT+miqFV8J_;(IQJ|5>pG2e3N+Nmv*(TS8?&b@#SO;S)yJ8XF4xQ$PW<7%dP(vzbm zmki;>ay`g}bjT&(O|c~b>1w+exsE=%O0sWN6W92tJ!i){`rZ;nb?5phNT|yYs(*hb zP3!>mvApb){KJsrAz%9JQ01-8KO-d8uhU^32K>#tFYR!I*OP%$6Ir5gd#2vleoAoV z3qg`f6~zqA^aSh5MU6W$^H@j%~1pF&Fw%raNyNqED8*Z1iHrd_99R!1oy6%P~bFP1!*X{ zNY;Re#>%iTT-|U)dNd0HJvwjpPHw3TDWIpjxo`&Y@n;KcDTA&y!+vyTY_&v6z|WSm zjF0e#E8z1I@lZe#uI{tKb?uF7D;96NP z!=GU;Q83-PQC^XDswBijLLKgwQ_b76iDSOC!=sSPj-K>JaGF&C)zx4eq&(;B9Uq4+ zN_R(oweY7o^WS<28H@HHz3|si%r0V-W_`Cgh#hgK z`Vnf_^$Kk+lcEt66zh^RT~g`wBJR^=`d6oAxm*MD>X0Vie!neMz>~zH>k(lqjT=I6 zj<3=FTtjADj4fQF<^xx;(Hp^@XR)s%W=oufU67BT9|;pPWg}r-7lykU*^sTHBNopcdgV$pA@G~}o^v{)^xNG`7hri4;V?mu{^oRbu7SVo z5vma8#f7IbTT?+kT^Cd*ctzs*ePfKyiL4Pwi$F=-bxXXR)lD%yiD2Bv8V=>B{S3{` z6^iG;j2jw!8gZ;4;o*S<&etN=dMrvc|1y|NQRIS+cAYxF4K@U3=S3E|;e?N9Dr64) z0-|%#h11^7TL+d1cJ}tfPC6@Nh=QMRvKp0xF^2vD|E`!T6>dA?QYu$7!QqNH-$v!I}$DF3xE1*lgC@u<`6 z^tU#kuRgu?T`LvKVFv|=e224IDrAn%~<&x2#Lvkyvsr@RBM9?Y;FC4MhC6Ze4g@fJ1)sa;hIB*ueVmCV(&{Fg#RY(WzOG;2 z&dpddf2|YF=4-3E_}8Nr;t|X0#4Dlk1@~lfzd`WLcQcL>q6R+#_9>Y>2DGBB(`0m2 zKq;Vwn=IF<<+5J+vI$wFkcSf}qQ&hlw8hbM;>^CUw}UuuyH35ljwVod{Wv@amZmBr zdwY1=?CbXjA-N2;=zCb*R20}=UY;YkrE1-n<|>_X?tl)}9G|CPuM)pEQ*0X)P0kV- zkIV7nnLK^l%-|o9V zF`T;q!u%z7hvUR_XH0l_7w9lAs~s;`oHhtH>{Wo6;~VtRoVflFJ>QVHIAtpR@6LBE#5 zyrqQBP1r!c(80~zb>)oBNZ!}ui)8Lip)j4deZC4p{~MW zF&#j3aKGV$Ljv}rG>2#TtO>LCMMNofqt;vp2c%VZPj-Hlw7ZH-^wKjG6{i*g0}SIP zoW=K9{5G`d!@v3ad$hC{@VIY}FVU1l_mj4O$;-pemMSH1q@RVgmZg$J`PzRE;TKFr zeS%-;I*B4u#7w>AnKucc@^bQpNYB1l>x7zi1o3=WaeO#kL?s=15Iy)bO=Y)7VQ|vq zbNJZy`9ROyXd+-W#E&{#hy|qKpfE;daF-P+itv zpK4m8OwtEG)aT}qqe|c>K0r!yv7(Tv7I#ZuR<+6ub@AHLAUT6?kzJoHmz7Nb76-|| zCMG9t$KwTSZ~PW}oe<`l=)7wsHfU0btCnL$>Q3eILB9$jMY} zFGk`>Px!64Fc{aP2?aBOvDSh`%p67|(9y`p=)^5AVrUdj4df|0676o+63YUC+!o(} z;x92XH8d}NQGW|EZZV1&Yz}D2<|+&CiQVI4$-YZPIGoq``16mq$Dw9*SuRNN0*R=z z$`+^$q;MWKb6Gi=z9saCBsY0*>DWi!7!tA-UF$b>`<3tW-8w<9O}xg(XXFdXyfUie zPF8bW=;1|CWL&!aL8g72uztdw$%;LTi@`bqy6D){2Ww@jC1`3-WH?SOz_&_^D5Roz zd-U|~`)Jn5c139Xn*zEWQM@DOjYxkICeLRw_)fr$>Mv867_=K-5s}(m!8R z7obrsi9)Ms50?asFtmXv&<7WtB_boA!zUbt-P!IgGMY=|BiwDP?~OQs9X-hMV^%f! zj5FJ%w`Vxqvi0rKX+1i?Z3~La=>aQ5zL48w(Ca&=YP{+h!4fgm z454oGk6~NYt)4d1JR3VkZ5AZjlk4kA?(2N6QS;uXnnPNq-N)0hpf=IB<}<(1;aX}6 z5Q%S?Hsdh8fQy?rfb}j(kQ!YKX3k|qBr30TX4iLb6uX8kzm$(h{P_HAD&a}-yUq~k zllQ$T?qQQDS4c$M@Ww{Ahs()2+})EGvtfU}(5_C8siqDVUd(%2;-U~|RVU74aiGsp z2yOWKF6l503Nu+D3#6-kjObh6;!t+E4>6EenLj?;i=<2E{dzrw0^Dr=MeFVKzq~Hh zrU%c62?ZfYGBUOvjS7no`?DE>tx9S9NGQ1fp=e-f$Q@7)N5SH7>(s$*j`LyT7fJ5V zFkCH&u|vmcDfs$s8yv>UDdV6eu_&`bhz#qozSF?i6%-oE>vqW-W?r#mf+c;Bm6lVGLRdiU7JLh|? z6^x#YnnbHk0NF}92{kIm7}bGHV` z$hbPPvB|5Mu|pY#7x4r0+@JPC*<^-&@D_Mbk+ZVVSMuuj10HFSsRl8ApCb~#sN)~#8gHft!r;v@xjy8?$U+o|unoyb2oJ{YxicR}jh53P zB62=l&%-J{okb{~kkSX38GL6yBa>EJFkzFd)UQaMBSF3sGyC8{QO0y5G{cXeirUgF ziRx>778q_IwaoQ07fgp@F!el+&B_X7m&M4{Xm~1Bi#sZULR1cVlql!XA0@X>@g3E(p<;KiFsIQ<-s&cYHn zz;@l}GFNpu-GIRmR;2uQY!NfRdNThB;9(YYpL25oE3z@p?our42Oyxn?_P=GnpDd(Ehw|NHAA04dyt!=eMB}C?3~zMbJi2_4 zU&dLiP(N%clR;Hv>nyUwO{l41Y@*CUYo*!uR8je^!&w^H&2g;>Fu+fdCLrYU5Q`co zIJ9w+WKgruy%hY3CAzvy&TcE0poda49XpXo=KLGU22lp(k_-FzrEsA*6OfFr2)goX z7{g|yDHsz57oZ-{os+|okj3K|*`_&iuQ?oZcu(^?u~1Gt-eI*Kol!tgD$8NMgE{-+ z9bCulB0Ahzu-|g~W8iql@sT?W!vy}+;eznaz~^Dr$A|OUS7092=Bgn!{bk$lNkF8Z zRJr>HK8h!Zi=TJAN74Ydy2$PesaX+O_{h1;oeGDbr0Tu;>oT}aL^L5QN_UM;O(UVb z6xlfM1JKEAe`a=nfdap*k$Smdn{K@gs!7HPK^(LRtqICDP7kwcTnc3f<(piRaJMrj z-TtAnR;MxU6JdnPhs=T6uWmv;s<)sHfniIe6XneGOvs!78rJ{LCo|A|WZ>147X&H+ z&jnc6-^fpcPd|iIkModQ{GmtMr8RlN3PjHhrG7%BFcgh6B5q~=&TlB0eeXdbyt$vI zc#IWcxn%ijvfV_vTx&dlm9LKAw4AGKK)|$B-S)|dh;)3|zfWYLM7iW#JvHc+)+YKG z4>#Q}>b4W#yC7q@7^7`SG-ab!My0))hzOU_@#6V>zTZAsNltK0vI^-54g#zqH&De9 z=MNGG%epXVYdK{PnZ+`jd{2^t|1K@BquE==dP}27d$pxeHX%-5MT14pwpI@6vh0_A#$Rl~kk#ea3zjwkp$C zg~Lyyc^Ppute{``7pI2u5Ryjw;%=>8$vuAwyZZ;Nr4M3HP<~6?r8tw9I+B^mBa9N| zg3Gke4dnb5J0t0}q?v!GfA(RBb_6RF$+8$LIUJog9^07T-j3trlv#Sh_oV+SXO(74f=c zjW82502bOekMATMyP4gQnP&2cB(n z*VO7EYOzp{KAu^2Md! z(^ovv_Fn*u5G*geQ_+TbG3j~n;*(mavxgspq;XytIbr>YP%Z86ME>((fckL@+H)%G z`x(#dj@ilgo-NHd={tjxSh}mF4g!iuYzWC$7FGc$Vi5T2!0@?Z#eG*QWs97>8^8Mo}8UA4qlW=lyz_H+V^O$g}F*2O4 zK)j&sfwc8IsU%!U2XBA{e~$QA8Tdr%C*M#cPC(hd1XaS_S=5>yJh;k8-tyuyv=#Vw z7|dW+JfEV$qjENoRWD$xC5*i3aVN4=$wY)g3=4wX`lMydF%H=DH7r~ zpB$e%p)uJ#&$rVa{VyL+wc7jy-R~37u_>QFKaPK}&tkBmH7UgrrkN4TW+A+4oK?8F zXEb}G5Io2?=3F8jZHL0KXMG_apc04G>IQ!Svk|qN1 zT-bS_@Qn_e1Y3)^rWfGp?FmxxCw}n^KaCmETh%uEMmKSH(#7wF9b@~~KyFvH2vf--j$RX9gPxVXrKZKAW*}ts^-j{(+CK`|*w_!lyv~-ZuVD&)H9-dWZJo?D zFit-BN#0Yy-`>HBN{!(uAH>8>gcomfu0pR$({pg9gS%a`f2-no?!j8pZ#3LEY1Bt% zRiRQHPL7&Qw`ilacEZwXptfngC}gvZ?8*zmYvaK@fQ)6+Dg0|ShPTm}346UyHdmep zlhvG4Y_YOO_!VoH(84wVZOid#HTXlkf8|zJjF$n4-MP{wuHG{>}*ZzSi z+Puu;x&q=St)b)#Dk}UB=?XVI)2%$2`RO7RUpuWX$utL<W(!M0$;I)E)R8H4AA!>wXs6t*-*HC`m}E(nU86rD45gb7^L=AJtrqK7AGMq( zIBXopeQE0H^&ENVL=JZq?p(&pqxewE-dkyYkJV%4D5sOS=6Jfb*wn-(eLx}(zCCOo zU#q_V!Om@*O=X~Uild_8L;i3FHT2vl>6+YG!aKJw+6DfbpiaXU|FdjZ-WFO;vY(E40tcB`(0_ zvt*^+nERq?;^ugId#=(2v!jy_+olDLgrwkyN=SIREPJIv8ct_1Ai-jqI+T;NHj{QJ z#t->+;WgoDa*GP(Dp?PV?ocFf7vw;vagDgM=kkNsx*-31ScS~g z6`^4mg4`QUd{AHhmSEtCz5dr>_&)Hj zlE$p3AWTnqq=_fPGc`G-JeIM`^=BX)bljgTI49E2Li*{O(izQ{YoVMa@+t^Vye`+Wtj*4vXZEqZ;3EOb#fkyz_RhbUCD$L$fRi1Oo#{d>~oc)FxzuY5D{vu`4u7{^u zd{Js|@|GTHNO+w#3rtc=P7|g`;M_~xnayD>vAEh6NxETn_hFZ!*u}p}t3Q|D711K{T!|7&Fcg=(CP&P68=Oo$9}vn^`la!U`exaZ)J(LP|nEcTS#=a zQ2i=0`naNUKqR^%=-bz!C#;1Qy!{hvNu>t$6Gok0+ z5Cb}{=Haj_POFHrvC+kUA{Z*K(CK%{{7~-N)R_s*ognTu1mG4=rbkGSEe|>{UT)o< zF2l~$iMIPE5(~Uxe6*rWZ9rpilikbDzDFIr+u(q0w$#>C>0uX-DPbU=WqcL@|3g;b z1PGwPAz}MigUt6VoYssx!EvOE7rU2#Eu59@umF+ZJ^calDfz&UZoq-0A(fb!<(X_I zk1a^aC4N(UwRJqqS96={oXw47Tq1-Y9$2=dvK(<>rAh19HpzZ?bQdhIOqrnuutxSh z&$}B^HBg1yJoQrMR5UnZS}_O>_CDa)*b=e9NX8JD4zw82B}sQE zk~aa8M!ak=GZJ*uVn-hg=SpvDb{n3Ljjkd`PvnR9M7ppiv&GIy*w`|Wm&$47LDF3O z-#!pM9G*09Dv`j$_h^)F?v}ppYq|<}Tah|AMDcjNXC)0VQJnCJgh@q6O{*Co%|xvA zc6`3}K@a70-8=^=6X-(3-wybiNa^I^DRj!~Ut=VMNR_CNMvBvn7a`GsNx3X`r(ml2 zu3hZTmYv^}?9CEW9x>_NNlXviJVS&+@+3CFWOFsil}6?Pj4abs<7Not zK~YFz`HqywTm+`odGFBokOh0NfKHwPQ8&7o)Y@B#jf#WIwd0>#BFre zV=v2wm$f#ojA5*w`xz+}xd{1beCMLXT|Os5{_JGG`WBWA0+1AaLt_T7x2OAI z`?FP#Qbq9mp8G*B)_%e*;wI(6O%Y3*GI4&mzVI~Kw;IIpjh~7#K3!-_l$vG1?dgu% zGS{(_1;P~DjfI_>8YQKBe1#g$)?Cli+)Sqk(o2lm6eZ;}cQ0LQ#FZK1ec}yu%e5-% ziK)Kc?2?`czMDgS)M*|CR3yJ6N*dsd2_ZJsCLY8W4#A)31gtr3SFS}GS$eBg=%Ump zGKqy=F78vKk3NDjV?^~2lI>>Wa|T}oa)M!eE)kv)iC82VCpzborib0<=eFZ}V|jZ%byCrsc!2|{hEe&7Ct4Hdkp{3^4w%i0 zOuJVqK5)U;blP4a7}&0NEOu*^wU~k1zlJ^otzOJFH90-QhCai`bHD5htZ7&|l6Y=% zVw)e&mx>YlaBLzIUn|#JARIa#8NduQRA0uA+R3na;g51{)_M%B%4OdpyGPmY^z+Z^ z8^-c@G>XfIuFC~8Za|% zfAlA7Ix=1gOYzQNd9dD%q)O zy*56H?e!6~q#-eElnFlZbzED25Zp-o^nhn6sokwSulVPW_F^? zsoZF8a>|;kogi^C*BM$qH4W?6liqZWWnMav)O(@4tddfLOqB_ZR@8sqE|19-M?VB1 zxkZ*>rOI>gcx0kHAxFj(fJoXWo*i{Opn?$~-Xg`^OP%h}vbvrt{3v51{PZz4IYw5l zW9@LE=MGn73&?j%`eTFg(QlI;o{AxEV~v_oueSljDPHfUe2Fx;g7OMB$R`2k5`UAGLr+U@;21; zVGhaFqL=kuV?HM?E}6u?z#YTorG{ozEoVzDu)!e!reC|JVUda=hw)upgVN=rNx?xj zss9ywG%j$lt;5A3W(_5bX^e&6o9&NmIDac6+nUO0MalwqH9FdT#;hLU2%v@yLnkh#gud6z1R z$G-e#b=WEl2~HD(%M$HDwyqM*PaAz?H47t<7)hX}=kR31%B5bt8c3;Uf;?q${kB^Y z5{Jw^l!ho~Cs<>hScu7^KBuP2ejyD~Q=&VQR^r!SstBJUkH0Hi@Ch5HHX$8chqeTP z)4kZk`i!^q1$1#f2M_1@y?i6rRTLK^<=}?5jAxxK5XV1Mw14aB=ju4b@?A{Q$UyXC z!=5BMMV5p&mmXHkE(*7SH-813uT|Z+)@zz6Vfnfx-#rphC-w6bQvIHZ`+cddHA`5jj#ei_K)yDj1-ZBYEI>LV&RMSwh zg=Zxmt+kY`$BYko`8$xc_DTspsulO2*x}A&skU);hL|!Q8%^RdDs&{5Q z?o3dv0PsCT=-??AG{6y16X!^hYRP_Z{cAjIka^euZL$-lUt*%Pw2s|Wu9oZ2K?cdb z7Qit{L(2AAdH16E`=~p<<~w+wCABX}Mxi43o2r%=2@gljCt_v4De8h2?T6T>>`chV zaK3VhjC3MBdST<*@)b{Xvny?Vrq-eCkFPH1g(_4z)HR_c(t!Vit#^!)&wtDdZgCnD~R8$el0^dkwFT37?Z zf&Ax^cMMU}Q)ioWWMsQ!wsGXJ2@4a0$lhYNSF4})Z=G5_{pNUDBB2S%A_^bRL8l6UPVISSOn*enbEieIhsMpgL$ zOdYfUEo0YWV0AU@L%cN#eIvuv%FuA!eQ_Dxz9?C;GxbG@Z}=9P;2j5x6&4oJMy`AA z*(G~suYYxpI8WS`2l^R{5p*h|4mWrHhROJP7EakGND5Ad%_k;qG&^zIca6 z%c<`T#l@z1N=Ob+vZTZl9M!eVIA$6Yk5q=*Ry5Mee+fvK#4sQtBgmov0p1cRGQsbe zO?f_KNWeTF3BP_)mbAO+0b;UN$~|Ixw2L_$1D3v1MV!c7&}Ba(nI)s<#z&N*0U2vU zISyc1hQptji;C=Zw3wrav|^M|LY!x4p_}sSPQ*SpDXVeRdw*Dv<5mCw0CIpKsp@gQ zKpD+-0&fF}MByA5qR+~yJ17?rt_j+ExsqbQ@qwsfrC70&z3vJ3#F8tDmP1;?n@kAM z!=1t8?BMg@4%gR+(Lbsw~AZ!YLgvGn0up+B@)S=E3rf!H@i9dI-&dvF$j4I;44y3d;*{cQ<)sk<8 z#5$uWU&mgE$fKR<2#1yi6l>bymmFm17)f3!4#hnC6X$j(@36&f25Y@xh^C5oCA<@= zvRsXFZ;ZE*v5H{aw^0SVi&)U6M_tJWQ`}SW{$@xb%C?e58+J^<7x9j}BBJ1#tT=Qg zCG+dr-Bld0iX+sjY*AyfA6+w$Kz0R`aNS$&9he#B-+hDPOmw$I6MK_h^^A3=767Jx zth7G=2uwDb^{S(g$>xcUdy*|=0@qL9PebH1FR%SJuA|~?QuS$Hb%e97Wm;OzSw?4; zQe5q}e&rG8gUV5HBys;f@E(oqw9o_kFmyv1(s8Z~xNCp#o@L|28FM9k2M!o>BSR;> zP96b{_dkBs!A+44KXI}crQf4H*0`%Esutft>^)^NR_3V0iSSr2#4`4OAdJ^fu+a|5 zVr5>y&+x}paNIdNE@Htx&`#|tNCe~~dGIZ&AUKi)G9ZXgjzpA9|iyxrTm@WV9FLo%a0_agOKE?gTXKJ;>iNN(G2t&DK8F_{hrN z?^11jZC{t}U6!GkzHaYoE^fc%NW3Pn>T0GtmWkPmNV67N(RbC?tsu}vU3YGj|63n9 zJAhjtxIV8;=-3u`i_D9Ejp{x9Y~&O&8C{_gAMz>Gircq5HmYEK=|@l}pQGuHinp{S zb}Ysjx?ev2lzq*7)*x0SFIUcYC?sMx@{V#)s!DwWOD1@n=4__RVuX`8Q_&&q79ZQz z0Wd|qj*8DTh!NRk75XxwQysqDEB&KZU!UTNLXxj~1{8b!A@CyP)Oax*Nl#IHQ*dy<#!>TbnA3UPQihU<>1X)ScsUxcrlX{ujd z(asEelLWRAjTU@eyhBf7=n5&dSo_99_X*$8HIts%x9zv{E5nCsck=F~rZy*`Lq4R( zzZ8KsUosa2-@@S$*;PJ6ucf!HS^EX=kfdTA5^oa03FTTDkbk80p&NUjWxUe-TjLkAKpfOM>weu0L>j=Ej6pxO(aR~g4HUi* zL@u0czS@Y~jeN8bto*a)BEwFKd{7&eowS{%uAP{3I0fYo8%FvMR#8hgHgDrf~&WQ;bCmrGDlTL1FJ}1 zHot5w(6x6C1n-iu3F$S#qh$hGqtPW4kad0&pvT$J`+Q**y?Nn_?(oCg2cx5;KVWZp zMA2MI)r+qZcdQt_p=Sx#Co9~iW;&p zTWlixM(3E_sOFJr%t8f20*|ZWaC@idNCcDxO0c3JWf;#2L}bҩ$mgxy)sP%O?sWVj3clwk}O|h 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Ⓢ_mj)3DEI5hqmVzp0F9Fr3HX7Jv{dIBy;4;8 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 PH}Yq!Z?0FZb1U{ABW_Xy literal 0 HcmV?d00001 diff --git a/docs/querying/querying.md b/docs/querying/querying.md index 5a3e99a31755..82d91a763f12 100644 --- a/docs/querying/querying.md +++ b/docs/querying/querying.md @@ -35,6 +35,13 @@ posted like this: curl -X POST ':/druid/v2/?pretty' -H 'Content-Type:application/json' -H 'Accept:application/json' -d @ ``` +> Replace `:` with the appropriate address and port for your system. For example, if running the quickstart configuration, replace `:` with localhost:8888. + +You can also enter them directly in the Druid console's Query view. Simply pasting a native query into the console switches the editor into JSON mode. + +![Native query](../assets/native-queries-01.png "Native query") + + Druid's native query language is JSON over HTTP, although many members of the community have contributed different [client libraries](/libraries.html) in other languages to query Druid. @@ -44,7 +51,7 @@ The Content-Type/Accept Headers can also take 'application/x-jackson-smile'. curl -X POST ':/druid/v2/?pretty' -H 'Content-Type:application/json' -H 'Accept:application/x-jackson-smile' -d @ ``` -Note: If Accept header is not provided, it defaults to value of 'Content-Type' header. +> If the Accept header is not provided, it defaults to the value of 'Content-Type' header. Druid's native query is relatively low level, mapping closely to how computations are performed internally. Druid queries are designed to be lightweight and complete very quickly. This means that for more complex analysis, or to build diff --git a/docs/tutorials/tutorial-query.md b/docs/tutorials/tutorial-query.md index 4b19c92b89b3..19461ac82191 100644 --- a/docs/tutorials/tutorial-query.md +++ b/docs/tutorials/tutorial-query.md @@ -24,56 +24,164 @@ sidebar_label: "Querying data" --> -This tutorial will demonstrate how to query data in Apache Druid, with examples for Druid SQL and Druid's native query format. +This tutorial demonstrates how to query data in Apache Druid using SQL. -The tutorial assumes that you've already completed one of the 4 ingestion tutorials, as we will be querying the sample Wikipedia edits data. +It assumes that you've completed the [Quickstart](../tutorials/index.md) +or one of the following tutorials, since we'll query datasources that you would have created +by following one of them: * [Tutorial: Loading a file](../tutorials/tutorial-batch.md) * [Tutorial: Loading stream data from Kafka](../tutorials/tutorial-kafka.md) * [Tutorial: Loading a file using Hadoop](../tutorials/tutorial-batch-hadoop.md) -Druid queries are sent over HTTP. -The Druid console includes a view to issue queries to Druid and nicely format the results. +There are various ways to run Druid SQL queries: from the Druid console, using a command line utility +and by posting the query by HTTP. We'll look at each of these. -## Druid SQL queries -Druid supports a dialect of SQL for querying. +## Query SQL from the Druid console -This query retrieves the 10 Wikipedia pages with the most page edits on 2015-09-12. +The Druid console includes a view that makes it easier to build and test queries, and +view their results. -```sql -SELECT page, COUNT(*) AS Edits -FROM wikipedia -WHERE TIMESTAMP '2015-09-12 00:00:00' <= "__time" AND "__time" < TIMESTAMP '2015-09-13 00:00:00' -GROUP BY page -ORDER BY Edits DESC -LIMIT 10 -``` +1. Start up the Druid cluster, if it's not already running, and open the Druid console in your web +browser. + +2. Click **Query** from the header to open the Query view: + + ![Query view](../assets/tutorial-query-01.png "Query view") + + You can always write queries directly in the edit pane, but the Query view also provides + facilities to help you construct SQL queries, which we will use to generate a starter query. + +3. Expand the wikipedia datasource tree in the left pane. We'll +create a query for the page dimension. + +4. Click `page` and then **Show:page** from the menu: + + ![Query select page](../assets/tutorial-query-02.png "Query select page") + + A SELECT query appears in the query edit pane and immediately runs. However, in this case, the query + returns no data, since by default the query filters for data from the last day, while our data is considerably + older than that. Let's remove the filter. + +5. In the datasource tree, click `__time` and **Remove Filter**. + + ![Clear WHERE filter](../assets/tutorial-query-03.png "Clear WHERE filter") + +6. Click **Run** to run the query. + + You should now see two columns of data, a page name and the count: + + ![Query results](../assets/tutorial-query-04.png "Query results") + + Notice that the results are limited in the console to about a hundred, by default, due to the **Smart query limit** + feature. This helps users avoid inadvertently running queries that return an excessive amount of data, possibly + overwhelming their system. + +7. Let's edit the query directly and take a look at a few more query building features in the editor. + Click in the query edit pane and make the following changes: + + 1. Add a line after the first column, `"page"` and Start typing the name of a new column, `"countryName"`. Notice that the autocomplete menu suggests column names, functions, keywords, and more. Choose "countryName" and +add the new column to the GROUP BY clause as well, either by name or by reference to its position, `2`. + + 2. For readability, replace `Count` column name with `Edits`, since the `COUNT()` function actually +returns the number of edits for the page. Make the same column name change in the ORDER BY clause as well. + + The `COUNT()` function is one of many functions available for use in Druid SQL queries. You can mouse over a function name + in the autocomplete menu to see a brief description of a function. Also, you can find more information in the Druid + documentation; for example, the `COUNT()` function is documented in + [Aggregation functions](../querying/sql.md#aggregation-functions). + + The query should now be: + + ```sql + SELECT + "page", + "countryName", + COUNT(*) AS "Edits" + FROM "wikipedia" + GROUP BY 1, 2 + ORDER BY "Edits" DESC + ``` + + When you run the query again, notice that we're getting the new dimension,`countryName`, but for most of the rows, its value + is null. Let's + show only rows with a `countryName` value. + +8. Click the countryName dimension in the left pane and choose the first filtering option. It's not exactly what we want, but +we'll edit it by hand. The new WHERE clause should appear in your query. + +8. Modify the WHERE clause to exclude results that do not have a value for countryName: + + ```sql + WHERE "countryName" IS NOT NULL + ``` + Run the query again. You should now see the top edits by country: + + ![Finished query](../assets/tutorial-query-035.png "Finished query") -Let's look at the different ways to issue this query. +9. Under the covers, every Druid SQL query is translated into a query in the JSON-based _Druid native query_ format before it runs + on data nodes. You can view the native query for this query by clicking `...` and **Explain SQL Query**. -### Query SQL via the console + While you can use Druid SQL for most purposes, familiarity with native query is useful for composing complex queries and for troubleshooting +performance issues. For more information, see [Native queries](../querying/querying.md). -You can issue the above query from the console. + ![Explain query](../assets/tutorial-query-06.png "Explain query") -![Query autocomplete](../assets/tutorial-query-01.png "Query autocomplete") + > Another way to view the explain plan is by adding EXPLAIN PLAN FOR to the front of your query, as follows: + > + >```sql + >EXPLAIN PLAN FOR + >SELECT + > "page", + > "countryName", + > COUNT(*) AS "Edits" + >FROM "wikipedia" + >WHERE "countryName" IS NOT NULL + >GROUP BY 1, 2 + >ORDER BY "Edits" DESC + >``` + >This is particularly useful when running queries + from the command line or over HTTP. -The console query view provides autocomplete functionality with inline documentation. -![Query options](../assets/tutorial-query-02.png "Query options") +9. Finally, click `...` and **Edit context** to see how you can add additional parameters controlling the execution of the query execution. In the field, enter query context options as JSON key-value pairs, as described in [Context flags](../querying/query-context.md). -You can also configure extra [context flags](../querying/query-context.md) to be sent with the query from the `...` options menu. +That's it! We've built a simple query using some of the query builder features built into the Druid Console. The following +sections provide a few more example queries you can try. Also, see [Other ways to invoke SQL queries](#other-ways-to-invoke-sql-queries) to learn how +to run Druid SQL from the command line or over HTTP. -Note that the console will (by default) wrap your SQL queries in a limit where appropriate so that queries such as `SELECT * FROM wikipedia` can complete. -You can turn off this behavior from the `Smart query limit` toggle. +## More Druid SQL examples -![Query actions](../assets/tutorial-query-03.png "Query actions") +Here is a collection of queries to try out: + +### Query over time + +```sql +SELECT FLOOR(__time to HOUR) AS HourTime, SUM(deleted) AS LinesDeleted +FROM wikipedia WHERE "__time" BETWEEN TIMESTAMP '2015-09-12 00:00:00' AND TIMESTAMP '2015-09-13 00:00:00' +GROUP BY 1 +``` + +![Query example](../assets/tutorial-query-07.png "Query example") + +### General group by + +```sql +SELECT channel, page, SUM(added) +FROM wikipedia WHERE "__time" BETWEEN TIMESTAMP '2015-09-12 00:00:00' AND TIMESTAMP '2015-09-13 00:00:00' +GROUP BY channel, page +ORDER BY SUM(added) DESC +``` -The query view provides contextual actions that can write and modify the query for you. +![Query example](../assets/tutorial-query-08.png "Query example") + + +## Other ways to invoke SQL queries ### Query SQL via dsql -For convenience, the Druid package includes a SQL command-line client, located at `bin/dsql` from the Druid package root. +For convenience, the Druid package includes a SQL command-line client, located at `bin/dsql` in the Druid package root. Let's now run `bin/dsql`; you should see the following prompt: @@ -107,7 +215,8 @@ Retrieved 10 rows in 0.06s. ### Query SQL over HTTP -The SQL queries are submitted as JSON over HTTP. + +You can submit queries directly to the Druid Broker over HTTP. The tutorial package includes an example file that contains the SQL query shown above at `quickstart/tutorial/wikipedia-top-pages-sql.json`. Let's submit that query to the Druid Broker: @@ -162,150 +271,8 @@ The following results should be returned: ] ``` -### More Druid SQL examples - -Here is a collection of queries to try out: - -#### Query over time - -```sql -SELECT FLOOR(__time to HOUR) AS HourTime, SUM(deleted) AS LinesDeleted -FROM wikipedia WHERE "__time" BETWEEN TIMESTAMP '2015-09-12 00:00:00' AND TIMESTAMP '2015-09-13 00:00:00' -GROUP BY 1 -``` - -![Query example](../assets/tutorial-query-03.png "Query example") - -#### General group by - -```sql -SELECT channel, page, SUM(added) -FROM wikipedia WHERE "__time" BETWEEN TIMESTAMP '2015-09-12 00:00:00' AND TIMESTAMP '2015-09-13 00:00:00' -GROUP BY channel, page -ORDER BY SUM(added) DESC -``` - -![Query example](../assets/tutorial-query-04.png "Query example") - -#### Select raw data - -```sql -SELECT user, page -FROM wikipedia WHERE "__time" BETWEEN TIMESTAMP '2015-09-12 02:00:00' AND TIMESTAMP '2015-09-12 03:00:00' -LIMIT 5 -``` - -![Query example](../assets/tutorial-query-05.png "Query example") - -### Explain query plan - -Druid SQL has the ability to explain the query plan for a given query. -In the console this functionality is accessible from the `...` button. - -![Explain query](../assets/tutorial-query-06.png "Explain query") - -If you are querying in other ways you can get the plan by prepending `EXPLAIN PLAN FOR ` to a Druid SQL query. - -Using a query from an example above: - -`EXPLAIN PLAN FOR SELECT page, COUNT(*) AS Edits FROM wikipedia WHERE "__time" BETWEEN TIMESTAMP '2015-09-12 00:00:00' AND TIMESTAMP '2015-09-13 00:00:00' GROUP BY page ORDER BY Edits DESC LIMIT 10;` - -```bash -dsql> EXPLAIN PLAN FOR SELECT page, COUNT(*) AS Edits FROM wikipedia WHERE "__time" BETWEEN TIMESTAMP '2015-09-12 00:00:00' AND TIMESTAMP '2015-09-13 00:00:00' GROUP BY page ORDER BY Edits DESC LIMIT 10; -┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ PLAN │ -├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ -│ DruidQueryRel(query=[{"queryType":"topN","dataSource":{"type":"table","name":"wikipedia"},"virtualColumns":[],"dimension":{"type":"default","dimension":"page","outputName":"d0","outputType":"STRING"},"metric":{"type":"numeric","metric":"a0"},"threshold":10,"intervals":{"type":"intervals","intervals":["2015-09-12T00:00:00.000Z/2015-09-13T00:00:00.001Z"]},"filter":null,"granularity":{"type":"all"},"aggregations":[{"type":"count","name":"a0"}],"postAggregations":[],"context":{},"descending":false}], signature=[{d0:STRING, a0:LONG}]) │ -└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ -Retrieved 1 row in 0.03s. -``` - - -## Native JSON queries - -Druid's native query format is expressed in JSON. - -### Native query via the console - -You can issue native Druid queries from the console's Query view. - -Here is a query that retrieves the 10 Wikipedia pages with the most page edits on 2015-09-12. - -```json -{ - "queryType" : "topN", - "dataSource" : "wikipedia", - "intervals" : ["2015-09-12/2015-09-13"], - "granularity" : "all", - "dimension" : "page", - "metric" : "count", - "threshold" : 10, - "aggregations" : [ - { - "type" : "count", - "name" : "count" - } - ] -} -``` - -Simply paste it into the console to switch the editor into JSON mode. - -![Native query](../assets/tutorial-query-07.png "Native query") - - -### Native queries over HTTP - -We have included a sample native TopN query under `quickstart/tutorial/wikipedia-top-pages.json`: - -Let's submit this query to Druid: - -```bash -curl -X 'POST' -H 'Content-Type:application/json' -d @quickstart/tutorial/wikipedia-top-pages.json http://localhost:8888/druid/v2?pretty -``` - -You should see the following query results: - -```json -[ { - "timestamp" : "2015-09-12T00:46:58.771Z", - "result" : [ { - "count" : 33, - "page" : "Wikipedia:Vandalismusmeldung" - }, { - "count" : 28, - "page" : "User:Cyde/List of candidates for speedy deletion/Subpage" - }, { - "count" : 27, - "page" : "Jeremy Corbyn" - }, { - "count" : 21, - "page" : "Wikipedia:Administrators' noticeboard/Incidents" - }, { - "count" : 20, - "page" : "Flavia Pennetta" - }, { - "count" : 18, - "page" : "Total Drama Presents: The Ridonculous Race" - }, { - "count" : 18, - "page" : "User talk:Dudeperson176123" - }, { - "count" : 18, - "page" : "Wikipédia:Le Bistro/12 septembre 2015" - }, { - "count" : 17, - "page" : "Wikipedia:In the news/Candidates" - }, { - "count" : 17, - "page" : "Wikipedia:Requests for page protection" - } ] -} ] -``` - - ## Further reading -The [Queries documentation](../querying/querying.md) has more information on Druid's native JSON queries. +See the [Druid SQL documentation](../querying/sql.md) for more information on using Druid SQL queries. -The [Druid SQL documentation](../querying/sql.md) has more information on using Druid SQL queries. +See the [Queries documentation](../querying/querying.md) for more information on Druid native queries. From 2e1aa6ff997a3e832d8a10757fc6f3a50c1269fd Mon Sep 17 00:00:00 2001 From: Suneet Saldanha <44787917+suneet-s@users.noreply.github.com> Date: Fri, 29 May 2020 15:03:35 -0700 Subject: [PATCH 042/107] Refactor JoinFilterAnalyzer - part 2 (#9929) * Refactor JoinFilterAnalyzer This patch attempts to make it easier to follow the join filter analysis code with the hope of making it easier to add rewrite optimizations in the future. To keep the patch small and easy to review, this is the first of at least 2 patches that are planned. This patch adds a builder to the Pre-Analysis, so that it is easier to instantiate the preAnalysis. It also moves some of the filter normalization code out to Fitlers with associated tests. * fix tests * Refactor JoinFilterAnalyzer - part 2 This change introduces the following components: * RhsRewriteCandidates - a wrapper for a list of candidates and associated functions to operate on the set of candidates. * JoinableClauses - a wrapper for the list of JoinableClause that represent a join condition and the associated functions to operate on the clauses. * Equiconditions - a wrapper representing the equiconditions that are used in the join condition. And associated test changes. This refactoring surfaced 2 bugs: - Missing equals and hashcode implementation for RhsRewriteCandidate, thus allowing potential duplicates in the rhs rewrite candidates - Missing Filter#supportsRequiredColumnRewrite check in analyzeJoinFilterClause, which could result in UnsupportedOperationException being thrown by the filter * fix compile error * remove unused class --- .../benchmark/JoinAndLookupBenchmark.java | 9 +- .../apache/druid/segment/filter/Filters.java | 9 + .../apache/druid/segment/join/Joinables.java | 48 +--- .../segment/join/filter/Equiconditions.java | 60 ++++ .../join/filter/JoinFilterAnalyzer.java | 260 ++---------------- .../JoinFilterColumnCorrelationAnalysis.java | 32 ++- .../join/filter/JoinFilterPreAnalysis.java | 27 +- .../segment/join/filter/JoinableClauses.java | 136 +++++++++ .../filter/rewrite/RhsRewriteCandidate.java | 101 +++++++ .../filter/rewrite/RhsRewriteCandidates.java | 157 +++++++++++ ...BaseHashJoinSegmentStorageAdapterTest.java | 3 +- .../HashJoinSegmentStorageAdapterTest.java | 71 ++--- .../segment/join/HashJoinSegmentTest.java | 5 +- .../segment/join/JoinFilterAnalyzerTest.java | 21 +- ...inFilterColumnCorrelationAnalysisTest.java | 32 +++ .../filter/rewrite/RhsRewriteCandateTest.java | 32 +++ .../rewrite/RhsRewriteCandidatesTest.java | 32 +++ 17 files changed, 687 insertions(+), 348 deletions(-) create mode 100644 processing/src/main/java/org/apache/druid/segment/join/filter/Equiconditions.java create mode 100644 processing/src/main/java/org/apache/druid/segment/join/filter/JoinableClauses.java create mode 100644 processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidate.java create mode 100644 processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidates.java create mode 100644 processing/src/test/java/org/apache/druid/segment/join/filter/JoinFilterColumnCorrelationAnalysisTest.java create mode 100644 processing/src/test/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandateTest.java create mode 100644 processing/src/test/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidatesTest.java diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java index 9098953fbe97..cb630a2e400b 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java @@ -50,6 +50,7 @@ import org.apache.druid.segment.join.JoinableClause; import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; +import org.apache.druid.segment.join.filter.JoinableClauses; import org.apache.druid.segment.join.lookup.LookupJoinable; import org.apache.druid.segment.join.table.IndexedTableJoinable; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; @@ -140,7 +141,7 @@ public void setup() throws IOException ) ); JoinFilterPreAnalysis preAnalysisLookupStringKey = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClausesLookupStringKey, + JoinableClauses.fromList(joinableClausesLookupStringKey), VirtualColumns.EMPTY, null, false, @@ -167,7 +168,7 @@ public void setup() throws IOException ) ); JoinFilterPreAnalysis preAnalysisLookupLongKey = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClausesLookupLongKey, + JoinableClauses.fromList(joinableClausesLookupLongKey), VirtualColumns.EMPTY, null, false, @@ -194,7 +195,7 @@ public void setup() throws IOException ) ); JoinFilterPreAnalysis preAnalysisIndexedTableStringKey = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClausesIndexedTableStringKey, + JoinableClauses.fromList(joinableClausesIndexedTableStringKey), VirtualColumns.EMPTY, null, false, @@ -221,7 +222,7 @@ public void setup() throws IOException ) ); JoinFilterPreAnalysis preAnalysisIndexedTableLongKey = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClausesIndexedTableLonggKey, + JoinableClauses.fromList(joinableClausesIndexedTableLonggKey), VirtualColumns.EMPTY, null, false, diff --git a/processing/src/main/java/org/apache/druid/segment/filter/Filters.java b/processing/src/main/java/org/apache/druid/segment/filter/Filters.java index 69775a318212..b6a717bff1b1 100644 --- a/processing/src/main/java/org/apache/druid/segment/filter/Filters.java +++ b/processing/src/main/java/org/apache/druid/segment/filter/Filters.java @@ -45,6 +45,7 @@ import org.apache.druid.segment.data.Indexed; import org.apache.druid.segment.filter.cnf.CalciteCnfHelper; import org.apache.druid.segment.filter.cnf.HiveCnfHelper; +import org.apache.druid.segment.join.filter.AllNullColumnSelectorFactory; import javax.annotation.Nullable; import java.io.IOException; @@ -61,6 +62,7 @@ */ public class Filters { + private static final ColumnSelectorFactory ALL_NULL_COLUMN_SELECTOR_FACTORY = new AllNullColumnSelectorFactory(); /** * Convert a list of DimFilters to a list of Filters. @@ -543,4 +545,11 @@ public static Set toNormalizedOrClauses(Filter filter) } return normalizedOrClauses; } + + + public static boolean filterMatchesNull(Filter filter) + { + ValueMatcher valueMatcher = filter.makeMatcher(ALL_NULL_COLUMN_SELECTOR_FACTORY); + return valueMatcher.matches(); + } } diff --git a/processing/src/main/java/org/apache/druid/segment/join/Joinables.java b/processing/src/main/java/org/apache/druid/segment/join/Joinables.java index 261f2d6c8385..76bd19e877d2 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/Joinables.java +++ b/processing/src/main/java/org/apache/druid/segment/join/Joinables.java @@ -20,7 +20,6 @@ package org.apache.druid.segment.join; import org.apache.druid.java.util.common.IAE; -import org.apache.druid.java.util.common.ISE; import org.apache.druid.query.filter.Filter; import org.apache.druid.query.planning.PreJoinableClause; import org.apache.druid.segment.Segment; @@ -28,16 +27,14 @@ import org.apache.druid.segment.column.ColumnHolder; import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; +import org.apache.druid.segment.join.filter.JoinableClauses; import org.apache.druid.utils.JvmUtils; import javax.annotation.Nullable; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; -import java.util.stream.Collectors; /** * Utility methods for working with {@link Joinable} related classes. @@ -110,7 +107,7 @@ public static Function createSegmentMapFn( if (clauses.isEmpty()) { return Function.identity(); } else { - final List joinableClauses = createJoinableClauses(clauses, joinableFactory); + final JoinableClauses joinableClauses = JoinableClauses.createClauses(clauses, joinableFactory); JoinFilterPreAnalysis jfpa = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( joinableClauses, virtualColumns, @@ -120,51 +117,12 @@ public static Function createSegmentMapFn( enableRewriteValueColumnFilters, filterRewriteMaxSize ); - return baseSegment -> new HashJoinSegment(baseSegment, joinableClauses, jfpa); + return baseSegment -> new HashJoinSegment(baseSegment, joinableClauses.getJoinableClauses(), jfpa); } } ); } - /** - * Returns a list of {@link JoinableClause} corresponding to a list of {@link PreJoinableClause}. This will call - * {@link JoinableFactory#build} on each one and therefore may be an expensive operation. - */ - private static List createJoinableClauses( - final List clauses, - final JoinableFactory joinableFactory - ) - { - // Since building a JoinableClause can be expensive, check for prefix conflicts before building - checkPreJoinableClausesForDuplicatesAndShadowing(clauses); - - return clauses.stream().map(preJoinableClause -> { - final Optional joinable = joinableFactory.build( - preJoinableClause.getDataSource(), - preJoinableClause.getCondition() - ); - - return new JoinableClause( - preJoinableClause.getPrefix(), - joinable.orElseThrow(() -> new ISE("dataSource is not joinable: %s", preJoinableClause.getDataSource())), - preJoinableClause.getJoinType(), - preJoinableClause.getCondition() - ); - }).collect(Collectors.toList()); - } - - private static void checkPreJoinableClausesForDuplicatesAndShadowing( - final List preJoinableClauses - ) - { - List prefixes = new ArrayList<>(); - for (PreJoinableClause clause : preJoinableClauses) { - prefixes.add(clause.getPrefix()); - } - - checkPrefixesForDuplicatesAndShadowing(prefixes); - } - /** * Check if any prefixes in the provided list duplicate or shadow each other. * diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/Equiconditions.java b/processing/src/main/java/org/apache/druid/segment/join/filter/Equiconditions.java new file mode 100644 index 000000000000..33a07987b8b9 --- /dev/null +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/Equiconditions.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.join.filter; + +import org.apache.druid.math.expr.Expr; +import org.apache.druid.query.filter.Filter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; +import java.util.Set; + +public class Equiconditions +{ + @Nonnull private final Map> equiconditions; + + public Equiconditions(Map> equiconditions) + { + this.equiconditions = equiconditions; + } + + /** + * @param filterClause the filter. + * @return true if direct join filter rewrite is supported for the provided filter + */ + public boolean doesFilterSupportDirectJoinFilterRewrite(Filter filterClause) + { + if (filterClause.supportsRequiredColumnRewrite()) { + Set requiredColumns = filterClause.getRequiredColumns(); + if (requiredColumns.size() == 1) { + String reqColumn = requiredColumns.iterator().next(); + return equiconditions.containsKey(reqColumn); + } + } + return false; + } + + @Nullable + public Set getLhsExprs(String rhsColumn) + { + return equiconditions.get(rhsColumn); + } +} diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java index 6d1da8f20f98..a4f9ba666d6f 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java @@ -25,8 +25,6 @@ import org.apache.druid.math.expr.Expr; import org.apache.druid.query.filter.Filter; import org.apache.druid.query.filter.InDimFilter; -import org.apache.druid.query.filter.ValueMatcher; -import org.apache.druid.segment.ColumnSelectorFactory; import org.apache.druid.segment.VirtualColumn; import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.column.ValueType; @@ -36,15 +34,15 @@ import org.apache.druid.segment.join.Equality; import org.apache.druid.segment.join.JoinConditionAnalysis; import org.apache.druid.segment.join.JoinableClause; +import org.apache.druid.segment.join.filter.rewrite.RhsRewriteCandidate; +import org.apache.druid.segment.join.filter.rewrite.RhsRewriteCandidates; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -80,7 +78,6 @@ public class JoinFilterAnalyzer { private static final String PUSH_DOWN_VIRTUAL_COLUMN_NAME_BASE = "JOIN-FILTER-PUSHDOWN-VIRTUAL-COLUMN-"; - private static final ColumnSelectorFactory ALL_NULL_COLUMN_SELECTOR_FACTORY = new AllNullColumnSelectorFactory(); /** * Before making per-segment filter splitting decisions, we first do a pre-analysis step @@ -103,7 +100,7 @@ public class JoinFilterAnalyzer * @return A JoinFilterPreAnalysis containing information determined in this pre-analysis step. */ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( - List joinableClauses, + JoinableClauses joinableClauses, VirtualColumns virtualColumns, Filter originalFilter, boolean enableFilterPushDown, @@ -115,7 +112,7 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( final List preJoinVirtualColumns = new ArrayList<>(); final List postJoinVirtualColumns = new ArrayList<>(); - splitVirtualColumns(joinableClauses, virtualColumns, preJoinVirtualColumns, postJoinVirtualColumns); + joinableClauses.splitVirtualColumns(virtualColumns, preJoinVirtualColumns, postJoinVirtualColumns); JoinFilterPreAnalysis.Builder preAnalysisBuilder = new JoinFilterPreAnalysis.Builder(joinableClauses, originalFilter, postJoinVirtualColumns) .withEnableFilterPushDown(enableFilterPushDown) @@ -131,7 +128,7 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( for (Filter orClause : normalizedOrClauses) { Set reqColumns = orClause.getRequiredColumns(); - if (areSomeColumnsFromJoin(joinableClauses, reqColumns) || areSomeColumnsFromPostJoinVirtualColumns( + if (joinableClauses.areSomeColumnsFromJoin(reqColumns) || areSomeColumnsFromPostJoinVirtualColumns( postJoinVirtualColumns, reqColumns )) { @@ -148,15 +145,15 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( } // build the equicondition map, used for determining how the tables are connected through joins - Map> equiconditions = preAnalysisBuilder.computeEquiconditionsFromJoinableClauses(); + Equiconditions equiconditions = preAnalysisBuilder.computeEquiconditionsFromJoinableClauses(); - Set rhsRewriteCandidates = getRhsRewriteCandidates(normalizedJoinTableClauses, equiconditions, joinableClauses); + RhsRewriteCandidates rhsRewriteCandidates = + RhsRewriteCandidates.getRhsRewriteCandidates(normalizedJoinTableClauses, equiconditions, joinableClauses); // Build a map of RHS table prefix -> JoinFilterColumnCorrelationAnalysis based on the RHS rewrite candidates Map>> correlationsByPrefix = new HashMap<>(); Map> directRewriteCorrelations = new HashMap<>(); - - for (RhsRewriteCandidate rhsRewriteCandidate : rhsRewriteCandidates) { + for (RhsRewriteCandidate rhsRewriteCandidate : rhsRewriteCandidates.getRhsRewriteCandidates()) { if (rhsRewriteCandidate.isDirectRewrite()) { directRewriteCorrelations.computeIfAbsent( rhsRewriteCandidate.getRhsColumn(), @@ -202,7 +199,7 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( // (See JoinFilterAnalyzerTest.test_filterPushDown_factToRegionOneColumnToTwoRHSColumnsAndFilterOnRHS for an example) Map> correlationsByFilteringColumn = new LinkedHashMap<>(); Map> correlationsByDirectFilteringColumn = new LinkedHashMap<>(); - for (RhsRewriteCandidate rhsRewriteCandidate : rhsRewriteCandidates) { + for (RhsRewriteCandidate rhsRewriteCandidate : rhsRewriteCandidates.getRhsRewriteCandidates()) { if (rhsRewriteCandidate.isDirectRewrite()) { List perColumnCorrelations = correlationsByDirectFilteringColumn.computeIfAbsent( @@ -277,61 +274,6 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( return preAnalysisBuilder.build(); } - private static Optional determineRhsRewriteCandidatesForSingleFilter( - Filter orClause, - Map> equiconditions, - List joinableClauses - ) - { - // Check if the filter clause is on the RHS join column. If so, we can rewrite the clause to filter on the - // LHS join column instead. - // Currently, we only support rewrites of filters that operate on a single column for simplicity. - Set requiredColumns = orClause.getRequiredColumns(); - if (orClause.supportsRequiredColumnRewrite() && - doesRequiredColumnSetSupportDirectJoinFilterRewrite(requiredColumns, equiconditions)) { - String reqColumn = requiredColumns.iterator().next(); - JoinableClause joinableClause = isColumnFromJoin(joinableClauses, reqColumn); - - return Optional.of( - new RhsRewriteCandidate( - joinableClause, - reqColumn, - null, - true - ) - ); - } else if (orClause instanceof SelectorFilter) { - // this is a candidate for RHS filter rewrite, determine column correlations and correlated values - String reqColumn = ((SelectorFilter) orClause).getDimension(); - String reqValue = ((SelectorFilter) orClause).getValue(); - JoinableClause joinableClause = isColumnFromJoin(joinableClauses, reqColumn); - if (joinableClause != null) { - return Optional.of( - new RhsRewriteCandidate( - joinableClause, - reqColumn, - reqValue, - false - ) - ); - } - } - - return Optional.empty(); - } - - private static boolean doesRequiredColumnSetSupportDirectJoinFilterRewrite( - Set requiredColumns, - Map> equiconditions - ) - { - if (requiredColumns.size() == 1) { - String reqColumn = requiredColumns.iterator().next(); - return equiconditions.containsKey(reqColumn); - } - return false; - } - /** * @param joinFilterPreAnalysis The pre-analysis computed by {@link #computeJoinFilterPreAnalysis)} * @@ -355,7 +297,7 @@ public static JoinFilterSplit splitFilter( Map pushDownVirtualColumnsForLhsExprs = new HashMap<>(); for (Filter baseTableFilter : joinFilterPreAnalysis.getNormalizedBaseTableClauses()) { - if (!filterMatchesNull(baseTableFilter)) { + if (!Filters.filterMatchesNull(baseTableFilter)) { leftFilters.add(baseTableFilter); } else { rightFilters.add(baseTableFilter); @@ -411,7 +353,7 @@ private static JoinFilterAnalysis analyzeJoinFilterClause( // NULL matching conditions are not currently pushed down. // They require special consideration based on the join type, and for simplicity of the initial implementation // this is not currently handled. - if (!joinFilterPreAnalysis.isEnableFilterRewrite() || filterMatchesNull(filterClause)) { + if (!joinFilterPreAnalysis.isEnableFilterRewrite() || Filters.filterMatchesNull(filterClause)) { return JoinFilterAnalysis.createNoPushdownFilterAnalysis(filterClause); } @@ -423,10 +365,7 @@ private static JoinFilterAnalysis analyzeJoinFilterClause( ); } - if (filterClause.supportsRequiredColumnRewrite() && doesRequiredColumnSetSupportDirectJoinFilterRewrite( - filterClause.getRequiredColumns(), - joinFilterPreAnalysis.getEquiconditions() - )) { + if (joinFilterPreAnalysis.getEquiconditions().doesFilterSupportDirectJoinFilterRewrite(filterClause)) { return rewriteFilterDirect( filterClause, joinFilterPreAnalysis, @@ -531,16 +470,15 @@ private static JoinFilterAnalysis rewriteOrFilter( boolean retainRhs = false; for (Filter filter : orFilter.getFilters()) { - if (!areSomeColumnsFromJoin(joinFilterPreAnalysis.getJoinableClauses(), filter.getRequiredColumns())) { + if (!joinFilterPreAnalysis.getJoinableClauses().areSomeColumnsFromJoin(filter.getRequiredColumns())) { newFilters.add(filter); continue; } JoinFilterAnalysis rewritten = null; - if (doesRequiredColumnSetSupportDirectJoinFilterRewrite( - filter.getRequiredColumns(), - joinFilterPreAnalysis.getEquiconditions() - )) { + if (joinFilterPreAnalysis.getEquiconditions() + .doesFilterSupportDirectJoinFilterRewrite(filter) + ) { rewritten = rewriteFilterDirect( filter, joinFilterPreAnalysis, @@ -598,7 +536,7 @@ private static JoinFilterAnalysis rewriteSelectorFilter( return JoinFilterAnalysis.createNoPushdownFilterAnalysis(selectorFilter); } - if (!areSomeColumnsFromJoin(joinFilterPreAnalysis.getJoinableClauses(), selectorFilter.getRequiredColumns())) { + if (!joinFilterPreAnalysis.getJoinableClauses().areSomeColumnsFromJoin(selectorFilter.getRequiredColumns())) { return new JoinFilterAnalysis( false, selectorFilter, @@ -741,10 +679,10 @@ private static Set getCorrelatedValuesForPushDown( * the tablePrefix */ private static Optional> findCorrelatedBaseTableColumns( - List joinableClauses, + JoinableClauses joinableClauses, String tablePrefix, RhsRewriteCandidate rhsRewriteCandidate, - Map> equiConditions + Equiconditions equiConditions ) { JoinableClause clauseForTablePrefix = rhsRewriteCandidate.getJoinableClause(); @@ -807,15 +745,15 @@ private static Optional> findCo * modified. */ private static void getCorrelationForRHSColumn( - List joinableClauses, - Map> equiConditions, + JoinableClauses joinableClauses, + Equiconditions equiConditions, String rhsColumn, Set correlatedBaseColumns, Set correlatedBaseExpressions ) { String findMappingFor = rhsColumn; - Set lhsExprs = equiConditions.get(findMappingFor); + Set lhsExprs = equiConditions.getLhsExprs(findMappingFor); if (lhsExprs == null) { return; } @@ -827,14 +765,14 @@ private static void getCorrelationForRHSColumn( Expr.BindingDetails bindingDetails = lhsExpr.analyzeInputs(); Set requiredBindings = bindingDetails.getRequiredBindings(); - if (areSomeColumnsFromJoin(joinableClauses, requiredBindings)) { + if (joinableClauses.areSomeColumnsFromJoin(requiredBindings)) { break; } correlatedBaseExpressions.add(lhsExpr); } else { // simple identifier, see if we can correlate it with a column on the base table findMappingFor = identifier; - if (isColumnFromJoin(joinableClauses, identifier) == null) { + if (joinableClauses.getColumnFromJoinIfExists(identifier) == null) { correlatedBaseColumns.add(findMappingFor); } else { getCorrelationForRHSColumn( @@ -887,27 +825,6 @@ private static List eliminateCorrelationDup return new ArrayList<>(uniquesMap.values()); } - private static boolean filterMatchesNull(Filter filter) - { - ValueMatcher valueMatcher = filter.makeMatcher(ALL_NULL_COLUMN_SELECTOR_FACTORY); - return valueMatcher.matches(); - } - - @Nullable - private static JoinableClause isColumnFromJoin( - List joinableClauses, - String column - ) - { - for (JoinableClause joinableClause : joinableClauses) { - if (joinableClause.includesColumn(column)) { - return joinableClause; - } - } - - return null; - } - private static boolean isColumnFromPostJoinVirtualColumns( List postJoinVirtualColumns, String column @@ -921,19 +838,6 @@ private static boolean isColumnFromPostJoinVirtualColumns( return false; } - private static boolean areSomeColumnsFromJoin( - List joinableClauses, - Collection columns - ) - { - for (String column : columns) { - if (isColumnFromJoin(joinableClauses, column) != null) { - return true; - } - } - return false; - } - private static boolean areSomeColumnsFromPostJoinVirtualColumns( List postJoinVirtualColumns, Collection columns @@ -946,118 +850,4 @@ private static boolean areSomeColumnsFromPostJoinVirtualColumns( } return false; } - - private static void splitVirtualColumns( - List joinableClauses, - final VirtualColumns virtualColumns, - final List preJoinVirtualColumns, - final List postJoinVirtualColumns - ) - { - for (VirtualColumn virtualColumn : virtualColumns.getVirtualColumns()) { - if (areSomeColumnsFromJoin(joinableClauses, virtualColumn.requiredColumns())) { - postJoinVirtualColumns.add(virtualColumn); - } else { - preJoinVirtualColumns.add(virtualColumn); - } - } - } - - /** - * Determine candidates for filter rewrites. - * A candidate is an RHS column that appears in a filter, along with the value being filtered on, plus - * the joinable clause associated with the table that the RHS column is from. - * - * These candidates are redued to filter rewrite correlations. - * - * @param normalizedJoinTableClauses - * @param equiconditions - * @param joinableClauses - * @return A set of candidates for filter rewrites. - */ - private static Set getRhsRewriteCandidates( - List normalizedJoinTableClauses, - Map> equiconditions, - List joinableClauses) - { - Set rhsRewriteCandidates = new LinkedHashSet<>(); - for (Filter orClause : normalizedJoinTableClauses) { - if (filterMatchesNull(orClause)) { - continue; - } - - if (orClause instanceof OrFilter) { - for (Filter subFilter : ((OrFilter) orClause).getFilters()) { - Optional rhsRewriteCandidate = determineRhsRewriteCandidatesForSingleFilter( - subFilter, - equiconditions, - joinableClauses - ); - - rhsRewriteCandidate.ifPresent(rhsRewriteCandidates::add); - } - continue; - } - - Optional rhsRewriteCandidate = determineRhsRewriteCandidatesForSingleFilter( - orClause, - equiconditions, - joinableClauses - ); - - rhsRewriteCandidate.ifPresent(rhsRewriteCandidates::add); - } - return rhsRewriteCandidates; - } - - /** - * A candidate is an RHS column that appears in a filter, along with the value being filtered on, plus - * the joinable clause associated with the table that the RHS column is from. - */ - private static class RhsRewriteCandidate - { - private final boolean isDirectRewrite; - private final JoinableClause joinableClause; - private final String rhsColumn; - private final String valueForRewrite; - - public RhsRewriteCandidate( - JoinableClause joinableClause, - String rhsColumn, - String valueForRewrite, - boolean isDirectRewrite - ) - { - this.joinableClause = joinableClause; - this.rhsColumn = rhsColumn; - this.valueForRewrite = valueForRewrite; - this.isDirectRewrite = isDirectRewrite; - } - - public JoinableClause getJoinableClause() - { - return joinableClause; - } - - public String getRhsColumn() - { - return rhsColumn; - } - - public String getValueForRewrite() - { - return valueForRewrite; - } - - /** - * A direct rewrite occurs when we filter on an RHS column that is also part of a join equicondition. - * - * For example, if we have the filter (j.x = 'hello') and the join condition is (y = j.x), we can directly - * rewrite the j.x filter to (y = 'hello'). - */ - public boolean isDirectRewrite() - { - return isDirectRewrite; - } - } } diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterColumnCorrelationAnalysis.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterColumnCorrelationAnalysis.java index f978a8466a13..6071f404e499 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterColumnCorrelationAnalysis.java +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterColumnCorrelationAnalysis.java @@ -22,10 +22,12 @@ import org.apache.druid.java.util.common.Pair; import org.apache.druid.math.expr.Expr; +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -39,9 +41,9 @@ public class JoinFilterColumnCorrelationAnalysis { private final String joinColumn; - private final List baseColumns; - private final List baseExpressions; - private Map, Optional>> correlatedValuesMap; + @Nonnull private final List baseColumns; + @Nonnull private final List baseExpressions; + private final Map, Optional>> correlatedValuesMap; public JoinFilterColumnCorrelationAnalysis( String joinColumn, @@ -61,11 +63,13 @@ public String getJoinColumn() return joinColumn; } + @Nonnull public List getBaseColumns() { return baseColumns; } + @Nonnull public List getBaseExpressions() { return baseExpressions; @@ -80,4 +84,26 @@ public boolean supportsPushDown() { return !baseColumns.isEmpty() || !baseExpressions.isEmpty(); } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + JoinFilterColumnCorrelationAnalysis that = (JoinFilterColumnCorrelationAnalysis) o; + return Objects.equals(joinColumn, that.joinColumn) && + baseColumns.equals(that.baseColumns) && + baseExpressions.equals(that.baseExpressions) && + Objects.equals(correlatedValuesMap, that.correlatedValuesMap); + } + + @Override + public int hashCode() + { + return Objects.hash(joinColumn, baseColumns, baseExpressions, correlatedValuesMap); + } } diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java index 3ed12fae1c7b..5b3217cbd2ff 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java @@ -48,7 +48,7 @@ */ public class JoinFilterPreAnalysis { - private final List joinableClauses; + private final JoinableClauses joinableClauses; private final Filter originalFilter; private final List normalizedBaseTableClauses; private final List normalizedJoinTableClauses; @@ -57,10 +57,10 @@ public class JoinFilterPreAnalysis private final boolean enableFilterPushDown; private final boolean enableFilterRewrite; private final List postJoinVirtualColumns; - private final Map> equiconditions; + private final Equiconditions equiconditions; private JoinFilterPreAnalysis( - final List joinableClauses, + final JoinableClauses joinableClauses, final Filter originalFilter, final List postJoinVirtualColumns, final List normalizedBaseTableClauses, @@ -69,7 +69,7 @@ private JoinFilterPreAnalysis( final Map> correlationsByDirectFilteringColumn, final boolean enableFilterPushDown, final boolean enableFilterRewrite, - final Map> equiconditions + final Equiconditions equiconditions ) { this.joinableClauses = joinableClauses; @@ -84,7 +84,7 @@ private JoinFilterPreAnalysis( this.equiconditions = equiconditions; } - public List getJoinableClauses() + public JoinableClauses getJoinableClauses() { return joinableClauses; } @@ -129,7 +129,7 @@ public boolean isEnableFilterRewrite() return enableFilterRewrite; } - public Map> getEquiconditions() + public Equiconditions getEquiconditions() { return equiconditions; } @@ -139,7 +139,7 @@ public Map> getEquiconditions() */ public static class Builder { - @Nonnull private final List joinableClauses; + @Nonnull private final JoinableClauses joinableClauses; @Nullable private final Filter originalFilter; @Nullable private List normalizedBaseTableClauses; @Nullable private List normalizedJoinTableClauses; @@ -148,10 +148,10 @@ public static class Builder private boolean enableFilterPushDown = false; private boolean enableFilterRewrite = false; @Nonnull private final List postJoinVirtualColumns; - @Nonnull private Map> equiconditions = Collections.emptyMap(); + @Nonnull private Equiconditions equiconditions = new Equiconditions(Collections.emptyMap()); public Builder( - @Nonnull List joinableClauses, + @Nonnull JoinableClauses joinableClauses, @Nullable Filter originalFilter, @Nonnull List postJoinVirtualColumns ) @@ -201,18 +201,19 @@ public Builder withEnableFilterRewrite(boolean enableFilterRewrite) return this; } - public Map> computeEquiconditionsFromJoinableClauses() + public Equiconditions computeEquiconditionsFromJoinableClauses() { - this.equiconditions = new HashMap<>(); - for (JoinableClause clause : joinableClauses) { + Map> equiconditionsMap = new HashMap<>(); + for (JoinableClause clause : joinableClauses.getJoinableClauses()) { for (Equality equality : clause.getCondition().getEquiConditions()) { - Set exprsForRhs = equiconditions.computeIfAbsent( + Set exprsForRhs = equiconditionsMap.computeIfAbsent( clause.getPrefix() + equality.getRightColumn(), (rhs) -> new HashSet<>() ); exprsForRhs.add(equality.getLeftExpr()); } } + this.equiconditions = new Equiconditions(equiconditionsMap); return equiconditions; } diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinableClauses.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinableClauses.java new file mode 100644 index 000000000000..44736ef8dd2f --- /dev/null +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinableClauses.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.join.filter; + +import org.apache.druid.java.util.common.ISE; +import org.apache.druid.query.planning.PreJoinableClause; +import org.apache.druid.segment.VirtualColumn; +import org.apache.druid.segment.VirtualColumns; +import org.apache.druid.segment.join.Joinable; +import org.apache.druid.segment.join.JoinableClause; +import org.apache.druid.segment.join.JoinableFactory; +import org.apache.druid.segment.join.Joinables; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class JoinableClauses +{ + @Nonnull private final List joinableClauses; + + /** + * Builds a list of {@link JoinableClause} corresponding to a list of {@link PreJoinableClause}. This will call + * {@link JoinableFactory#build} on each one and therefore may be an expensive operation. + */ + public static JoinableClauses createClauses(final List clauses, final JoinableFactory joinableFactory) + { + // Since building a JoinableClause can be expensive, check for prefix conflicts before building + checkPreJoinableClausesForDuplicatesAndShadowing(clauses); + + List joinableClauses = clauses.stream().map(preJoinableClause -> { + final Optional joinable = joinableFactory.build( + preJoinableClause.getDataSource(), + preJoinableClause.getCondition() + ); + + return new JoinableClause( + preJoinableClause.getPrefix(), + joinable.orElseThrow(() -> new ISE("dataSource is not joinable: %s", preJoinableClause.getDataSource())), + preJoinableClause.getJoinType(), + preJoinableClause.getCondition() + ); + }).collect(Collectors.toList()); + return new JoinableClauses(joinableClauses); + } + + private JoinableClauses(@Nonnull List joinableClauses) + { + this.joinableClauses = joinableClauses; + } + + @Nonnull + public List getJoinableClauses() + { + return joinableClauses; + } + + public void splitVirtualColumns( + final VirtualColumns virtualColumns, + final List preJoinVirtualColumns, + final List postJoinVirtualColumns + ) + { + for (VirtualColumn virtualColumn : virtualColumns.getVirtualColumns()) { + if (areSomeColumnsFromJoin(virtualColumn.requiredColumns())) { + postJoinVirtualColumns.add(virtualColumn); + } else { + preJoinVirtualColumns.add(virtualColumn); + } + } + } + + public boolean areSomeColumnsFromJoin( + Collection columns + ) + { + for (String column : columns) { + if (getColumnFromJoinIfExists(column) != null) { + return true; + } + } + return false; + } + + @Nullable + public JoinableClause getColumnFromJoinIfExists( + String column + ) + { + for (JoinableClause joinableClause : joinableClauses) { + if (joinableClause.includesColumn(column)) { + return joinableClause; + } + } + + return null; + } + + private static void checkPreJoinableClausesForDuplicatesAndShadowing( + final List preJoinableClauses + ) + { + List prefixes = new ArrayList<>(); + for (PreJoinableClause clause : preJoinableClauses) { + prefixes.add(clause.getPrefix()); + } + + Joinables.checkPrefixesForDuplicatesAndShadowing(prefixes); + } + + public static JoinableClauses fromList(List clauses) + { + return new JoinableClauses(clauses); + } +} diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidate.java b/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidate.java new file mode 100644 index 000000000000..3a7a8df31916 --- /dev/null +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidate.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.join.filter.rewrite; + +import org.apache.druid.segment.join.JoinableClause; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Objects; + +/** + * A candidate is an RHS column that appears in a filter, along with the value being filtered on, plus + * the joinable clause associated with the table that the RHS column is from. + */ +public class RhsRewriteCandidate +{ + private final boolean isDirectRewrite; + @Nonnull private final JoinableClause joinableClause; + private final String rhsColumn; + @Nullable private final String valueForRewrite; + + public RhsRewriteCandidate( + @Nonnull JoinableClause joinableClause, + String rhsColumn, + @Nullable String valueForRewrite, + boolean isDirectRewrite + ) + { + this.joinableClause = joinableClause; + this.rhsColumn = rhsColumn; + this.valueForRewrite = valueForRewrite; + this.isDirectRewrite = isDirectRewrite; + } + + @Nonnull + public JoinableClause getJoinableClause() + { + return joinableClause; + } + + public String getRhsColumn() + { + return rhsColumn; + } + + @Nullable + public String getValueForRewrite() + { + return valueForRewrite; + } + + /** + * A direct rewrite occurs when we filter on an RHS column that is also part of a join equicondition. + * + * For example, if we have the filter (j.x = 'hello') and the join condition is (y = j.x), we can directly + * rewrite the j.x filter to (y = 'hello'). + */ + public boolean isDirectRewrite() + { + return isDirectRewrite; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RhsRewriteCandidate that = (RhsRewriteCandidate) o; + return isDirectRewrite == that.isDirectRewrite && + joinableClause.equals(that.joinableClause) && + Objects.equals(rhsColumn, that.rhsColumn) && + Objects.equals(valueForRewrite, that.valueForRewrite); + } + + @Override + public int hashCode() + { + return Objects.hash(isDirectRewrite, joinableClause, rhsColumn, valueForRewrite); + } +} diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidates.java b/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidates.java new file mode 100644 index 000000000000..71c732fb1149 --- /dev/null +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidates.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.join.filter.rewrite; + +import org.apache.druid.query.filter.Filter; +import org.apache.druid.segment.filter.Filters; +import org.apache.druid.segment.filter.OrFilter; +import org.apache.druid.segment.filter.SelectorFilter; +import org.apache.druid.segment.join.JoinableClause; +import org.apache.druid.segment.join.filter.Equiconditions; +import org.apache.druid.segment.join.filter.JoinableClauses; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +public class RhsRewriteCandidates +{ + private final Set rhsRewriteCandidates; + + private RhsRewriteCandidates(Set rhsRewriteCandidates) + { + this.rhsRewriteCandidates = rhsRewriteCandidates; + } + + public Set getRhsRewriteCandidates() + { + return rhsRewriteCandidates; + } + + /** + * Determine candidates for filter rewrites. + * A candidate is an RHS column that appears in a filter, along with the value being filtered on, plus + * the joinable clause associated with the table that the RHS column is from. + * + * These candidates are redued to filter rewrite correlations. + * + * @param normalizedJoinTableClauses + * @param equiconditions + * @param joinableClauses + * @return A set of candidates for filter rewrites. + */ + public static RhsRewriteCandidates getRhsRewriteCandidates( + List normalizedJoinTableClauses, + Equiconditions equiconditions, + JoinableClauses joinableClauses) + { + Set rhsRewriteCandidates = new LinkedHashSet<>(); + for (Filter orClause : normalizedJoinTableClauses) { + if (Filters.filterMatchesNull(orClause)) { + continue; + } + + if (orClause instanceof OrFilter) { + for (Filter subFilter : ((OrFilter) orClause).getFilters()) { + Optional rhsRewriteCandidate = determineRhsRewriteCandidatesForSingleFilter( + subFilter, + equiconditions, + joinableClauses + ); + + rhsRewriteCandidate.ifPresent(rhsRewriteCandidates::add); + } + continue; + } + + Optional rhsRewriteCandidate = determineRhsRewriteCandidatesForSingleFilter( + orClause, + equiconditions, + joinableClauses + ); + + rhsRewriteCandidate.ifPresent(rhsRewriteCandidates::add); + } + return new RhsRewriteCandidates(rhsRewriteCandidates); + } + + private static Optional determineRhsRewriteCandidatesForSingleFilter( + Filter orClause, + Equiconditions equiconditions, + JoinableClauses joinableClauses + ) + { + // Check if the filter clause is on the RHS join column. If so, we can rewrite the clause to filter on the + // LHS join column instead. + // Currently, we only support rewrites of filters that operate on a single column for simplicity. + if (equiconditions.doesFilterSupportDirectJoinFilterRewrite(orClause)) { + String reqColumn = orClause.getRequiredColumns().iterator().next(); + JoinableClause joinableClause = joinableClauses.getColumnFromJoinIfExists(reqColumn); + if (joinableClause != null) { + return Optional.of( + new RhsRewriteCandidate( + joinableClause, + reqColumn, + null, + true + ) + ); + } + } else if (orClause instanceof SelectorFilter) { + // this is a candidate for RHS filter rewrite, determine column correlations and correlated values + String reqColumn = ((SelectorFilter) orClause).getDimension(); + String reqValue = ((SelectorFilter) orClause).getValue(); + JoinableClause joinableClause = joinableClauses.getColumnFromJoinIfExists(reqColumn); + if (joinableClause != null) { + return Optional.of( + new RhsRewriteCandidate( + joinableClause, + reqColumn, + reqValue, + false + ) + ); + } + } + + return Optional.empty(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RhsRewriteCandidates that = (RhsRewriteCandidates) o; + return Objects.equals(rhsRewriteCandidates, that.rhsRewriteCandidates); + } + + @Override + public int hashCode() + { + return Objects.hash(rhsRewriteCandidates); + } +} diff --git a/processing/src/test/java/org/apache/druid/segment/join/BaseHashJoinSegmentStorageAdapterTest.java b/processing/src/test/java/org/apache/druid/segment/join/BaseHashJoinSegmentStorageAdapterTest.java index a01a34b9c23c..45befeba36ed 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/BaseHashJoinSegmentStorageAdapterTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/BaseHashJoinSegmentStorageAdapterTest.java @@ -29,6 +29,7 @@ import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; +import org.apache.druid.segment.join.filter.JoinableClauses; import org.apache.druid.segment.join.lookup.LookupJoinable; import org.apache.druid.segment.join.table.IndexedTable; import org.apache.druid.segment.join.table.IndexedTableJoinable; @@ -187,7 +188,7 @@ protected JoinableClause regionToCountry(final JoinType joinType) protected HashJoinSegmentStorageAdapter makeFactToCountrySegment() { JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT)), + JoinableClauses.fromList(ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT))), VirtualColumns.EMPTY, null, true, diff --git a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java index c58291301d06..2e863b2487a2 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java @@ -39,6 +39,7 @@ import org.apache.druid.segment.filter.SelectorFilter; import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; +import org.apache.druid.segment.join.filter.JoinableClauses; import org.apache.druid.segment.join.lookup.LookupJoinable; import org.apache.druid.segment.join.table.IndexedTableJoinable; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; @@ -302,7 +303,7 @@ public void test_makeCursors_factToCountryLeft() List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT)); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -371,7 +372,7 @@ public void test_makeCursors_factToCountryLeftUsingLookup() List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.LEFT)); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -438,7 +439,7 @@ public void test_makeCursors_factToCountryInner() { List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.INNER)); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -500,7 +501,7 @@ public void test_makeCursors_factToCountryInnerUsingLookup() { List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.INNER)); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -564,7 +565,7 @@ public void test_makeCursors_factToCountryInnerUsingCountryNumber() List joinableClauses = ImmutableList.of(factToCountryOnNumber(JoinType.INNER)); Filter filter = new SelectorDimFilter("channel", "#en.wikipedia", null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -634,7 +635,7 @@ public void test_makeCursors_factToCountryInnerUsingCountryNumberUsingLookup() List joinableClauses = ImmutableList.of(factToCountryNameUsingNumberLookup(JoinType.INNER)); Filter filter = new SelectorDimFilter("channel", "#en.wikipedia", null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -700,7 +701,7 @@ public void test_makeCursors_factToCountryLeftWithFilterOnFacts() List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT)); Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -741,7 +742,7 @@ public void test_makeCursors_factToCountryLeftWithFilterOnFactsUsingLookup() List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.LEFT)); Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -781,7 +782,7 @@ public void test_makeCursors_factToCountryRightWithFilterOnLeftIsNull() List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.RIGHT)); Filter filter = new SelectorDimFilter("channel", null, null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -824,7 +825,7 @@ public void test_makeCursors_factToCountryRightWithFilterOnLeftIsNullUsingLookup List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.RIGHT)); Filter filter = new SelectorDimFilter("channel", null, null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -866,7 +867,7 @@ public void test_makeCursors_factToCountryFullWithFilterOnLeftIsNull() List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.FULL)); Filter filter = new SelectorDimFilter("channel", null, null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -909,7 +910,7 @@ public void test_makeCursors_factToCountryFullWithFilterOnLeftIsNullUsingLookup( List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.FULL)); Filter filter = new SelectorDimFilter("channel", null, null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -956,7 +957,7 @@ public void test_makeCursors_factToCountryRightWithFilterOnJoinable() ).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -1003,7 +1004,7 @@ public void test_makeCursors_factToCountryRightWithFilterOnJoinableUsingLookup() ).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -1050,7 +1051,7 @@ public void test_makeCursors_factToCountryLeftWithFilterOnJoinable() ).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -1097,7 +1098,7 @@ public void test_makeCursors_factToCountryLeftWithFilterOnJoinableUsingLookup() ).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -1157,7 +1158,7 @@ public void test_makeCursors_factToCountryInnerWithFilterInsteadOfRealJoinCondit ).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -1239,7 +1240,7 @@ public void test_makeCursors_factToCountryInnerWithFilterInsteadOfRealJoinCondit ).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -1304,7 +1305,7 @@ public void test_makeCursors_factToRegionToCountryLeft() ); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -1383,7 +1384,7 @@ public void test_makeCursors_factToCountryAlwaysTrue() Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -1450,7 +1451,7 @@ public void test_makeCursors_factToCountryAlwaysFalse() Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -1501,7 +1502,7 @@ public void test_makeCursors_factToCountryAlwaysTrueUsingLookup() Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -1569,7 +1570,7 @@ public void test_makeCursors_factToCountryAlwaysFalseUsingLookup() Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -1629,7 +1630,7 @@ public void test_makeCursors_factToCountryUsingVirtualColumn() ); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), virtualColumns, null, true, @@ -1695,7 +1696,7 @@ public void test_makeCursors_factToCountryUsingVirtualColumnUsingLookup() ); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), virtualColumns, null, true, @@ -1753,7 +1754,7 @@ public void test_makeCursors_factToCountryUsingExpression() ); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -1810,7 +1811,7 @@ public void test_makeCursors_factToCountryUsingExpressionUsingLookup() ); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -1870,7 +1871,7 @@ public void test_makeCursors_factToRegionTheWrongWay() Filter filter = new SelectorDimFilter("regionIsoCode", "VA", null).toFilter(); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, filter, true, @@ -1930,7 +1931,7 @@ public void test_makeCursors_errorOnNonEquiJoin() ); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -1976,7 +1977,7 @@ public void test_makeCursors_errorOnNonEquiJoinUsingLookup() ); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -2022,7 +2023,7 @@ public void test_makeCursors_errorOnNonKeyBasedJoin() ); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -2067,7 +2068,7 @@ public void test_makeCursors_errorOnNonKeyBasedJoinUsingLookup() ); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -2100,7 +2101,7 @@ public void test_makeCursors_factToCountryLeft_filterExcludesAllLeftRows() List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT)); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, originalFilter, true, @@ -2139,7 +2140,7 @@ public void test_makeCursors_factToCountryLeft_filterExcludesAllLeftRowsUsingLoo Filter originalFilter = new SelectorFilter("page", "this matches nothing"); List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.LEFT)); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, originalFilter, true, @@ -2177,7 +2178,7 @@ public void test_makeCursors_originalFilterDoesNotMatchPreAnalysis_shouldThrowIS List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT)); JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, diff --git a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java index 40a4863e9590..3ff19e4d5918 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java @@ -27,6 +27,7 @@ import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; +import org.apache.druid.segment.join.filter.JoinableClauses; import org.apache.druid.segment.join.table.IndexedTableJoinable; import org.apache.druid.timeline.SegmentId; import org.hamcrest.CoreMatchers; @@ -82,7 +83,7 @@ public void setUp() throws IOException ); JoinFilterPreAnalysis joinFilterPreAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, @@ -107,7 +108,7 @@ public void test_constructor_noClauses() List joinableClauses = ImmutableList.of(); JoinFilterPreAnalysis joinFilterPreAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, null, true, diff --git a/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java b/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java index 753b35937891..e1e480be54ed 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java @@ -44,6 +44,7 @@ import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; import org.apache.druid.segment.join.filter.JoinFilterSplit; +import org.apache.druid.segment.join.filter.JoinableClauses; import org.apache.druid.segment.join.lookup.LookupJoinable; import org.apache.druid.segment.join.table.IndexedTableJoinable; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; @@ -466,9 +467,9 @@ public void test_filterPushDown_factToRegionFilterOnRHSRegionNameExprVirtualColu ) ); - List joinableClauses = ImmutableList.of( + JoinableClauses joinableClauses = JoinableClauses.fromList(ImmutableList.of( factToRegion(JoinType.LEFT) - ); + )); JoinFilterPreAnalysis joinFilterPreAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( joinableClauses, @@ -482,7 +483,7 @@ public void test_filterPushDown_factToRegionFilterOnRHSRegionNameExprVirtualColu HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), - joinableClauses, + joinableClauses.getJoinableClauses(), joinFilterPreAnalysis ); @@ -1974,10 +1975,10 @@ public void test_filterPushDown_factToRegionThreeRHSColumnsAllDirectAndFilterOnR @Test public void test_filterPushDown_factToRegionToCountryLeftFilterOnPageDisablePushDown() { - List joinableClauses = ImmutableList.of( + JoinableClauses joinableClauses = JoinableClauses.fromList(ImmutableList.of( factToRegion(JoinType.LEFT), regionToCountry(JoinType.LEFT) - ); + )); Filter originalFilter = new SelectorFilter("page", "Peremptory norm"); JoinFilterPreAnalysis joinFilterPreAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( @@ -1991,7 +1992,7 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnPageDisablePush ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), - joinableClauses, + joinableClauses.getJoinableClauses(), joinFilterPreAnalysis ); @@ -2026,10 +2027,10 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnPageDisablePush @Test public void test_filterPushDown_factToRegionToCountryLeftEnablePushDownDisableRewrite() { - List joinableClauses = ImmutableList.of( + JoinableClauses joinableClauses = JoinableClauses.fromList(ImmutableList.of( factToRegion(JoinType.LEFT), regionToCountry(JoinType.LEFT) - ); + )); Filter originalFilter = new AndFilter( ImmutableList.of( new SelectorFilter("channel", "#en.wikipedia"), @@ -2063,7 +2064,7 @@ public void test_filterPushDown_factToRegionToCountryLeftEnablePushDownDisableRe ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), - joinableClauses, + joinableClauses.getJoinableClauses(), joinFilterPreAnalysis ); @@ -2450,7 +2451,7 @@ private static JoinFilterPreAnalysis simplePreAnalysis( ) { return JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, + JoinableClauses.fromList(joinableClauses), VirtualColumns.EMPTY, originalFilter, true, diff --git a/processing/src/test/java/org/apache/druid/segment/join/filter/JoinFilterColumnCorrelationAnalysisTest.java b/processing/src/test/java/org/apache/druid/segment/join/filter/JoinFilterColumnCorrelationAnalysisTest.java new file mode 100644 index 000000000000..39fe2bcefb5a --- /dev/null +++ b/processing/src/test/java/org/apache/druid/segment/join/filter/JoinFilterColumnCorrelationAnalysisTest.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.join.filter; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; + +public class JoinFilterColumnCorrelationAnalysisTest +{ + @Test + public void testEqualsContract() + { + EqualsVerifier.forClass(JoinFilterColumnCorrelationAnalysis.class).usingGetClass().verify(); + } +} diff --git a/processing/src/test/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandateTest.java b/processing/src/test/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandateTest.java new file mode 100644 index 000000000000..2586307008b2 --- /dev/null +++ b/processing/src/test/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandateTest.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.join.filter.rewrite; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; + +public class RhsRewriteCandateTest +{ + @Test + public void testEqualsContract() + { + EqualsVerifier.forClass(RhsRewriteCandidate.class).usingGetClass().verify(); + } +} diff --git a/processing/src/test/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidatesTest.java b/processing/src/test/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidatesTest.java new file mode 100644 index 000000000000..331a6a66600b --- /dev/null +++ b/processing/src/test/java/org/apache/druid/segment/join/filter/rewrite/RhsRewriteCandidatesTest.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.join.filter.rewrite; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; + +public class RhsRewriteCandidatesTest +{ + @Test + public void testEqualsContract() + { + EqualsVerifier.forClass(RhsRewriteCandidates.class).usingGetClass().verify(); + } +} From cddfd9a82ad393b810f454db8d9b998e390e1af0 Mon Sep 17 00:00:00 2001 From: Suneet Saldanha <44787917+suneet-s@users.noreply.github.com> Date: Fri, 29 May 2020 16:53:03 -0700 Subject: [PATCH 043/107] Optimize join queries where filter matches nothing (#9931) * Refactor JoinFilterAnalyzer This patch attempts to make it easier to follow the join filter analysis code with the hope of making it easier to add rewrite optimizations in the future. To keep the patch small and easy to review, this is the first of at least 2 patches that are planned. This patch adds a builder to the Pre-Analysis, so that it is easier to instantiate the preAnalysis. It also moves some of the filter normalization code out to Fitlers with associated tests. * fix tests * Refactor JoinFilterAnalyzer - part 2 This change introduces the following components: * RhsRewriteCandidates - a wrapper for a list of candidates and associated functions to operate on the set of candidates. * JoinableClauses - a wrapper for the list of JoinableClause that represent a join condition and the associated functions to operate on the clauses. * Equiconditions - a wrapper representing the equiconditions that are used in the join condition. And associated test changes. This refactoring surfaced 2 bugs: - Missing equals and hashcode implementation for RhsRewriteCandidate, thus allowing potential duplicates in the rhs rewrite candidates - Missing Filter#supportsRequiredColumnRewrite check in analyzeJoinFilterClause, which could result in UnsupportedOperationException being thrown by the filter * fix compile error * remove unused class * Refactor JoinFilterAnalyzer - Correlations Move the correlation related code out into it's own class so it's easier to maintain. Another patch should follow this one so that the query path uses the correlation object instead of it's underlying maps. * Optimize join queries where filter matches nothing Fixes #9787 This PR changes the Joinable interface to return an Optional set of correlated values for a column. This allows the JoinFilterAnalyzer to differentiate between the case where the column has no matching values and when the column could not find matching values. This PR chose not to distinguish between cases where correlated values could not be computed because of a config that has this behavior disabled or because of user error - like a column that could not be found. The reasoning was that the latter is likely an error and the non filter pushdown path will surface the error if it is. --- .../apache/druid/segment/join/Joinable.java | 7 +- .../join/filter/JoinFilterAnalyzer.java | 366 +--------------- .../join/filter/JoinFilterCorrelations.java | 407 ++++++++++++++++++ .../join/filter/JoinFilterPreAnalysis.java | 35 +- .../segment/join/lookup/LookupJoinable.java | 9 +- .../join/table/IndexedTableJoinable.java | 16 +- .../segment/join/JoinFilterAnalyzerTest.java | 55 +++ .../join/lookup/LookupJoinableTest.java | 43 +- .../join/table/IndexedTableJoinableTest.java | 55 +-- .../sql/calcite/BaseCalciteQueryTest.java | 2 +- .../druid/sql/calcite/CalciteQueryTest.java | 37 ++ 11 files changed, 600 insertions(+), 432 deletions(-) create mode 100644 processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterCorrelations.java diff --git a/processing/src/main/java/org/apache/druid/segment/join/Joinable.java b/processing/src/main/java/org/apache/druid/segment/join/Joinable.java index 1ededff5e8b8..001a0fccd21e 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/Joinable.java +++ b/processing/src/main/java/org/apache/druid/segment/join/Joinable.java @@ -24,6 +24,7 @@ import javax.annotation.Nullable; import java.util.List; +import java.util.Optional; import java.util.Set; /** @@ -88,9 +89,11 @@ JoinMatcher makeJoinMatcher( * returned than this limit, return an empty set. * @param allowNonKeyColumnSearch If true, allow searchs on non-key columns. If this is false, * a search on a non-key column should return an empty set. - * @return The set of correlated column values. If we cannot determine correlated values, return an empty set. + * @return The set of correlated column values. If we cannot determine correlated values, return absent. + * + * In case either the search or retrieval column names are not found, this will return absent. */ - Set getCorrelatedColumnValues( + Optional> getCorrelatedColumnValues( String searchColumnName, String searchColumnValue, String retrievalColumnName, diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java index a4f9ba666d6f..f4f7d31be1cf 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java @@ -28,21 +28,16 @@ import org.apache.druid.segment.VirtualColumn; import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.column.ValueType; +import org.apache.druid.segment.filter.FalseFilter; import org.apache.druid.segment.filter.Filters; import org.apache.druid.segment.filter.OrFilter; import org.apache.druid.segment.filter.SelectorFilter; -import org.apache.druid.segment.join.Equality; -import org.apache.druid.segment.join.JoinConditionAnalysis; -import org.apache.druid.segment.join.JoinableClause; -import org.apache.druid.segment.join.filter.rewrite.RhsRewriteCandidate; -import org.apache.druid.segment.join.filter.rewrite.RhsRewriteCandidates; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -147,131 +142,16 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( // build the equicondition map, used for determining how the tables are connected through joins Equiconditions equiconditions = preAnalysisBuilder.computeEquiconditionsFromJoinableClauses(); - RhsRewriteCandidates rhsRewriteCandidates = - RhsRewriteCandidates.getRhsRewriteCandidates(normalizedJoinTableClauses, equiconditions, joinableClauses); - - // Build a map of RHS table prefix -> JoinFilterColumnCorrelationAnalysis based on the RHS rewrite candidates - Map>> correlationsByPrefix = new HashMap<>(); - Map> directRewriteCorrelations = new HashMap<>(); - for (RhsRewriteCandidate rhsRewriteCandidate : rhsRewriteCandidates.getRhsRewriteCandidates()) { - if (rhsRewriteCandidate.isDirectRewrite()) { - directRewriteCorrelations.computeIfAbsent( - rhsRewriteCandidate.getRhsColumn(), - c -> { - Optional> correlatedBaseTableColumns = - findCorrelatedBaseTableColumns( - joinableClauses, - c, - rhsRewriteCandidate, - equiconditions - ); - if (!correlatedBaseTableColumns.isPresent()) { - return Optional.empty(); - } else { - JoinFilterColumnCorrelationAnalysis baseColumnAnalysis = correlatedBaseTableColumns.get().get(c); - // for direct rewrites, there will only be one analysis keyed by the RHS column - assert (baseColumnAnalysis != null); - return Optional.of(correlatedBaseTableColumns.get().get(c)); - } - } - ); - } else { - correlationsByPrefix.computeIfAbsent( - rhsRewriteCandidate.getJoinableClause().getPrefix(), - p -> findCorrelatedBaseTableColumns( - joinableClauses, - p, - rhsRewriteCandidate, - equiconditions - ) - ); - } - } - - // Using the RHS table prefix -> JoinFilterColumnCorrelationAnalysis created in the previous step, - // build a map of rhsFilterColumn -> Pair(rhsFilterColumn, rhsFilterValue) -> correlatedValues for specific filter pair - // The Pair(rhsFilterColumn, rhsFilterValue) -> correlatedValues mappings are stored in the - // JoinFilterColumnCorrelationAnalysis objects, which are shared across all rhsFilterColumn entries that belong - // to the same RHS table. - // - // The value is a List instead of a single value because a table can be joined - // to another via multiple columns. - // (See JoinFilterAnalyzerTest.test_filterPushDown_factToRegionOneColumnToTwoRHSColumnsAndFilterOnRHS for an example) - Map> correlationsByFilteringColumn = new LinkedHashMap<>(); - Map> correlationsByDirectFilteringColumn = new LinkedHashMap<>(); - for (RhsRewriteCandidate rhsRewriteCandidate : rhsRewriteCandidates.getRhsRewriteCandidates()) { - if (rhsRewriteCandidate.isDirectRewrite()) { - List perColumnCorrelations = - correlationsByDirectFilteringColumn.computeIfAbsent( - rhsRewriteCandidate.getRhsColumn(), - (rhsCol) -> new ArrayList<>() - ); - perColumnCorrelations.add( - directRewriteCorrelations.get(rhsRewriteCandidate.getRhsColumn()).get() - ); - continue; - } - - Optional> correlationsForPrefix = correlationsByPrefix.get( - rhsRewriteCandidate.getJoinableClause().getPrefix() - ); - if (correlationsForPrefix.isPresent()) { - for (Map.Entry correlationForPrefix : correlationsForPrefix.get() - .entrySet()) { - List perColumnCorrelations = - correlationsByFilteringColumn.computeIfAbsent( - rhsRewriteCandidate.getRhsColumn(), - (rhsCol) -> new ArrayList<>() - ); - perColumnCorrelations.add(correlationForPrefix.getValue()); - correlationForPrefix.getValue().getCorrelatedValuesMap().computeIfAbsent( - Pair.of(rhsRewriteCandidate.getRhsColumn(), rhsRewriteCandidate.getValueForRewrite()), - (rhsVal) -> { - Set correlatedValues = getCorrelatedValuesForPushDown( - rhsRewriteCandidate.getRhsColumn(), - rhsRewriteCandidate.getValueForRewrite(), - correlationForPrefix.getValue().getJoinColumn(), - rhsRewriteCandidate.getJoinableClause(), - enableRewriteValueColumnFilters, - filterRewriteMaxSize - ); - - if (correlatedValues.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(correlatedValues); - } - } - ); - } - } else { - correlationsByFilteringColumn.put(rhsRewriteCandidate.getRhsColumn(), null); - } - } - - // Go through each per-column analysis list and prune duplicates - for (Map.Entry> correlation : correlationsByFilteringColumn - .entrySet()) { - if (correlation.getValue() != null) { - List dedupList = eliminateCorrelationDuplicates( - correlation.getValue() - ); - correlationsByFilteringColumn.put(correlation.getKey(), dedupList); - } - } - for (Map.Entry> correlation : correlationsByDirectFilteringColumn - .entrySet()) { - if (correlation.getValue() != null) { - List dedupList = eliminateCorrelationDuplicates( - correlation.getValue() - ); - correlationsByDirectFilteringColumn.put(correlation.getKey(), dedupList); - } - } - preAnalysisBuilder.withCorrelationsByFilteringColumn(correlationsByFilteringColumn) - .withCorrelationsByDirectFilteringColumn(correlationsByDirectFilteringColumn); + JoinFilterCorrelations correlations = JoinFilterCorrelations.computeJoinFilterCorrelations( + normalizedJoinTableClauses, + equiconditions, + joinableClauses, + enableRewriteValueColumnFilters, + filterRewriteMaxSize + ); - return preAnalysisBuilder.build(); + return preAnalysisBuilder.withCorrelations(correlations) + .build(); } /** @@ -561,10 +441,20 @@ private static JoinFilterAnalysis rewriteSelectorFilter( return JoinFilterAnalysis.createNoPushdownFilterAnalysis(selectorFilter); } + Set newFilterValues = correlatedValues.get(); + // in nothing => match nothing + if (newFilterValues.isEmpty()) { + return new JoinFilterAnalysis( + true, + selectorFilter, + FalseFilter.instance() + ); + } + for (String correlatedBaseColumn : correlationAnalysis.getBaseColumns()) { Filter rewrittenFilter = new InDimFilter( correlatedBaseColumn, - correlatedValues.get(), + newFilterValues, null, null ).toFilter(); @@ -587,7 +477,7 @@ private static JoinFilterAnalysis rewriteSelectorFilter( Filter rewrittenFilter = new InDimFilter( pushDownVirtualColumn.getOutputName(), - correlatedValues.get(), + newFilterValues, null, null ).toFilter(); @@ -613,218 +503,6 @@ private static String getCorrelatedBaseExprVirtualColumnName(int counter) return PUSH_DOWN_VIRTUAL_COLUMN_NAME_BASE + counter; } - /** - * Helper method for rewriting filters on join table columns into filters on base table columns. - * - * @param filterColumn A join table column that we're filtering on - * @param filterValue The value to filter on - * @param correlatedJoinColumn A join table column that appears as the RHS of an equicondition, which we can correlate - * with a column on the base table - * @param clauseForFilteredTable The joinable clause that corresponds to the join table being filtered on - * - * @return A list of values of the correlatedJoinColumn that appear in rows where filterColumn = filterValue - * Returns an empty set if we cannot determine the correlated values. - */ - private static Set getCorrelatedValuesForPushDown( - String filterColumn, - String filterValue, - String correlatedJoinColumn, - JoinableClause clauseForFilteredTable, - boolean enableRewriteValueColumnFilters, - long filterRewriteMaxSize - ) - { - String filterColumnNoPrefix = filterColumn.substring(clauseForFilteredTable.getPrefix().length()); - String correlatedColumnNoPrefix = correlatedJoinColumn.substring(clauseForFilteredTable.getPrefix().length()); - - return clauseForFilteredTable.getJoinable().getCorrelatedColumnValues( - filterColumnNoPrefix, - filterValue, - correlatedColumnNoPrefix, - filterRewriteMaxSize, - enableRewriteValueColumnFilters - ); - } - - /** - * For each rhs column that appears in the equiconditions for a table's JoinableClause, - * we try to determine what base table columns are related to the rhs column through the total set of equiconditions. - * We do this by searching backwards through the chain of join equiconditions using the provided equicondition map. - * - * For example, suppose we have 3 tables, A,B,C, joined with the following conditions, where A is the base table: - * A.joinColumn == B.joinColumn - * B.joinColum == C.joinColumn - * - * We would determine that C.joinColumn is correlated with A.joinColumn: we first see that - * C.joinColumn is linked to B.joinColumn which in turn is linked to A.joinColumn - * - * Suppose we had the following join conditions instead: - * f(A.joinColumn) == B.joinColumn - * B.joinColum == C.joinColumn - * In this case, the JoinFilterColumnCorrelationAnalysis for C.joinColumn would be linked to f(A.joinColumn). - * - * Suppose we had the following join conditions instead: - * A.joinColumn == B.joinColumn - * f(B.joinColum) == C.joinColumn - * - * Because we cannot reverse the function f() applied to the second table B in all cases, - * we cannot relate C.joinColumn to A.joinColumn, and we would not generate a correlation for C.joinColumn - * - * @param joinableClauses List of joinable clauses for the query - * @param tablePrefix Prefix for a join table - * @param rhsRewriteCandidate RHS rewrite candidate that we find correlated base table columns for - * @param equiConditions Map of equiconditions, keyed by the right hand columns - * - * @return A list of correlatation analyses for the equicondition RHS columns that reside in the table associated with - * the tablePrefix - */ - private static Optional> findCorrelatedBaseTableColumns( - JoinableClauses joinableClauses, - String tablePrefix, - RhsRewriteCandidate rhsRewriteCandidate, - Equiconditions equiConditions - ) - { - JoinableClause clauseForTablePrefix = rhsRewriteCandidate.getJoinableClause(); - JoinConditionAnalysis jca = clauseForTablePrefix.getCondition(); - - Set rhsColumns = new HashSet<>(); - if (rhsRewriteCandidate.isDirectRewrite()) { - // If we filter on a RHS join column, we only need to consider that column from the RHS side - rhsColumns.add(rhsRewriteCandidate.getRhsColumn()); - } else { - for (Equality eq : jca.getEquiConditions()) { - rhsColumns.add(tablePrefix + eq.getRightColumn()); - } - } - - Map correlations = new LinkedHashMap<>(); - - for (String rhsColumn : rhsColumns) { - Set correlatedBaseColumns = new HashSet<>(); - Set correlatedBaseExpressions = new HashSet<>(); - - getCorrelationForRHSColumn( - joinableClauses, - equiConditions, - rhsColumn, - correlatedBaseColumns, - correlatedBaseExpressions - ); - - if (correlatedBaseColumns.isEmpty() && correlatedBaseExpressions.isEmpty()) { - continue; - } - - correlations.put( - rhsColumn, - new JoinFilterColumnCorrelationAnalysis( - rhsColumn, - correlatedBaseColumns, - correlatedBaseExpressions - ) - ); - } - - if (correlations.size() == 0) { - return Optional.empty(); - } else { - return Optional.of(correlations); - } - } - - /** - * Helper method for {@link #findCorrelatedBaseTableColumns} that determines correlated base table columns - * and/or expressions for a single RHS column and adds them to the provided sets as it traverses the - * equicondition column relationships. - * - * @param equiConditions Map of equiconditions, keyed by the right hand columns - * @param rhsColumn RHS column to find base table correlations for - * @param correlatedBaseColumns Set of correlated base column names for the provided RHS column. Will be modified. - * @param correlatedBaseExpressions Set of correlated base column expressions for the provided RHS column. Will be - * modified. - */ - private static void getCorrelationForRHSColumn( - JoinableClauses joinableClauses, - Equiconditions equiConditions, - String rhsColumn, - Set correlatedBaseColumns, - Set correlatedBaseExpressions - ) - { - String findMappingFor = rhsColumn; - Set lhsExprs = equiConditions.getLhsExprs(findMappingFor); - if (lhsExprs == null) { - return; - } - - for (Expr lhsExpr : lhsExprs) { - String identifier = lhsExpr.getBindingIfIdentifier(); - if (identifier == null) { - // We push down if the function only requires base table columns - Expr.BindingDetails bindingDetails = lhsExpr.analyzeInputs(); - Set requiredBindings = bindingDetails.getRequiredBindings(); - - if (joinableClauses.areSomeColumnsFromJoin(requiredBindings)) { - break; - } - correlatedBaseExpressions.add(lhsExpr); - } else { - // simple identifier, see if we can correlate it with a column on the base table - findMappingFor = identifier; - if (joinableClauses.getColumnFromJoinIfExists(identifier) == null) { - correlatedBaseColumns.add(findMappingFor); - } else { - getCorrelationForRHSColumn( - joinableClauses, - equiConditions, - findMappingFor, - correlatedBaseColumns, - correlatedBaseExpressions - ); - } - } - } - } - - /** - * Given a list of JoinFilterColumnCorrelationAnalysis, prune the list so that we only have one - * JoinFilterColumnCorrelationAnalysis for each unique combination of base columns. - * - * Suppose we have a join condition like the following, where A is the base table: - * A.joinColumn == B.joinColumn && A.joinColumn == B.joinColumn2 - * - * We only need to consider one correlation to A.joinColumn since B.joinColumn and B.joinColumn2 must - * have the same value in any row that matches the join condition. - * - * In the future this method could consider which column correlation should be preserved based on availability of - * indices and other heuristics. - * - * When push down of filters with LHS expressions in the join condition is supported, this method should also - * consider expressions. - * - * @param originalList Original list of column correlation analyses. - * - * @return Pruned list of column correlation analyses. - */ - private static List eliminateCorrelationDuplicates( - List originalList - ) - { - Map, JoinFilterColumnCorrelationAnalysis> uniquesMap = new HashMap<>(); - - for (JoinFilterColumnCorrelationAnalysis jca : originalList) { - Set mapKey = new HashSet<>(jca.getBaseColumns()); - for (Expr expr : jca.getBaseExpressions()) { - mapKey.add(expr.stringify()); - } - - uniquesMap.put(mapKey, jca); - } - - return new ArrayList<>(uniquesMap.values()); - } - private static boolean isColumnFromPostJoinVirtualColumns( List postJoinVirtualColumns, String column diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterCorrelations.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterCorrelations.java new file mode 100644 index 000000000000..84fbccd8a572 --- /dev/null +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterCorrelations.java @@ -0,0 +1,407 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.join.filter; + +import org.apache.druid.java.util.common.Pair; +import org.apache.druid.math.expr.Expr; +import org.apache.druid.query.filter.Filter; +import org.apache.druid.segment.join.Equality; +import org.apache.druid.segment.join.JoinConditionAnalysis; +import org.apache.druid.segment.join.JoinableClause; +import org.apache.druid.segment.join.filter.rewrite.RhsRewriteCandidate; +import org.apache.druid.segment.join.filter.rewrite.RhsRewriteCandidates; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * A wrapper class for correlation analyses of different filters involved in the query. It contains: + * + * - A mapping of RHS filtering columns -> List, used for filter rewrites + * - A second mapping of RHS filtering columns -> List, used for direct filter rewrites + */ +public class JoinFilterCorrelations +{ + private final Map> correlationsByFilteringColumn; + private final Map> correlationsByDirectFilteringColumn; + + private JoinFilterCorrelations( + Map> correlationsByFilteringColumn, + Map> correlationsByDirectFilteringColumn + ) + { + this.correlationsByFilteringColumn = correlationsByFilteringColumn; + this.correlationsByDirectFilteringColumn = correlationsByDirectFilteringColumn; + } + + public Map> getCorrelationsByFilteringColumn() + { + return correlationsByFilteringColumn; + } + + public Map> getCorrelationsByDirectFilteringColumn() + { + return correlationsByDirectFilteringColumn; + } + + public static JoinFilterCorrelations computeJoinFilterCorrelations( + List normalizedJoinTableClauses, + Equiconditions equiconditions, + JoinableClauses joinableClauses, + boolean enableRewriteValueColumnFilters, + long filterRewriteMaxSize + ) + { + RhsRewriteCandidates rhsRewriteCandidates = + RhsRewriteCandidates.getRhsRewriteCandidates(normalizedJoinTableClauses, equiconditions, joinableClauses); + + // Build a map of RHS table prefix -> JoinFilterColumnCorrelationAnalysis based on the RHS rewrite candidates + Map>> correlationsByPrefix = new HashMap<>(); + Map> directRewriteCorrelations = new HashMap<>(); + for (RhsRewriteCandidate rhsRewriteCandidate : rhsRewriteCandidates.getRhsRewriteCandidates()) { + if (rhsRewriteCandidate.isDirectRewrite()) { + directRewriteCorrelations.computeIfAbsent( + rhsRewriteCandidate.getRhsColumn(), + c -> { + Optional> correlatedBaseTableColumns = + findCorrelatedBaseTableColumns( + joinableClauses, + c, + rhsRewriteCandidate, + equiconditions + ); + if (!correlatedBaseTableColumns.isPresent()) { + return Optional.empty(); + } else { + JoinFilterColumnCorrelationAnalysis baseColumnAnalysis = correlatedBaseTableColumns.get().get(c); + // for direct rewrites, there will only be one analysis keyed by the RHS column + assert (baseColumnAnalysis != null); + return Optional.of(correlatedBaseTableColumns.get().get(c)); + } + } + ); + } else { + correlationsByPrefix.computeIfAbsent( + rhsRewriteCandidate.getJoinableClause().getPrefix(), + p -> findCorrelatedBaseTableColumns( + joinableClauses, + p, + rhsRewriteCandidate, + equiconditions + ) + ); + } + } + + // Using the RHS table prefix -> JoinFilterColumnCorrelationAnalysis created in the previous step, + // build a map of rhsFilterColumn -> Pair(rhsFilterColumn, rhsFilterValue) -> correlatedValues for specific filter pair + // The Pair(rhsFilterColumn, rhsFilterValue) -> correlatedValues mappings are stored in the + // JoinFilterColumnCorrelationAnalysis objects, which are shared across all rhsFilterColumn entries that belong + // to the same RHS table. + // + // The value is a List instead of a single value because a table can be joined + // to another via multiple columns. + // (See JoinFilterAnalyzerTest.test_filterPushDown_factToRegionOneColumnToTwoRHSColumnsAndFilterOnRHS for an example) + Map> correlationsByFilteringColumn = new LinkedHashMap<>(); + Map> correlationsByDirectFilteringColumn = new LinkedHashMap<>(); + for (RhsRewriteCandidate rhsRewriteCandidate : rhsRewriteCandidates.getRhsRewriteCandidates()) { + if (rhsRewriteCandidate.isDirectRewrite()) { + List perColumnCorrelations = + correlationsByDirectFilteringColumn.computeIfAbsent( + rhsRewriteCandidate.getRhsColumn(), + (rhsCol) -> new ArrayList<>() + ); + perColumnCorrelations.add( + directRewriteCorrelations.get(rhsRewriteCandidate.getRhsColumn()).get() + ); + continue; + } + + Optional> correlationsForPrefix = correlationsByPrefix.get( + rhsRewriteCandidate.getJoinableClause().getPrefix() + ); + if (correlationsForPrefix.isPresent()) { + for (Map.Entry correlationForPrefix : correlationsForPrefix.get() + .entrySet()) { + List perColumnCorrelations = + correlationsByFilteringColumn.computeIfAbsent( + rhsRewriteCandidate.getRhsColumn(), + (rhsCol) -> new ArrayList<>() + ); + perColumnCorrelations.add(correlationForPrefix.getValue()); + correlationForPrefix.getValue().getCorrelatedValuesMap().computeIfAbsent( + Pair.of(rhsRewriteCandidate.getRhsColumn(), rhsRewriteCandidate.getValueForRewrite()), + (rhsVal) -> { + Optional> correlatedValues = getCorrelatedValuesForPushDown( + rhsRewriteCandidate.getRhsColumn(), + rhsRewriteCandidate.getValueForRewrite(), + correlationForPrefix.getValue().getJoinColumn(), + rhsRewriteCandidate.getJoinableClause(), + enableRewriteValueColumnFilters, + filterRewriteMaxSize + ); + return correlatedValues; + } + ); + } + } else { + correlationsByFilteringColumn.put(rhsRewriteCandidate.getRhsColumn(), null); + } + } + + // Go through each per-column analysis list and prune duplicates + for (Map.Entry> correlation : correlationsByFilteringColumn + .entrySet()) { + if (correlation.getValue() != null) { + List dedupList = eliminateCorrelationDuplicates( + correlation.getValue() + ); + correlationsByFilteringColumn.put(correlation.getKey(), dedupList); + } + } + for (Map.Entry> correlation : correlationsByDirectFilteringColumn + .entrySet()) { + if (correlation.getValue() != null) { + List dedupList = eliminateCorrelationDuplicates( + correlation.getValue() + ); + correlationsByDirectFilteringColumn.put(correlation.getKey(), dedupList); + } + } + return new JoinFilterCorrelations(correlationsByFilteringColumn, correlationsByDirectFilteringColumn); + } + + + /** + * Given a list of JoinFilterColumnCorrelationAnalysis, prune the list so that we only have one + * JoinFilterColumnCorrelationAnalysis for each unique combination of base columns. + *

+ * Suppose we have a join condition like the following, where A is the base table: + * A.joinColumn == B.joinColumn && A.joinColumn == B.joinColumn2 + *

+ * We only need to consider one correlation to A.joinColumn since B.joinColumn and B.joinColumn2 must + * have the same value in any row that matches the join condition. + *

+ * In the future this method could consider which column correlation should be preserved based on availability of + * indices and other heuristics. + *

+ * When push down of filters with LHS expressions in the join condition is supported, this method should also + * consider expressions. + * + * @param originalList Original list of column correlation analyses. + * @return Pruned list of column correlation analyses. + */ + private static List eliminateCorrelationDuplicates( + List originalList + ) + { + Map, JoinFilterColumnCorrelationAnalysis> uniquesMap = new HashMap<>(); + + for (JoinFilterColumnCorrelationAnalysis jca : originalList) { + Set mapKey = new HashSet<>(jca.getBaseColumns()); + for (Expr expr : jca.getBaseExpressions()) { + mapKey.add(expr.stringify()); + } + + uniquesMap.put(mapKey, jca); + } + + return new ArrayList<>(uniquesMap.values()); + } + + + /** + * Helper method for rewriting filters on join table columns into filters on base table columns. + * + * @param filterColumn A join table column that we're filtering on + * @param filterValue The value to filter on + * @param correlatedJoinColumn A join table column that appears as the RHS of an equicondition, which we can correlate + * with a column on the base table + * @param clauseForFilteredTable The joinable clause that corresponds to the join table being filtered on + * @return A list of values of the correlatedJoinColumn that appear in rows where filterColumn = filterValue + * Returns absent if we cannot determine the correlated values. + */ + private static Optional> getCorrelatedValuesForPushDown( + String filterColumn, + String filterValue, + String correlatedJoinColumn, + JoinableClause clauseForFilteredTable, + boolean enableRewriteValueColumnFilters, + long filterRewriteMaxSize + ) + { + String filterColumnNoPrefix = filterColumn.substring(clauseForFilteredTable.getPrefix().length()); + String correlatedColumnNoPrefix = correlatedJoinColumn.substring(clauseForFilteredTable.getPrefix().length()); + + return clauseForFilteredTable.getJoinable().getCorrelatedColumnValues( + filterColumnNoPrefix, + filterValue, + correlatedColumnNoPrefix, + filterRewriteMaxSize, + enableRewriteValueColumnFilters + ); + } + + /** + * For each rhs column that appears in the equiconditions for a table's JoinableClause, + * we try to determine what base table columns are related to the rhs column through the total set of equiconditions. + * We do this by searching backwards through the chain of join equiconditions using the provided equicondition map. + *

+ * For example, suppose we have 3 tables, A,B,C, joined with the following conditions, where A is the base table: + * A.joinColumn == B.joinColumn + * B.joinColum == C.joinColumnenableRewriteValueColumnFilters + *

+ * We would determine that C.joinColumn is correlated with A.joinColumn: we first see that + * C.joinColumn is linked to B.joinColumn which in turn is linked to A.joinColumn + *

+ * Suppose we had the following join conditions instead: + * f(A.joinColumn) == B.joinColumn + * B.joinColum == C.joinColumn + * In this case, the JoinFilterColumnCorrelationAnalysis for C.joinColumn would be linked to f(A.joinColumn). + *

+ * Suppose we had the following join conditions instead: + * A.joinColumn == B.joinColumn + * f(B.joinColum) == C.joinColumn + *

+ * Because we cannot reverse the function f() applied to the second table B in all cases, + * we cannot relate C.joinColumn to A.joinColumn, and we would not generate a correlation for C.joinColumn + * + * @param joinableClauses List of joinable clauses for the query + * @param tablePrefix Prefix for a join table + * @param rhsRewriteCandidate RHS rewrite candidate that we find correlated base table columns for + * @param equiConditions Map of equiconditions, keyed by the right hand columns + * @return A list of correlatation analyses for the equicondition RHS columns that reside in the table associated with + * the tablePrefix + */ + private static Optional> findCorrelatedBaseTableColumns( + JoinableClauses joinableClauses, + String tablePrefix, + RhsRewriteCandidate rhsRewriteCandidate, + Equiconditions equiConditions + ) + { + JoinableClause clauseForTablePrefix = rhsRewriteCandidate.getJoinableClause(); + JoinConditionAnalysis jca = clauseForTablePrefix.getCondition(); + + Set rhsColumns = new HashSet<>(); + if (rhsRewriteCandidate.isDirectRewrite()) { + // If we filter on a RHS join column, we only need to consider that column from the RHS side + rhsColumns.add(rhsRewriteCandidate.getRhsColumn()); + } else { + for (Equality eq : jca.getEquiConditions()) { + rhsColumns.add(tablePrefix + eq.getRightColumn()); + } + } + + Map correlations = new LinkedHashMap<>(); + + for (String rhsColumn : rhsColumns) { + Set correlatedBaseColumns = new HashSet<>(); + Set correlatedBaseExpressions = new HashSet<>(); + + getCorrelationForRHSColumn( + joinableClauses, + equiConditions, + rhsColumn, + correlatedBaseColumns, + correlatedBaseExpressions + ); + + if (correlatedBaseColumns.isEmpty() && correlatedBaseExpressions.isEmpty()) { + continue; + } + + correlations.put( + rhsColumn, + new JoinFilterColumnCorrelationAnalysis( + rhsColumn, + correlatedBaseColumns, + correlatedBaseExpressions + ) + ); + } + + if (correlations.size() == 0) { + return Optional.empty(); + } else { + return Optional.of(correlations); + } + } + + /** + * Helper method for {@link #findCorrelatedBaseTableColumns} that determines correlated base table columns + * and/or expressions for a single RHS column and adds them to the provided sets as it traverses the + * equicondition column relationships. + * + * @param equiConditions Map of equiconditions, keyed by the right hand columns + * @param rhsColumn RHS column to find base table correlations for + * @param correlatedBaseColumns Set of correlated base column names for the provided RHS column. Will be modified. + * @param correlatedBaseExpressions Set of correlated base column expressions for the provided RHS column. Will be + * modified. + */ + private static void getCorrelationForRHSColumn( + JoinableClauses joinableClauses, + Equiconditions equiConditions, + String rhsColumn, + Set correlatedBaseColumns, + Set correlatedBaseExpressions + ) + { + String findMappingFor = rhsColumn; + Set lhsExprs = equiConditions.getLhsExprs(findMappingFor); + if (lhsExprs == null) { + return; + } + + for (Expr lhsExpr : lhsExprs) { + String identifier = lhsExpr.getBindingIfIdentifier(); + if (identifier == null) { + // We push down if the function only requires base table columns + Expr.BindingDetails bindingDetails = lhsExpr.analyzeInputs(); + Set requiredBindings = bindingDetails.getRequiredBindings(); + + if (joinableClauses.areSomeColumnsFromJoin(requiredBindings)) { + break; + } + correlatedBaseExpressions.add(lhsExpr); + } else { + // simple identifier, see if we can correlate it with a column on the base table + findMappingFor = identifier; + if (joinableClauses.getColumnFromJoinIfExists(identifier) == null) { + correlatedBaseColumns.add(findMappingFor); + } else { + getCorrelationForRHSColumn( + joinableClauses, + equiConditions, + findMappingFor, + correlatedBaseColumns, + correlatedBaseExpressions + ); + } + } + } + } +} diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java index 5b3217cbd2ff..ae3731db77a2 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java @@ -41,8 +41,6 @@ * - The query's original filter (if any) * - A list of filter clauses from the original filter's CNF representation that only reference the base table * - A list of filter clauses from the original filter's CNF representation that reference RHS join tables - * - A mapping of RHS filtering columns -> List, used for filter rewrites - * - A second mapping of RHS filtering columns -> List, used for direct filter rewrites * - A list of virtual columns that can only be computed post-join * - Control flag booleans for whether filter push down and RHS rewrites are enabled. */ @@ -52,8 +50,7 @@ public class JoinFilterPreAnalysis private final Filter originalFilter; private final List normalizedBaseTableClauses; private final List normalizedJoinTableClauses; - private final Map> correlationsByFilteringColumn; - private final Map> correlationsByDirectFilteringColumn; + private final JoinFilterCorrelations correlations; private final boolean enableFilterPushDown; private final boolean enableFilterRewrite; private final List postJoinVirtualColumns; @@ -65,8 +62,7 @@ private JoinFilterPreAnalysis( final List postJoinVirtualColumns, final List normalizedBaseTableClauses, final List normalizedJoinTableClauses, - final Map> correlationsByFilteringColumn, - final Map> correlationsByDirectFilteringColumn, + JoinFilterCorrelations correlations, final boolean enableFilterPushDown, final boolean enableFilterRewrite, final Equiconditions equiconditions @@ -77,8 +73,7 @@ private JoinFilterPreAnalysis( this.postJoinVirtualColumns = postJoinVirtualColumns; this.normalizedBaseTableClauses = normalizedBaseTableClauses; this.normalizedJoinTableClauses = normalizedJoinTableClauses; - this.correlationsByFilteringColumn = correlationsByFilteringColumn; - this.correlationsByDirectFilteringColumn = correlationsByDirectFilteringColumn; + this.correlations = correlations; this.enableFilterPushDown = enableFilterPushDown; this.enableFilterRewrite = enableFilterRewrite; this.equiconditions = equiconditions; @@ -111,12 +106,12 @@ public List getNormalizedJoinTableClauses() public Map> getCorrelationsByFilteringColumn() { - return correlationsByFilteringColumn; + return correlations.getCorrelationsByFilteringColumn(); } public Map> getCorrelationsByDirectFilteringColumn() { - return correlationsByDirectFilteringColumn; + return correlations.getCorrelationsByDirectFilteringColumn(); } public boolean isEnableFilterPushDown() @@ -143,8 +138,7 @@ public static class Builder @Nullable private final Filter originalFilter; @Nullable private List normalizedBaseTableClauses; @Nullable private List normalizedJoinTableClauses; - @Nullable private Map> correlationsByFilteringColumn; - @Nullable private Map> correlationsByDirectFilteringColumn; + @Nullable private JoinFilterCorrelations correlations; private boolean enableFilterPushDown = false; private boolean enableFilterRewrite = false; @Nonnull private final List postJoinVirtualColumns; @@ -173,19 +167,11 @@ public Builder withNormalizedJoinTableClauses(List normalizedJoinTableCl return this; } - public Builder withCorrelationsByFilteringColumn( - Map> correlationsByFilteringColumn + public Builder withCorrelations( + JoinFilterCorrelations correlations ) { - this.correlationsByFilteringColumn = correlationsByFilteringColumn; - return this; - } - - public Builder withCorrelationsByDirectFilteringColumn( - Map> correlationsByDirectFilteringColumn - ) - { - this.correlationsByDirectFilteringColumn = correlationsByDirectFilteringColumn; + this.correlations = correlations; return this; } @@ -225,8 +211,7 @@ public JoinFilterPreAnalysis build() postJoinVirtualColumns, normalizedBaseTableClauses, normalizedJoinTableClauses, - correlationsByFilteringColumn, - correlationsByDirectFilteringColumn, + correlations, enableFilterPushDown, enableFilterRewrite, equiconditions diff --git a/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinable.java b/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinable.java index 353808dd6b33..7fae4e0db3b0 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinable.java +++ b/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinable.java @@ -33,6 +33,7 @@ import javax.annotation.Nullable; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.Set; public class LookupJoinable implements Joinable @@ -88,7 +89,7 @@ public JoinMatcher makeJoinMatcher( } @Override - public Set getCorrelatedColumnValues( + public Optional> getCorrelatedColumnValues( String searchColumnName, String searchColumnValue, String retrievalColumnName, @@ -97,7 +98,7 @@ public Set getCorrelatedColumnValues( ) { if (!ALL_COLUMNS.contains(searchColumnName) || !ALL_COLUMNS.contains(retrievalColumnName)) { - return ImmutableSet.of(); + return Optional.empty(); } Set correlatedValues; if (LookupColumnSelectorFactory.KEY_COLUMN.equals(searchColumnName)) { @@ -109,7 +110,7 @@ public Set getCorrelatedColumnValues( } } else { if (!allowNonKeyColumnSearch) { - return ImmutableSet.of(); + return Optional.empty(); } if (LookupColumnSelectorFactory.VALUE_COLUMN.equals(retrievalColumnName)) { // This should not happen in practice because the column to be joined on must be a key. @@ -120,6 +121,6 @@ public Set getCorrelatedColumnValues( correlatedValues = ImmutableSet.copyOf(extractor.unapply(searchColumnValue)); } } - return correlatedValues; + return Optional.of(correlatedValues); } } diff --git a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinable.java b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinable.java index a661b5ad21ce..66ef1213d6f5 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinable.java +++ b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinable.java @@ -19,7 +19,6 @@ package org.apache.druid.segment.join.table; -import com.google.common.collect.ImmutableSet; import it.unimi.dsi.fastutil.ints.IntList; import org.apache.druid.segment.ColumnSelectorFactory; import org.apache.druid.segment.column.ColumnCapabilities; @@ -31,6 +30,7 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; public class IndexedTableJoinable implements Joinable @@ -82,7 +82,7 @@ public JoinMatcher makeJoinMatcher( } @Override - public Set getCorrelatedColumnValues( + public Optional> getCorrelatedColumnValues( String searchColumnName, String searchColumnValue, String retrievalColumnName, @@ -94,7 +94,7 @@ public Set getCorrelatedColumnValues( int correlatedColumnPosition = table.rowSignature().indexOf(retrievalColumnName); if (filterColumnPosition < 0 || correlatedColumnPosition < 0) { - return ImmutableSet.of(); + return Optional.empty(); } Set correlatedValues = new HashSet<>(); @@ -108,13 +108,13 @@ public Set getCorrelatedColumnValues( correlatedValues.add(correlatedDimVal); if (correlatedValues.size() > maxCorrelationSetSize) { - return ImmutableSet.of(); + return Optional.empty(); } } - return correlatedValues; + return Optional.of(correlatedValues); } else { if (!allowNonKeyColumnSearch) { - return ImmutableSet.of(); + return Optional.empty(); } IndexedTable.Reader dimNameReader = table.columnReader(filterColumnPosition); @@ -125,12 +125,12 @@ public Set getCorrelatedColumnValues( String correlatedDimVal = Objects.toString(correlatedColumnReader.read(i), null); correlatedValues.add(correlatedDimVal); if (correlatedValues.size() > maxCorrelationSetSize) { - return ImmutableSet.of(); + return Optional.empty(); } } } - return correlatedValues; + return Optional.of(correlatedValues); } } } diff --git a/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java b/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java index e1e480be54ed..965dd2302f11 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java @@ -39,6 +39,7 @@ import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.filter.AndFilter; import org.apache.druid.segment.filter.BoundFilter; +import org.apache.druid.segment.filter.FalseFilter; import org.apache.druid.segment.filter.OrFilter; import org.apache.druid.segment.filter.SelectorFilter; import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; @@ -1262,6 +1263,60 @@ public void test_filterPushDown_factToCountryRightWithFilterOnNullColumns() ); } + @Test + public void test_filterPushDown_factToCountryRightWithFilterOnValueThatMatchesNothing() + { + List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.RIGHT)); + Filter originalFilter = new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", null), + new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", "NO MATCH") + ) + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + joinableClauses, + originalFilter + ); + HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( + factSegment.asStorageAdapter(), + joinableClauses, + joinFilterPreAnalysis + ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + FalseFilter.instance(), + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", null), + new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", "NO MATCH") + ) + ), + ImmutableSet.of() + ); + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); + + JoinTestHelper.verifyCursors( + adapter.makeCursors( + originalFilter, + Intervals.ETERNITY, + VirtualColumns.EMPTY, + Granularities.ALL, + false, + null + ), + ImmutableList.of( + "page", + "countryIsoCode", + "countryNumber", + FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryIsoCode", + FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", + FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryNumber" + ), + ImmutableList.of() + ); + } + @Test public void test_filterPushDown_factToCountryRightWithFilterOnNullColumnsUsingLookup() { diff --git a/processing/src/test/java/org/apache/druid/segment/join/lookup/LookupJoinableTest.java b/processing/src/test/java/org/apache/druid/segment/join/lookup/LookupJoinableTest.java index 4115b84acec2..2037f7763172 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/lookup/LookupJoinableTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/lookup/LookupJoinableTest.java @@ -36,6 +36,7 @@ import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.Set; @RunWith(MockitoJUnitRunner.class) @@ -117,7 +118,7 @@ public void getColumnCapabilitiesForUnknownColumnShouldReturnNull() @Test public void getCorrelatedColummnValuesMissingSearchColumnShouldReturnEmptySet() { - Set correlatedValues = + Optional> correlatedValues = target.getCorrelatedColumnValues( UNKNOWN_COLUMN, SEARCH_KEY_VALUE, @@ -125,13 +126,13 @@ public void getCorrelatedColummnValuesMissingSearchColumnShouldReturnEmptySet() 0, false); - Assert.assertEquals(Collections.emptySet(), correlatedValues); + Assert.assertFalse(correlatedValues.isPresent()); } @Test public void getCorrelatedColummnValuesMissingRetrievalColumnShouldReturnEmptySet() { - Set correlatedValues = + Optional> correlatedValues = target.getCorrelatedColumnValues( LookupColumnSelectorFactory.KEY_COLUMN, SEARCH_KEY_VALUE, @@ -139,85 +140,85 @@ public void getCorrelatedColummnValuesMissingRetrievalColumnShouldReturnEmptySet 0, false); - Assert.assertEquals(Collections.emptySet(), correlatedValues); + Assert.assertFalse(correlatedValues.isPresent()); } @Test public void getCorrelatedColumnValuesForSearchKeyAndRetrieveKeyColumnShouldReturnSearchValue() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( LookupColumnSelectorFactory.KEY_COLUMN, SEARCH_KEY_VALUE, LookupColumnSelectorFactory.KEY_COLUMN, 0, false); - Assert.assertEquals(ImmutableSet.of(SEARCH_KEY_VALUE), correlatedValues); + Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_KEY_VALUE)), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchKeyAndRetrieveValueColumnShouldReturnExtractedValue() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( LookupColumnSelectorFactory.KEY_COLUMN, SEARCH_KEY_VALUE, LookupColumnSelectorFactory.VALUE_COLUMN, 0, false); - Assert.assertEquals(ImmutableSet.of(SEARCH_VALUE_VALUE), correlatedValues); + Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_VALUE_VALUE)), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchKeyMissingAndRetrieveValueColumnShouldReturnExtractedValue() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( LookupColumnSelectorFactory.KEY_COLUMN, SEARCH_KEY_NULL_VALUE, LookupColumnSelectorFactory.VALUE_COLUMN, 0, false); - Assert.assertEquals(Collections.singleton(null), correlatedValues); + Assert.assertEquals(Optional.of(Collections.singleton(null)), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchValueAndRetrieveValueColumnAndNonKeyColumnSearchDisabledShouldReturnSearchValue() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( LookupColumnSelectorFactory.VALUE_COLUMN, SEARCH_VALUE_VALUE, LookupColumnSelectorFactory.VALUE_COLUMN, 10, false); - Assert.assertEquals(ImmutableSet.of(), correlatedValues); + Assert.assertEquals(Optional.empty(), correlatedValues); correlatedValues = target.getCorrelatedColumnValues( LookupColumnSelectorFactory.VALUE_COLUMN, SEARCH_VALUE_VALUE, LookupColumnSelectorFactory.KEY_COLUMN, 10, false); - Assert.assertEquals(ImmutableSet.of(), correlatedValues); + Assert.assertEquals(Optional.empty(), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchValueAndRetrieveValueColumnShouldReturnSearchValue() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( LookupColumnSelectorFactory.VALUE_COLUMN, SEARCH_VALUE_VALUE, LookupColumnSelectorFactory.VALUE_COLUMN, 0, true); - Assert.assertEquals(ImmutableSet.of(SEARCH_VALUE_VALUE), correlatedValues); + Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_VALUE_VALUE)), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchValueAndRetrieveKeyColumnShouldReturnUnAppliedValue() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( LookupColumnSelectorFactory.VALUE_COLUMN, SEARCH_VALUE_VALUE, LookupColumnSelectorFactory.KEY_COLUMN, 10, true); - Assert.assertEquals(ImmutableSet.of(SEARCH_KEY_VALUE), correlatedValues); + Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_KEY_VALUE)), correlatedValues); } @Test @@ -228,24 +229,24 @@ public void getCorrelatedColumnValuesForSearchValueAndRetrieveKeyColumnShouldRet */ public void getCorrelatedColumnValuesForSearchValueAndRetrieveKeyColumnWithMaxLimitSetShouldHonorMaxLimit() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( LookupColumnSelectorFactory.VALUE_COLUMN, SEARCH_VALUE_VALUE, LookupColumnSelectorFactory.KEY_COLUMN, 0, true); - Assert.assertEquals(ImmutableSet.of(), correlatedValues); + Assert.assertEquals(Optional.empty(), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchUnknownValueAndRetrieveKeyColumnShouldReturnNoCorrelatedValues() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( LookupColumnSelectorFactory.VALUE_COLUMN, SEARCH_VALUE_UNKNOWN, LookupColumnSelectorFactory.KEY_COLUMN, 10, true); - Assert.assertEquals(ImmutableSet.of(), correlatedValues); + Assert.assertEquals(Optional.of(ImmutableSet.of()), correlatedValues); } } diff --git a/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java b/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java index 89f78dc523d2..20cfdb786fb0 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java @@ -40,6 +40,7 @@ import org.junit.Test; import java.util.Collections; +import java.util.Optional; import java.util.Set; public class IndexedTableJoinableTest @@ -190,9 +191,9 @@ public void makeJoinMatcherWithDimensionSelectorOnString() } @Test - public void getCorrelatedColummnValuesMissingSearchColumnShouldReturnEmptySet() + public void getCorrelatedColummnValuesMissingSearchColumnShouldReturnEmpty() { - Set correlatedValues = + Optional> correlatedValues = target.getCorrelatedColumnValues( UNKNOWN_COLUMN, "foo", @@ -200,13 +201,13 @@ public void getCorrelatedColummnValuesMissingSearchColumnShouldReturnEmptySet() MAX_CORRELATION_SET_SIZE, false); - Assert.assertEquals(Collections.emptySet(), correlatedValues); + Assert.assertEquals(Optional.empty(), correlatedValues); } @Test - public void getCorrelatedColummnValuesMissingRetrievalColumnShouldReturnEmptySet() + public void getCorrelatedColummnValuesMissingRetrievalColumnShouldReturnEmpty() { - Set correlatedValues = + Optional> correlatedValues = target.getCorrelatedColumnValues( KEY_COLUMN, "foo", @@ -214,121 +215,121 @@ public void getCorrelatedColummnValuesMissingRetrievalColumnShouldReturnEmptySet MAX_CORRELATION_SET_SIZE, false); - Assert.assertEquals(Collections.emptySet(), correlatedValues); + Assert.assertEquals(Optional.empty(), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchKeyAndRetrieveKeyColumnShouldReturnSearchValue() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( KEY_COLUMN, SEARCH_KEY_VALUE, KEY_COLUMN, MAX_CORRELATION_SET_SIZE, false); - Assert.assertEquals(ImmutableSet.of(SEARCH_KEY_VALUE), correlatedValues); + Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_KEY_VALUE)), correlatedValues); } @Test - public void getCorrelatedColumnValuesForSearchKeyAndRetrieveKeyColumnAboveLimitShouldReturnEmptySet() + public void getCorrelatedColumnValuesForSearchKeyAndRetrieveKeyColumnAboveLimitShouldReturnEmpty() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( KEY_COLUMN, SEARCH_KEY_VALUE, KEY_COLUMN, 0, false); - Assert.assertEquals(ImmutableSet.of(), correlatedValues); + Assert.assertEquals(Optional.empty(), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchKeyAndRetrieveValueColumnShouldReturnExtractedValue() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( KEY_COLUMN, SEARCH_KEY_VALUE, VALUE_COLUMN, MAX_CORRELATION_SET_SIZE, false); - Assert.assertEquals(ImmutableSet.of(SEARCH_VALUE_VALUE), correlatedValues); + Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_VALUE_VALUE)), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchKeyMissingAndRetrieveValueColumnShouldReturnExtractedValue() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( KEY_COLUMN, SEARCH_KEY_NULL_VALUE, VALUE_COLUMN, MAX_CORRELATION_SET_SIZE, false); - Assert.assertEquals(Collections.singleton(null), correlatedValues); + Assert.assertEquals(Optional.of(Collections.singleton(null)), correlatedValues); } @Test - public void getCorrelatedColumnValuesForSearchValueAndRetrieveValueColumnAndNonKeyColumnSearchDisabledShouldReturnSearchValue() + public void getCorrelatedColumnValuesForSearchValueAndRetrieveValueColumnAndNonKeyColumnSearchDisabledShouldReturnEmpty() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( VALUE_COLUMN, SEARCH_VALUE_VALUE, VALUE_COLUMN, MAX_CORRELATION_SET_SIZE, false); - Assert.assertEquals(ImmutableSet.of(), correlatedValues); + Assert.assertEquals(Optional.empty(), correlatedValues); correlatedValues = target.getCorrelatedColumnValues( VALUE_COLUMN, SEARCH_VALUE_VALUE, KEY_COLUMN, 10, false); - Assert.assertEquals(ImmutableSet.of(), correlatedValues); + Assert.assertEquals(Optional.empty(), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchValueAndRetrieveValueColumnShouldReturnSearchValue() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( VALUE_COLUMN, SEARCH_VALUE_VALUE, VALUE_COLUMN, MAX_CORRELATION_SET_SIZE, true); - Assert.assertEquals(ImmutableSet.of(SEARCH_VALUE_VALUE), correlatedValues); + Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_VALUE_VALUE)), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchValueAndRetrieveKeyColumnShouldReturnUnAppliedValue() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( VALUE_COLUMN, SEARCH_VALUE_VALUE, KEY_COLUMN, 10, true); - Assert.assertEquals(ImmutableSet.of(SEARCH_KEY_VALUE), correlatedValues); + Assert.assertEquals(Optional.of(ImmutableSet.of(SEARCH_KEY_VALUE)), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchValueAndRetrieveKeyColumnWithMaxLimitSetShouldHonorMaxLimit() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( VALUE_COLUMN, SEARCH_VALUE_VALUE, KEY_COLUMN, 0, true); - Assert.assertEquals(ImmutableSet.of(), correlatedValues); + Assert.assertEquals(Optional.empty(), correlatedValues); } @Test public void getCorrelatedColumnValuesForSearchUnknownValueAndRetrieveKeyColumnShouldReturnNoCorrelatedValues() { - Set correlatedValues = target.getCorrelatedColumnValues( + Optional> correlatedValues = target.getCorrelatedColumnValues( VALUE_COLUMN, SEARCH_VALUE_UNKNOWN, KEY_COLUMN, 10, true); - Assert.assertEquals(ImmutableSet.of(), correlatedValues); + Assert.assertEquals(Optional.of(ImmutableSet.of()), correlatedValues); } } diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java index 39800c1911d6..a12775078bfd 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java @@ -162,7 +162,7 @@ public DateTimeZone getSqlTimeZone() public static final String DUMMY_SQL_ID = "dummy"; public static final String LOS_ANGELES = "America/Los_Angeles"; - static final ImmutableMap.Builder DEFAULT_QUERY_CONTEXT_BUILDER = + private static final ImmutableMap.Builder DEFAULT_QUERY_CONTEXT_BUILDER = ImmutableMap.builder() .put(PlannerContext.CTX_SQL_QUERY_ID, DUMMY_SQL_ID) .put(PlannerContext.CTX_SQL_CURRENT_TIMESTAMP, "2000-01-01T00:00:00Z") diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index 80330453aa84..87838e201d8f 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -8085,6 +8085,42 @@ public void testFilterAndGroupByLookup() throws Exception ); } + @Test + @Parameters(source = QueryContextForJoinProvider.class) + public void testFilterAndGroupByLookupUsingJoinOperatorWithValueFilterPushdownMatchesNothig(Map queryContext) throws Exception + { + // Cannot vectorize JOIN operator. + cannotVectorize(); + + testQuery( + "SELECT lookyloo.k, COUNT(*)\n" + + "FROM foo LEFT JOIN lookup.lookyloo ON foo.dim2 = lookyloo.k\n" + + "WHERE lookyloo.v = '123'\n" + + "GROUP BY lookyloo.k", + queryContext, + ImmutableList.of( + GroupByQuery.builder() + .setDataSource( + join( + new TableDataSource(CalciteTests.DATASOURCE1), + new LookupDataSource("lookyloo"), + "j0.", + equalsCondition(DruidExpression.fromColumn("dim2"), DruidExpression.fromColumn("j0.k")), + JoinType.LEFT + ) + ) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setDimFilter(selector("j0.v", "123", null)) + .setGranularity(Granularities.ALL) + .setDimensions(dimensions(new DefaultDimensionSpec("j0.k", "d0"))) + .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) + .setContext(queryContext) + .build() + ), + ImmutableList.of() + ); + } + @Test @Parameters(source = QueryContextForJoinProvider.class) public void testFilterAndGroupByLookupUsingJoinOperatorAllowNulls(Map queryContext) throws Exception @@ -8217,6 +8253,7 @@ public void testFilterAndGroupByLookupUsingJoinOperator(Map quer { // Cannot vectorize JOIN operator. cannotVectorize(); + testQuery( "SELECT lookyloo.k, COUNT(*)\n" + "FROM foo LEFT JOIN lookup.lookyloo ON foo.dim2 = lookyloo.k\n" From 366db96e5a78f29e933f6fb5303910d19f5b971c Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Fri, 29 May 2020 20:09:34 -0700 Subject: [PATCH 044/107] only close exec if it exists (#9952) --- .../kinesis/KinesisRecordSupplier.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplier.java b/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplier.java index b32ee149f544..65ae1f62ba07 100644 --- a/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplier.java +++ b/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplier.java @@ -560,16 +560,18 @@ public void close() assign(ImmutableSet.of()); - scheduledExec.shutdown(); + if (scheduledExec != null) { + scheduledExec.shutdown(); - try { - if (!scheduledExec.awaitTermination(EXCEPTION_RETRY_DELAY_MS, TimeUnit.MILLISECONDS)) { - scheduledExec.shutdownNow(); + try { + if (!scheduledExec.awaitTermination(EXCEPTION_RETRY_DELAY_MS, TimeUnit.MILLISECONDS)) { + scheduledExec.shutdownNow(); + } + } + catch (InterruptedException e) { + log.warn(e, "InterruptedException while shutting down"); + throw new RuntimeException(e); } - } - catch (InterruptedException e) { - log.warn(e, "InterruptedException while shutting down"); - throw new RuntimeException(e); } this.closed = true; From 1fd734607706263a47e0a28d4410d21d0fa4ed7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20L=C3=A9aut=C3=A9?= Date: Sun, 31 May 2020 09:12:25 -0700 Subject: [PATCH 045/107] fix unsafe concurrent access in StreamAppenderatorDriver (#9943) during segment publishing we do streaming operations on a collection not safe for concurrent modification. To guarantee correct results we must also guard any operations on the stream itself. This may explain the issue seen in https://github.com/apache/druid/issues/9845 --- .../appenderator/BaseAppenderatorDriver.java | 14 +++++++++----- .../appenderator/BatchAppenderatorDriver.java | 5 +---- .../appenderator/StreamAppenderatorDriver.java | 4 +--- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BaseAppenderatorDriver.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BaseAppenderatorDriver.java index 548de50327d0..c1226c72aa81 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BaseAppenderatorDriver.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BaseAppenderatorDriver.java @@ -426,9 +426,9 @@ protected AppenderatorDriverAddResult append( } /** - * Returns a stream of {@link SegmentWithState} for the given sequenceNames. + * Returns a stream of {@link SegmentIdWithShardSpec} for the given sequenceNames. */ - Stream getSegmentWithStates(Collection sequenceNames) + List getSegmentIdsWithShardSpecs(Collection sequenceNames) { synchronized (segments) { return sequenceNames @@ -436,11 +436,13 @@ Stream getSegmentWithStates(Collection sequenceNames) .map(segments::get) .filter(Objects::nonNull) .flatMap(segmentsForSequence -> segmentsForSequence.intervalToSegmentStates.values().stream()) - .flatMap(segmentsOfInterval -> segmentsOfInterval.getAllSegments().stream()); + .flatMap(segmentsOfInterval -> segmentsOfInterval.getAllSegments().stream()) + .map(SegmentWithState::getSegmentIdentifier) + .collect(Collectors.toList()); } } - Stream getAppendingSegments(Collection sequenceNames) + Set getAppendingSegments(Collection sequenceNames) { synchronized (segments) { return sequenceNames @@ -449,7 +451,9 @@ Stream getAppendingSegments(Collection sequenceNames) .filter(Objects::nonNull) .flatMap(segmentsForSequence -> segmentsForSequence.intervalToSegmentStates.values().stream()) .map(segmentsOfInterval -> segmentsOfInterval.appendingSegment) - .filter(Objects::nonNull); + .filter(Objects::nonNull) + .map(SegmentWithState::getSegmentIdentifier) + .collect(Collectors.toSet()); } } diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BatchAppenderatorDriver.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BatchAppenderatorDriver.java index 4f6fd54a18e8..7d1f3e2f393d 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BatchAppenderatorDriver.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BatchAppenderatorDriver.java @@ -136,10 +136,7 @@ private SegmentsAndCommitMetadata pushAndClear( long pushAndClearTimeoutMs ) throws InterruptedException, ExecutionException, TimeoutException { - final Set requestedSegmentIdsForSequences = getAppendingSegments(sequenceNames) - .map(SegmentWithState::getSegmentIdentifier) - .collect(Collectors.toSet()); - + final Set requestedSegmentIdsForSequences = getAppendingSegments(sequenceNames); final ListenableFuture future = ListenableFutures.transformAsync( pushInBackground(null, requestedSegmentIdsForSequences, false), diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/StreamAppenderatorDriver.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/StreamAppenderatorDriver.java index bee2c7dc9932..454ea96f1dcd 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/StreamAppenderatorDriver.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/StreamAppenderatorDriver.java @@ -271,9 +271,7 @@ public ListenableFuture publish( final Collection sequenceNames ) { - final List theSegments = getSegmentWithStates(sequenceNames) - .map(SegmentWithState::getSegmentIdentifier) - .collect(Collectors.toList()); + final List theSegments = getSegmentIdsWithShardSpecs(sequenceNames); final ListenableFuture publishFuture = ListenableFutures.transformAsync( // useUniquePath=true prevents inconsistencies in segment data when task failures or replicas leads to a second From 3da3dee488b3b209d3fd86c8c0eeb902ed147640 Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn <52679095+maytasm@users.noreply.github.com> Date: Mon, 1 Jun 2020 06:39:06 -1000 Subject: [PATCH 046/107] Prevent JOIN reducing to a JOIN with constant in the ON condition (#9941) * Prevent Join reducing to on constant condition * Prevent Join reducing to on constant condition * addreess comments * set queryContext in tests --- .../druid/sql/calcite/planner/Rules.java | 10 +- .../druid/sql/calcite/CalciteQueryTest.java | 347 +++++++++++++++++- 2 files changed, 354 insertions(+), 3 deletions(-) diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/planner/Rules.java b/sql/src/main/java/org/apache/druid/sql/calcite/planner/Rules.java index acc27c71b8df..03b1a31c3689 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/planner/Rules.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/planner/Rules.java @@ -123,14 +123,17 @@ public class Rules ProjectTableScanRule.INTERPRETER ); - // Rules from RelOptUtil's registerReductionRules. + // Rules from RelOptUtil's registerReductionRules, minus: + // + // 1) ReduceExpressionsRule.JOIN_INSTANCE + // Removed by https://github.com/apache/druid/pull/9941 due to issue in https://github.com/apache/druid/issues/9942 + // TODO: Re-enable when https://github.com/apache/druid/issues/9942 is fixed private static final List REDUCTION_RULES = ImmutableList.of( ReduceExpressionsRule.PROJECT_INSTANCE, ReduceExpressionsRule.FILTER_INSTANCE, ReduceExpressionsRule.CALC_INSTANCE, ReduceExpressionsRule.WINDOW_INSTANCE, - ReduceExpressionsRule.JOIN_INSTANCE, ValuesReduceRule.FILTER_INSTANCE, ValuesReduceRule.PROJECT_FILTER_INSTANCE, ValuesReduceRule.PROJECT_INSTANCE, @@ -166,6 +169,9 @@ public class Rules // 2) SemiJoinRule.PROJECT and SemiJoinRule.JOIN (we don't need to detect semi-joins, because they are handled // fine as-is by DruidJoinRule). // 3) JoinCommuteRule (we don't support reordering joins yet). + // 4) FilterJoinRule.FILTER_ON_JOIN and FilterJoinRule.JOIN + // Removed by https://github.com/apache/druid/pull/9773 due to issue in https://github.com/apache/druid/issues/9843 + // TODO: Re-enable when https://github.com/apache/druid/issues/9843 is fixed private static final List ABSTRACT_RELATIONAL_RULES = ImmutableList.of( AbstractConverter.ExpandConversionRule.INSTANCE, diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index 87838e201d8f..94edd222dc67 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableMap; import junitparams.JUnitParamsRunner; import junitparams.Parameters; +import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.runtime.CalciteContextException; import org.apache.calcite.tools.ValidationException; import org.apache.druid.annotations.UsedByJUnitParamsRunner; @@ -79,6 +80,7 @@ import org.apache.druid.query.filter.DimFilter; import org.apache.druid.query.filter.InDimFilter; import org.apache.druid.query.filter.LikeDimFilter; +import org.apache.druid.query.filter.NotDimFilter; import org.apache.druid.query.filter.SelectorDimFilter; import org.apache.druid.query.groupby.GroupByQuery; import org.apache.druid.query.groupby.orderby.DefaultLimitSpec; @@ -9169,7 +9171,7 @@ public void testWhereInSelectNullFromLookup() throws Exception .build() ), "j0.", - "0", + equalsCondition(DruidExpression.fromColumn("dim1"), DruidExpression.fromColumn("j0.d0")), JoinType.INNER ) ) @@ -14077,6 +14079,349 @@ public void testQueryContextOuterLimit() throws Exception ); } + @Test + @Parameters(source = QueryContextForJoinProvider.class) + public void testCountOnSemiJoinSingleColumn(Map queryContext) throws Exception + { + testQuery( + "SELECT dim1 FROM foo WHERE dim1 IN (SELECT dim1 FROM foo WHERE dim1 = '10.1')\n", + queryContext, + ImmutableList.of( + newScanQueryBuilder() + .dataSource( + join( + new TableDataSource(CalciteTests.DATASOURCE1), + new QueryDataSource( + GroupByQuery.builder() + .setDataSource(CalciteTests.DATASOURCE1) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setDimFilter( + selector("dim1", "10.1", null) + ) + .setGranularity(Granularities.ALL) + .setDimensions(dimensions(new DefaultDimensionSpec("dim1", "d0"))) + .setContext(queryContext) + .build() + ), + "j0.", + equalsCondition(DruidExpression.fromColumn("dim1"), DruidExpression.fromColumn("j0.d0")), + JoinType.INNER + ) + ) + .intervals(querySegmentSpec(Filtration.eternity())) + .virtualColumns(expressionVirtualColumn("v0", "\'10.1\'", ValueType.STRING)) + .columns("v0") + .context(queryContext) + .build() + ), + ImmutableList.of( + new Object[]{"10.1"} + ) + ); + } + + @Test + @Parameters(source = QueryContextForJoinProvider.class) + public void testLeftJoinOnTwoInlineDataSourcesWithTimeFilter(Map queryContext) throws Exception + { + testQuery( + "with abc as\n" + + "(\n" + + " SELECT dim1, \"__time\", m1 from foo WHERE \"dim1\" = '10.1' AND \"__time\" >= '1999'\n" + + ")\n" + + "SELECT t1.dim1, t1.\"__time\" from abc as t1 LEFT JOIN abc as t2 on t1.dim1 = t2.dim1 WHERE t1.dim1 = '10.1'\n", + queryContext, + ImmutableList.of( + newScanQueryBuilder() + .dataSource( + join( + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals( + querySegmentSpec( + Intervals.utc( + DateTimes.of("1999-01-01").getMillis(), + JodaUtils.MAX_INSTANT + ) + ) + ) + .filters(new SelectorDimFilter("dim1", "10.1", null)) + .virtualColumns(expressionVirtualColumn("v0", "\'10.1\'", ValueType.STRING)) + .columns(ImmutableList.of("__time", "v0")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals( + querySegmentSpec( + Intervals.utc( + DateTimes.of("1999-01-01").getMillis(), + JodaUtils.MAX_INSTANT + ) + ) + ) + .filters(new SelectorDimFilter("dim1", "10.1", null)) + .virtualColumns(expressionVirtualColumn("v0", "\'10.1\'", ValueType.STRING)) + .columns(ImmutableList.of("v0")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + "j0.", + equalsCondition(DruidExpression.fromColumn("v0"), DruidExpression.fromColumn("j0.v0")), + JoinType.LEFT + ) + ) + .intervals(querySegmentSpec(Filtration.eternity())) + .virtualColumns(expressionVirtualColumn("_v0", "\'10.1\'", ValueType.STRING)) + .columns("__time", "_v0") + .filters(new SelectorDimFilter("v0", "10.1", null)) + .context(queryContext) + .build() + ), + ImmutableList.of( + new Object[]{"10.1", 946771200000L} + ) + ); + } + + @Test + @Parameters(source = QueryContextForJoinProvider.class) + public void testLeftJoinOnTwoInlineDataSourcesWithOuterWhere(Map queryContext) throws Exception + { + testQuery( + "with abc as\n" + + "(\n" + + " SELECT dim1, \"__time\", m1 from foo WHERE \"dim1\" = '10.1'\n" + + ")\n" + + "SELECT t1.dim1, t1.\"__time\" from abc as t1 LEFT JOIN abc as t2 on t1.dim1 = t2.dim1 WHERE t1.dim1 = '10.1'\n", + queryContext, + ImmutableList.of( + newScanQueryBuilder() + .dataSource( + join( + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(new SelectorDimFilter("dim1", "10.1", null)) + .virtualColumns(expressionVirtualColumn("v0", "\'10.1\'", ValueType.STRING)) + .columns(ImmutableList.of("__time", "v0")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(new SelectorDimFilter("dim1", "10.1", null)) + .columns(ImmutableList.of("dim1")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + "j0.", + equalsCondition(DruidExpression.fromColumn("v0"), DruidExpression.fromColumn("j0.dim1")), + JoinType.LEFT + ) + ) + .intervals(querySegmentSpec(Filtration.eternity())) + .virtualColumns(expressionVirtualColumn("_v0", "\'10.1\'", ValueType.STRING)) + .columns("__time", "_v0") + .filters(new SelectorDimFilter("v0", "10.1", null)) + .context(queryContext) + .build() + ), + ImmutableList.of( + new Object[]{"10.1", 946771200000L} + ) + ); + } + + @Test + @Parameters(source = QueryContextForJoinProvider.class) + public void testLeftJoinOnTwoInlineDataSources(Map queryContext) throws Exception + { + testQuery( + "with abc as\n" + + "(\n" + + " SELECT dim1, \"__time\", m1 from foo WHERE \"dim1\" = '10.1'\n" + + ")\n" + + "SELECT t1.dim1, t1.\"__time\" from abc as t1 LEFT JOIN abc as t2 on t1.dim1 = t2.dim1\n", + queryContext, + ImmutableList.of( + newScanQueryBuilder() + .dataSource( + join( + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(new SelectorDimFilter("dim1", "10.1", null)) + .virtualColumns(expressionVirtualColumn("v0", "\'10.1\'", ValueType.STRING)) + .columns(ImmutableList.of("__time", "v0")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(new SelectorDimFilter("dim1", "10.1", null)) + .columns(ImmutableList.of("dim1")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + "j0.", + equalsCondition(DruidExpression.fromColumn("v0"), DruidExpression.fromColumn("j0.dim1")), + JoinType.LEFT + ) + ) + .intervals(querySegmentSpec(Filtration.eternity())) + .virtualColumns(expressionVirtualColumn("_v0", "\'10.1\'", ValueType.STRING)) + .columns("__time", "_v0") + .context(queryContext) + .build() + ), + ImmutableList.of( + new Object[]{"10.1", 946771200000L} + ) + ); + } + + @Test + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinOnTwoInlineDataSourcesWithOuterWhere(Map queryContext) throws Exception + { + testQuery( + "with abc as\n" + + "(\n" + + " SELECT dim1, \"__time\", m1 from foo WHERE \"dim1\" = '10.1'\n" + + ")\n" + + "SELECT t1.dim1, t1.\"__time\" from abc as t1 INNER JOIN abc as t2 on t1.dim1 = t2.dim1 WHERE t1.dim1 = '10.1'\n", + queryContext, + ImmutableList.of( + newScanQueryBuilder() + .dataSource( + join( + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(new SelectorDimFilter("dim1", "10.1", null)) + .virtualColumns(expressionVirtualColumn("v0", "\'10.1\'", ValueType.STRING)) + .columns(ImmutableList.of("__time", "v0")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(new SelectorDimFilter("dim1", "10.1", null)) + .columns(ImmutableList.of("dim1")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + "j0.", + equalsCondition(DruidExpression.fromColumn("v0"), DruidExpression.fromColumn("j0.dim1")), + JoinType.INNER + ) + ) + .intervals(querySegmentSpec(Filtration.eternity())) + .virtualColumns(expressionVirtualColumn("_v0", "\'10.1\'", ValueType.STRING)) + .columns("__time", "_v0") + .filters(new NotDimFilter(new SelectorDimFilter("v0", null, null))) + .context(queryContext) + .build() + ), + ImmutableList.of( + new Object[]{"10.1", 946771200000L} + ) + ); + } + + @Test + @Parameters(source = QueryContextForJoinProvider.class) + public void testInnerJoinOnTwoInlineDataSources(Map queryContext) throws Exception + { + testQuery( + "with abc as\n" + + "(\n" + + " SELECT dim1, \"__time\", m1 from foo WHERE \"dim1\" = '10.1'\n" + + ")\n" + + "SELECT t1.dim1, t1.\"__time\" from abc as t1 INNER JOIN abc as t2 on t1.dim1 = t2.dim1\n", + queryContext, + ImmutableList.of( + newScanQueryBuilder() + .dataSource( + join( + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(new SelectorDimFilter("dim1", "10.1", null)) + .virtualColumns(expressionVirtualColumn("v0", "\'10.1\'", ValueType.STRING)) + .columns(ImmutableList.of("__time", "v0")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(new SelectorDimFilter("dim1", "10.1", null)) + .columns(ImmutableList.of("dim1")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + "j0.", + equalsCondition(DruidExpression.fromColumn("v0"), DruidExpression.fromColumn("j0.dim1")), + JoinType.INNER + ) + ) + .intervals(querySegmentSpec(Filtration.eternity())) + .virtualColumns(expressionVirtualColumn("_v0", "\'10.1\'", ValueType.STRING)) + .columns("__time", "_v0") + .context(queryContext) + .build() + ), + ImmutableList.of( + new Object[]{"10.1", 946771200000L} + ) + ); + } + + // This query is expected to fail as we do not support join with constant in the on condition + // (see issue https://github.com/apache/druid/issues/9942 for more information) + // TODO: Remove expected Exception when https://github.com/apache/druid/issues/9942 is fixed + @Test(expected = RelOptPlanner.CannotPlanException.class) + @Parameters(source = QueryContextForJoinProvider.class) + public void testJoinOnConstantShouldFail(Map queryContext) throws Exception + { + cannotVectorize(); + + final String query = "SELECT t1.dim1 from foo as t1 LEFT JOIN foo as t2 on t1.dim1 = '10.1'"; + + testQuery( + query, + queryContext, + ImmutableList.of(), + ImmutableList.of() + ); + } + @Test public void testRepeatedIdenticalVirtualExpressionGrouping() throws Exception { From 6cfdab42984ead15526f042bd694e17b8166342c Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Mon, 1 Jun 2020 16:36:32 -0700 Subject: [PATCH 047/107] support customized factory.json via IndexSpec for segment persist (#9957) * support customized factory.json via IndexSpec for segment persist * equals verifier --- .../common/task/CompactionTaskRunTest.java | 30 ++-- .../apache/druid/segment/IndexMergerV9.java | 8 +- .../org/apache/druid/segment/IndexSpec.java | 34 ++++- .../segment/CustomSegmentizerFactoryTest.java | 139 ++++++++++++++++++ .../apache/druid/segment/IndexSpecTest.java | 7 + 5 files changed, 199 insertions(+), 19 deletions(-) create mode 100644 processing/src/test/java/org/apache/druid/segment/CustomSegmentizerFactoryTest.java diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskRunTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskRunTest.java index cf2bb2a255df..2a5b02e1db17 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskRunTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskRunTest.java @@ -19,10 +19,10 @@ package org.apache.druid.indexing.common.task; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.NamedType; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.io.Files; import org.apache.druid.client.coordinator.CoordinatorClient; import org.apache.druid.client.indexing.IndexingServiceClient; @@ -45,6 +45,7 @@ import org.apache.druid.indexing.common.task.CompactionTask.Builder; import org.apache.druid.indexing.firehose.IngestSegmentFirehoseFactory; import org.apache.druid.indexing.overlord.Segments; +import org.apache.druid.jackson.DefaultObjectMapper; import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.Pair; @@ -57,6 +58,7 @@ import org.apache.druid.query.dimension.DefaultDimensionSpec; import org.apache.druid.segment.Cursor; import org.apache.druid.segment.DimensionSelector; +import org.apache.druid.segment.IndexSpec; import org.apache.druid.segment.QueryableIndexStorageAdapter; import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.indexing.DataSchema; @@ -84,6 +86,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -125,19 +128,7 @@ public class CompactionTaskRunTest extends IngestionTestBase false, 0 ); - private static final CompactionState DEFAULT_COMPACTION_STATE = new CompactionState( - new DynamicPartitionsSpec(5000000, Long.MAX_VALUE), - ImmutableMap.of( - "bitmap", - ImmutableMap.of("type", "roaring", "compressRunOnSerialization", true), - "dimensionCompression", - "lz4", - "metricCompression", - "lz4", - "longEncoding", - "longs" - ) - ); + private static CompactionState DEFAULT_COMPACTION_STATE; private static final List TEST_ROWS = ImmutableList.of( "2014-01-01T00:00:10Z,a,1\n", @@ -195,6 +186,17 @@ public Collection fetchUsedSegmentsInDataSourceForIntervals( this.lockGranularity = lockGranularity; } + @BeforeClass + public static void setupClass() throws JsonProcessingException + { + ObjectMapper mapper = new DefaultObjectMapper(); + + DEFAULT_COMPACTION_STATE = new CompactionState( + new DynamicPartitionsSpec(5000000, Long.MAX_VALUE), + mapper.readValue(mapper.writeValueAsString(new IndexSpec()), Map.class) + ); + } + @Before public void setup() throws IOException { diff --git a/processing/src/main/java/org/apache/druid/segment/IndexMergerV9.java b/processing/src/main/java/org/apache/druid/segment/IndexMergerV9.java index e1a92f7d1a36..bdf0bccf672a 100644 --- a/processing/src/main/java/org/apache/druid/segment/IndexMergerV9.java +++ b/processing/src/main/java/org/apache/druid/segment/IndexMergerV9.java @@ -49,6 +49,7 @@ import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.incremental.IncrementalIndexAdapter; import org.apache.druid.segment.loading.MMappedQueryableSegmentizerFactory; +import org.apache.druid.segment.loading.SegmentizerFactory; import org.apache.druid.segment.serde.ColumnPartSerde; import org.apache.druid.segment.serde.ComplexColumnPartSerde; import org.apache.druid.segment.serde.ComplexMetricSerde; @@ -152,7 +153,12 @@ private File makeIndexFiles( progress.progress(); startTime = System.currentTimeMillis(); try (FileOutputStream fos = new FileOutputStream(new File(outDir, "factory.json"))) { - mapper.writeValue(fos, new MMappedQueryableSegmentizerFactory(indexIO)); + SegmentizerFactory customSegmentLoader = indexSpec.getSegmentLoader(); + if (customSegmentLoader != null) { + mapper.writeValue(fos, customSegmentLoader); + } else { + mapper.writeValue(fos, new MMappedQueryableSegmentizerFactory(indexIO)); + } } log.debug("Completed factory.json in %,d millis", System.currentTimeMillis() - startTime); diff --git a/processing/src/main/java/org/apache/druid/segment/IndexSpec.java b/processing/src/main/java/org/apache/druid/segment/IndexSpec.java index 6edc33686f23..13101de94db0 100644 --- a/processing/src/main/java/org/apache/druid/segment/IndexSpec.java +++ b/processing/src/main/java/org/apache/druid/segment/IndexSpec.java @@ -21,12 +21,14 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import org.apache.druid.segment.data.BitmapSerde; import org.apache.druid.segment.data.BitmapSerdeFactory; import org.apache.druid.segment.data.CompressionFactory; import org.apache.druid.segment.data.CompressionStrategy; +import org.apache.druid.segment.loading.SegmentizerFactory; import javax.annotation.Nullable; import java.util.Arrays; @@ -62,13 +64,26 @@ public class IndexSpec private final CompressionStrategy metricCompression; private final CompressionFactory.LongEncodingStrategy longEncoding; + @Nullable + private final SegmentizerFactory segmentLoader; /** * Creates an IndexSpec with default parameters */ public IndexSpec() { - this(null, null, null, null); + this(null, null, null, null, null); + } + + @VisibleForTesting + public IndexSpec( + @Nullable BitmapSerdeFactory bitmapSerdeFactory, + @Nullable CompressionStrategy dimensionCompression, + @Nullable CompressionStrategy metricCompression, + @Nullable CompressionFactory.LongEncodingStrategy longEncoding + ) + { + this(bitmapSerdeFactory, dimensionCompression, metricCompression, longEncoding, null); } /** @@ -93,7 +108,8 @@ public IndexSpec( @JsonProperty("bitmap") @Nullable BitmapSerdeFactory bitmapSerdeFactory, @JsonProperty("dimensionCompression") @Nullable CompressionStrategy dimensionCompression, @JsonProperty("metricCompression") @Nullable CompressionStrategy metricCompression, - @JsonProperty("longEncoding") @Nullable CompressionFactory.LongEncodingStrategy longEncoding + @JsonProperty("longEncoding") @Nullable CompressionFactory.LongEncodingStrategy longEncoding, + @JsonProperty("segmentLoader") @Nullable SegmentizerFactory segmentLoader ) { Preconditions.checkArgument(dimensionCompression == null || DIMENSION_COMPRESSION.contains(dimensionCompression), @@ -111,6 +127,7 @@ public IndexSpec( this.dimensionCompression = dimensionCompression == null ? DEFAULT_DIMENSION_COMPRESSION : dimensionCompression; this.metricCompression = metricCompression == null ? DEFAULT_METRIC_COMPRESSION : metricCompression; this.longEncoding = longEncoding == null ? DEFAULT_LONG_ENCODING : longEncoding; + this.segmentLoader = segmentLoader; } @JsonProperty("bitmap") @@ -137,6 +154,13 @@ public CompressionFactory.LongEncodingStrategy getLongEncoding() return longEncoding; } + @JsonProperty + @Nullable + public SegmentizerFactory getSegmentLoader() + { + return segmentLoader; + } + @Override public boolean equals(Object o) { @@ -150,13 +174,14 @@ public boolean equals(Object o) return Objects.equals(bitmapSerdeFactory, indexSpec.bitmapSerdeFactory) && dimensionCompression == indexSpec.dimensionCompression && metricCompression == indexSpec.metricCompression && - longEncoding == indexSpec.longEncoding; + longEncoding == indexSpec.longEncoding && + Objects.equals(segmentLoader, indexSpec.segmentLoader); } @Override public int hashCode() { - return Objects.hash(bitmapSerdeFactory, dimensionCompression, metricCompression, longEncoding); + return Objects.hash(bitmapSerdeFactory, dimensionCompression, metricCompression, longEncoding, segmentLoader); } @Override @@ -167,6 +192,7 @@ public String toString() ", dimensionCompression=" + dimensionCompression + ", metricCompression=" + metricCompression + ", longEncoding=" + longEncoding + + ", segmentLoader=" + segmentLoader + '}'; } } diff --git a/processing/src/test/java/org/apache/druid/segment/CustomSegmentizerFactoryTest.java b/processing/src/test/java/org/apache/druid/segment/CustomSegmentizerFactoryTest.java new file mode 100644 index 000000000000..8b4a8d3f5313 --- /dev/null +++ b/processing/src/test/java/org/apache/druid/segment/CustomSegmentizerFactoryTest.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment; + +import com.fasterxml.jackson.databind.InjectableValues; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import org.apache.druid.jackson.DefaultObjectMapper; +import org.apache.druid.jackson.SegmentizerModule; +import org.apache.druid.java.util.common.Intervals; +import org.apache.druid.math.expr.ExprMacroTable; +import org.apache.druid.query.expression.TestExprMacroTable; +import org.apache.druid.segment.incremental.IncrementalIndex; +import org.apache.druid.segment.loading.MMappedQueryableSegmentizerFactory; +import org.apache.druid.segment.loading.SegmentLoadingException; +import org.apache.druid.segment.loading.SegmentizerFactory; +import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; +import org.apache.druid.testing.InitializedNullHandlingTest; +import org.apache.druid.timeline.DataSegment; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; + +public class CustomSegmentizerFactoryTest extends InitializedNullHandlingTest +{ + private static ObjectMapper JSON_MAPPER; + private static IndexIO INDEX_IO; + private static IndexMerger INDEX_MERGER; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @BeforeClass + public static void setup() + { + final ObjectMapper mapper = new DefaultObjectMapper(); + mapper.registerModule(new SegmentizerModule()); + mapper.registerSubtypes(new NamedType(CustomSegmentizerFactory.class, "customSegmentFactory")); + final IndexIO indexIO = new IndexIO(mapper, () -> 0); + + mapper.setInjectableValues( + new InjectableValues.Std() + .addValue(ExprMacroTable.class.getName(), TestExprMacroTable.INSTANCE) + .addValue(ObjectMapper.class.getName(), mapper) + .addValue(IndexIO.class, indexIO) + .addValue(DataSegment.PruneSpecsHolder.class, DataSegment.PruneSpecsHolder.DEFAULT) + ); + + JSON_MAPPER = mapper; + INDEX_IO = indexIO; + INDEX_MERGER = new IndexMergerV9(mapper, indexIO, OffHeapMemorySegmentWriteOutMediumFactory.instance()); + } + + @Test + public void testDefaultSegmentizerPersist() throws IOException + { + IncrementalIndex data = TestIndex.makeRealtimeIndex("druid.sample.numeric.tsv"); + File segment = new File(temporaryFolder.newFolder(), "segment"); + File persisted = INDEX_MERGER.persist( + data, + Intervals.of("2011-01-12T00:00:00.000Z/2011-05-01T00:00:00.000Z"), + segment, + new IndexSpec( + null, + null, + null, + null, + null + ), + null + ); + + File factoryJson = new File(persisted, "factory.json"); + Assert.assertTrue(factoryJson.exists()); + SegmentizerFactory factory = JSON_MAPPER.readValue(factoryJson, SegmentizerFactory.class); + Assert.assertTrue(factory instanceof MMappedQueryableSegmentizerFactory); + } + + @Test + public void testCustomSegmentizerPersist() throws IOException + { + IncrementalIndex data = TestIndex.makeRealtimeIndex("druid.sample.numeric.tsv"); + File segment = new File(temporaryFolder.newFolder(), "segment"); + File persisted = INDEX_MERGER.persist( + data, + Intervals.of("2011-01-12T00:00:00.000Z/2011-05-01T00:00:00.000Z"), + segment, + new IndexSpec( + null, + null, + null, + null, + new CustomSegmentizerFactory() + ), + null + ); + + File factoryJson = new File(persisted, "factory.json"); + Assert.assertTrue(factoryJson.exists()); + SegmentizerFactory factory = JSON_MAPPER.readValue(factoryJson, SegmentizerFactory.class); + Assert.assertTrue(factory instanceof CustomSegmentizerFactory); + } + + private static class CustomSegmentizerFactory implements SegmentizerFactory + { + @Override + public Segment factorize(DataSegment segment, File parentDir, boolean lazy) throws SegmentLoadingException + { + try { + return new QueryableIndexSegment(INDEX_IO.loadIndex(parentDir, lazy), segment.getId()); + } + catch (IOException e) { + throw new SegmentLoadingException(e, "%s", e.getMessage()); + } + } + } +} diff --git a/processing/src/test/java/org/apache/druid/segment/IndexSpecTest.java b/processing/src/test/java/org/apache/druid/segment/IndexSpecTest.java index 4e03668f68ba..e9197ac8647a 100644 --- a/processing/src/test/java/org/apache/druid/segment/IndexSpecTest.java +++ b/processing/src/test/java/org/apache/druid/segment/IndexSpecTest.java @@ -20,6 +20,7 @@ package org.apache.druid.segment; import com.fasterxml.jackson.databind.ObjectMapper; +import nl.jqno.equalsverifier.EqualsVerifier; import org.apache.druid.jackson.DefaultObjectMapper; import org.apache.druid.segment.data.CompressionFactory; import org.apache.druid.segment.data.CompressionStrategy; @@ -65,4 +66,10 @@ public void testDefaults() Assert.assertEquals(CompressionStrategy.LZ4, spec.getMetricCompression()); Assert.assertEquals(CompressionFactory.LongEncodingStrategy.LONGS, spec.getLongEncoding()); } + + @Test + public void testEquals() + { + EqualsVerifier.forClass(IndexSpec.class).usingGetClass().verify(); + } } From af8a940bf47bcee9b3854cdbfbbc3ed989466912 Mon Sep 17 00:00:00 2001 From: agricenko Date: Tue, 2 Jun 2020 19:38:53 +0300 Subject: [PATCH 048/107] Integration Tests. (#9854) * Integration Tests. Added docker-compose with druid-cluster configuration. Refactored shell scripts. split code in a few files * Integration Tests. Added environment variable: DRUID_INTEGRATION_TEST_GROUP * Integration Tests. Removed nit * Integration Tests. Updated if block in docker_run_cluster.sh. * Integration Tests. Readme. Added Docker-compose section. * Integration Tests. removed yml files for s3, gcs, azure. Renamed variables for skip start/stop/build docker. Updated readme. Rollback maven profile: int-tests-config-file * Integration Tests. Removed docker-compose.test-env.yml file. Added DRUID_INTEGRATION_TEST_GROUP variable to docker-compose.yml * Integration Tests. Readme. Added details about docker-compose * Integration Tests. cleanup shell scripts Co-authored-by: agritsenko --- integration-tests/README.md | 28 +- integration-tests/build_run_cluster.sh | 39 +++ .../docker/docker-compose.base.yml | 271 ++++++++++++++++++ .../docker/docker-compose.druid-hadoop.yml | 28 ++ .../docker/docker-compose.override-env.yml | 195 +++++++++++++ integration-tests/docker/docker-compose.yml | 173 +++++++++++ .../environment-configs/override-examples/s3 | 2 +- integration-tests/pom.xml | 26 +- integration-tests/run_cluster.sh | 247 ---------------- .../script/copy_hadoop_resources.sh | 44 +++ integration-tests/script/copy_resources.sh | 80 ++++++ .../script/docker_build_containers.sh | 44 +++ .../script/docker_run_cluster.sh | 55 ++++ integration-tests/stop_cluster.sh | 7 +- 14 files changed, 973 insertions(+), 266 deletions(-) create mode 100755 integration-tests/build_run_cluster.sh create mode 100644 integration-tests/docker/docker-compose.base.yml create mode 100644 integration-tests/docker/docker-compose.druid-hadoop.yml create mode 100644 integration-tests/docker/docker-compose.override-env.yml create mode 100644 integration-tests/docker/docker-compose.yml delete mode 100755 integration-tests/run_cluster.sh create mode 100755 integration-tests/script/copy_hadoop_resources.sh create mode 100755 integration-tests/script/copy_resources.sh create mode 100755 integration-tests/script/docker_build_containers.sh create mode 100755 integration-tests/script/docker_run_cluster.sh diff --git a/integration-tests/README.md b/integration-tests/README.md index 7fe1bc2b5daa..afb123a67490 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -68,16 +68,38 @@ can either be 8 or 11. Druid's configuration (using Docker) can be overrided by providing -Doverride.config.path=. The file must contain one property per line, the key must start with `druid_` and the format should be snake case. +## Manual bringing up docker containers and running tests + +1. Build druid-cluster, druid-hadoop docker images. From root module run maven command: +``` +mvn clean install -pl integration-tests -P integration-tests -Ddocker.run.skip=true -Dmaven.test.skip=true +``` + +2. Run druid cluster by docker-compose: + +``` +- Basic Druid cluster: +docker-compose -f integration-tests/docker/docker-compose.yml up +- Druid cluster with override env for cloud integration tests: +OVERRIDE_ENV= docker-compose -f ${DOCKERDIR}/docker-compose.override-env.yml up +- Druid hadoop: +docker-compose -f ${DOCKERDIR}/docker-compose.druid-hadoop.yml up +``` + +3. Run maven command to execute tests with -Ddocker.build.skip=true -Ddocker.run.skip=true + ## Tips & tricks for debugging and developing integration tests ### Useful mvn command flags -- -Dskip.start.docker=true to skip starting docker containers. This can save ~3 minutes by skipping building and bringing +- -Ddocker.build.skip=true to skip build druid containers. +If you do not apply any change to druid then you can do not rebuild druid. +This can save ~4 minutes to build druid cluster and druid hadoop. +You need to build druid containers only once, after you can skip docker build step. +- -Ddocker.run.skip=true to skip starting docker containers. This can save ~3 minutes by skipping building and bringing up the docker containers (Druid, Kafka, Hadoop, MYSQL, zookeeper, etc). Please make sure that you actually do have these containers already running if using this flag. Additionally, please make sure that the running containers are in the same state that the setup script (run_cluster.sh) would have brought it up in. -- -Dskip.stop.docker=true to skip stopping and teardowning down the docker containers. This can be useful in further -debugging after the integration tests have finish running. ### Debugging Druid while running tests diff --git a/integration-tests/build_run_cluster.sh b/integration-tests/build_run_cluster.sh new file mode 100755 index 000000000000..aba2d6af805d --- /dev/null +++ b/integration-tests/build_run_cluster.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +echo $DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH + +export DIR=$(cd $(dirname $0) && pwd) +export HADOOP_DOCKER_DIR=$DIR/../examples/quickstart/tutorial/hadoop/docker +export DOCKERDIR=$DIR/docker +export SHARED_DIR=${HOME}/shared + +# so docker IP addr will be known during docker build +echo ${DOCKER_IP:=127.0.0.1} > $DOCKERDIR/docker_ip + +if !($DRUID_INTEGRATION_TEST_SKIP_BUILD_DOCKER); then + sh ./script/copy_resources.sh + sh ./script/docker_build_containers.sh +fi + +if !($DRUID_INTEGRATION_TEST_SKIP_RUN_DOCKER); then + sh ./stop_cluster.sh + sh ./script/docker_run_cluster.sh +fi + +if ($DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER); then + sh ./script/copy_hadoop_resources.sh +fi diff --git a/integration-tests/docker/docker-compose.base.yml b/integration-tests/docker/docker-compose.base.yml new file mode 100644 index 000000000000..1f7931a623af --- /dev/null +++ b/integration-tests/docker/docker-compose.base.yml @@ -0,0 +1,271 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +version: "2.2" +services: + druid-it-hadoop: + image: druid-it/hadoop:2.8.5 + container_name: druid-it-hadoop + ports: + - 2049:2049 + - 2122:2122 + - 8020:8020 + - 8021:8021 + - 8030:8030 + - 8031:8031 + - 8032:8032 + - 8033:8033 + - 8040:8040 + - 8042:8042 + - 8088:8088 + - 8443:8443 + - 9000:9000 + - 10020:10020 + - 19888:19888 + - 34455:34455 + - 50010:50010 + - 50020:50020 + - 50030:50030 + - 50060:50060 + - 50070:50070 + - 50075:50075 + - 50090:50090 + - 51111:51111 + networks: + druid-it-net: + ipv4_address: 172.172.172.13 + privileged: true + volumes: + - ${HOME}/shared:/shared + - ./../src/test/resources:/resources + hostname: "druid-it-hadoop" + command: "bash -c 'echo Start druid-it-hadoop container... && \ + /etc/bootstrap.sh && \ + tail -f /dev/null'" + + druid-zookeeper-kafka: + image: druid/cluster + container_name: druid-zookeeper-kafka + ports: + - 2181:2181 + - 9092:9092 + - 9093:9093 + networks: + druid-it-net: + ipv4_address: 172.172.172.2 + privileged: true + volumes: + - ${HOME}/shared:/shared + - ./service-supervisords/zookeeper.conf:/usr/lib/druid/conf/zookeeper.conf + - ./service-supervisords/kafka.conf:/usr/lib/druid/conf/kafka.conf + env_file: + - ./environment-configs/common + + druid-metadata-storage: + image: druid/cluster + container_name: druid-metadata-storage + ports: + - 3306:3306 + networks: + druid-it-net: + ipv4_address: 172.172.172.3 + privileged: true + volumes: + - ${HOME}/shared:/shared + - ./service-supervisords/metadata-storage.conf:/usr/lib/druid/conf/metadata-storage.conf + env_file: + - ./environment-configs/common + + druid-overlord: + image: druid/cluster + container_name: druid-overlord + networks: + druid-it-net: + ipv4_address: 172.172.172.4 + ports: + - 8090:8090 + - 8290:8290 + - 5009:5009 + privileged: true + volumes: + - ${HOME}/shared:/shared + - ./service-supervisords/druid.conf:/usr/lib/druid/conf/druid.conf + env_file: + - ./environment-configs/common + - ./environment-configs/overlord + + druid-coordinator: + image: druid/cluster + container_name: druid-coordinator + networks: + druid-it-net: + ipv4_address: 172.172.172.5 + ports: + - 8081:8081 + - 8281:8281 + - 5006:5006 + privileged: true + volumes: + - ${HOME}/shared:/shared + - ./service-supervisords/druid.conf:/usr/lib/druid/conf/druid-overlord.conf + env_file: + - ./environment-configs/common + - ./environment-configs/coordinator + + druid-historical: + image: druid/cluster + container_name: druid-historical + networks: + druid-it-net: + ipv4_address: 172.172.172.6 + ports: + - 8083:8083 + - 8283:8283 + - 5007:5007 + privileged: true + volumes: + - ${HOME}/shared:/shared + - ./service-supervisords/druid.conf:/usr/lib/druid/conf/druid.conf + env_file: + - ./environment-configs/common + - ./environment-configs/historical + + druid-middlemanager: + image: druid/cluster + container_name: druid-middlemanager + networks: + druid-it-net: + ipv4_address: 172.172.172.7 + ports: + - 5008:5008 + - 8091:8091 + - 8291:8291 + - 8100:8100 + - 8101:8101 + - 8102:8102 + - 8103:8103 + - 8104:8104 + - 8105:8105 + - 8300:8300 + - 8301:8301 + - 8302:8302 + - 8303:8303 + - 8304:8304 + - 8305:8305 + privileged: true + volumes: + - ./../src/test/resources:/resources + - ${HOME}/shared:/shared + - ./service-supervisords/druid.conf:/usr/lib/druid/conf/druid.conf + env_file: + - ./environment-configs/common + - ./environment-configs/middlemanager + + druid-broker: + image: druid/cluster + container_name: druid-broker + networks: + druid-it-net: + ipv4_address: 172.172.172.8 + ports: + - 5005:5005 + - 8082:8082 + - 8282:8282 + privileged: true + volumes: + - ${HOME}/shared:/shared + - ./service-supervisords/druid.conf:/usr/lib/druid/conf/druid.conf + env_file: + - ./environment-configs/common + - ./environment-configs/broker + + druid-router: + image: druid/cluster + container_name: druid-router + networks: + druid-it-net: + ipv4_address: 172.172.172.9 + ports: + - 5004:5004 + - 8888:8888 + - 9088:9088 + privileged: true + volumes: + - ${HOME}/shared:/shared + - ./service-supervisords/druid.conf:/usr/lib/druid/conf/druid.conf + env_file: + - ./environment-configs/common + - ./environment-configs/router + + druid-router-permissive-tls: + image: druid/cluster + container_name: druid-router-permissive-tls + networks: + druid-it-net: + ipv4_address: 172.172.172.10 + ports: + - 5001:5001 + - 8889:8889 + - 9089:9089 + privileged: true + volumes: + - ${HOME}/shared:/shared + - ./service-supervisords/druid.conf:/usr/lib/druid/conf/druid.conf + env_file: + - ./environment-configs/common + - ./environment-configs/router-permissive-tls + + druid-router-no-client-auth-tls: + image: druid/cluster + container_name: druid-router-no-client-auth-tls + networks: + druid-it-net: + ipv4_address: 172.172.172.11 + ports: + - 5002:5002 + - 8890:8890 + - 9090:9090 + privileged: true + volumes: + - ${HOME}/shared:/shared + - ./service-supervisords/druid.conf:/usr/lib/druid/conf/druid.conf + env_file: + - ./environment-configs/common + - ./environment-configs/router-no-client-auth-tls + + druid-router-custom-check-tls: + image: druid/cluster + container_name: druid-router-custom-check-tls + networks: + druid-it-net: + ipv4_address: 172.172.172.12 + ports: + - 5003:5003 + - 8891:8891 + - 9091:9091 + privileged: true + volumes: + - ${HOME}/shared:/shared + - ./service-supervisords/druid.conf:/usr/lib/druid/conf/druid.conf + env_file: + - ./environment-configs/common + - ./environment-configs/router-custom-check-tls + +networks: + druid-it-net: + name: druid-it-net + ipam: + config: + - subnet: 172.172.172.0/24 \ No newline at end of file diff --git a/integration-tests/docker/docker-compose.druid-hadoop.yml b/integration-tests/docker/docker-compose.druid-hadoop.yml new file mode 100644 index 000000000000..735f9670277b --- /dev/null +++ b/integration-tests/docker/docker-compose.druid-hadoop.yml @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +version: "2.2" +services: + druid-it-hadoop: + extends: + file: docker-compose.base.yml + service: druid-it-hadoop + +networks: + druid-it-net: + name: druid-it-net + ipam: + config: + - subnet: 172.172.172.0/24 \ No newline at end of file diff --git a/integration-tests/docker/docker-compose.override-env.yml b/integration-tests/docker/docker-compose.override-env.yml new file mode 100644 index 000000000000..344ee0385eb6 --- /dev/null +++ b/integration-tests/docker/docker-compose.override-env.yml @@ -0,0 +1,195 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +version: "2.2" +services: + druid-zookeeper-kafka: + extends: + file: docker-compose.base.yml + service: druid-zookeeper-kafka + + druid-metadata-storage: + extends: + file: docker-compose.base.yml + service: druid-metadata-storage + depends_on: + - druid-zookeeper-kafka + + druid-overlord: + extends: + file: docker-compose.base.yml + service: druid-overlord + env_file: + - ${OVERRIDE_ENV} + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-metadata-storage:druid-metadata-storage + - druid-zookeeper-kafka:druid-zookeeper-kafka + depends_on: + - druid-metadata-storage + - druid-zookeeper-kafka + + druid-coordinator: + extends: + file: docker-compose.base.yml + service: druid-coordinator + env_file: + - ${OVERRIDE_ENV} + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-overlord:druid-overlord + - druid-metadata-storage:druid-metadata-storage + - druid-zookeeper-kafka:druid-zookeeper-kafka + depends_on: + - druid-overlord + - druid-metadata-storage + - druid-zookeeper-kafka + + druid-historical: + extends: + file: docker-compose.base.yml + service: druid-historical + env_file: + - ${OVERRIDE_ENV} + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + depends_on: + - druid-zookeeper-kafka + + druid-middlemanager: + extends: + file: docker-compose.base.yml + service: druid-middlemanager + env_file: + - ${OVERRIDE_ENV} + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-overlord:druid-overlord + depends_on: + - druid-zookeeper-kafka + - druid-overlord + + druid-broker: + extends: + file: docker-compose.base.yml + service: druid-broker + env_file: + - ${OVERRIDE_ENV} + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-middlemanager:druid-middlemanager + - druid-historical:druid-historical + depends_on: + - druid-zookeeper-kafka + - druid-middlemanager + - druid-historical + + druid-router: + extends: + file: docker-compose.base.yml + service: druid-router + env_file: + - ${OVERRIDE_ENV} + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-coordinator:druid-coordinator + - druid-broker:druid-broker + depends_on: + - druid-zookeeper-kafka + - druid-coordinator + - druid-broker + + druid-router-permissive-tls: + extends: + file: docker-compose.base.yml + service: druid-router-permissive-tls + env_file: + - ${OVERRIDE_ENV} + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-coordinator:druid-coordinator + - druid-broker:druid-broker + depends_on: + - druid-zookeeper-kafka + - druid-metadata-storage + - druid-overlord + - druid-coordinator + - druid-historical + - druid-middlemanager + - druid-broker + - druid-router + + druid-router-no-client-auth-tls: + extends: + file: docker-compose.base.yml + service: druid-router-no-client-auth-tls + env_file: + - ${OVERRIDE_ENV} + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-coordinator:druid-coordinator + - druid-broker:druid-broker + depends_on: + - druid-zookeeper-kafka + - druid-metadata-storage + - druid-overlord + - druid-coordinator + - druid-historical + - druid-middlemanager + - druid-broker + - druid-router + + druid-router-custom-check-tls: + extends: + file: docker-compose.base.yml + service: druid-router-custom-check-tls + env_file: + - ${OVERRIDE_ENV} + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-coordinator:druid-coordinator + - druid-broker:druid-broker + depends_on: + - druid-zookeeper-kafka + - druid-metadata-storage + - druid-overlord + - druid-coordinator + - druid-historical + - druid-middlemanager + - druid-broker + - druid-router + +networks: + druid-it-net: + name: druid-it-net + ipam: + config: + - subnet: 172.172.172.0/24 \ No newline at end of file diff --git a/integration-tests/docker/docker-compose.yml b/integration-tests/docker/docker-compose.yml new file mode 100644 index 000000000000..43fe11545d3d --- /dev/null +++ b/integration-tests/docker/docker-compose.yml @@ -0,0 +1,173 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +version: "2.2" +services: + druid-zookeeper-kafka: + extends: + file: docker-compose.base.yml + service: druid-zookeeper-kafka + + druid-metadata-storage: + extends: + file: docker-compose.base.yml + service: druid-metadata-storage + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + depends_on: + - druid-zookeeper-kafka + + druid-overlord: + extends: + file: docker-compose.base.yml + service: druid-overlord + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-metadata-storage:druid-metadata-storage + - druid-zookeeper-kafka:druid-zookeeper-kafka + depends_on: + - druid-metadata-storage + - druid-zookeeper-kafka + + druid-coordinator: + extends: + file: docker-compose.base.yml + service: druid-coordinator + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-overlord:druid-overlord + - druid-metadata-storage:druid-metadata-storage + - druid-zookeeper-kafka:druid-zookeeper-kafka + depends_on: + - druid-overlord + - druid-metadata-storage + - druid-zookeeper-kafka + + druid-historical: + extends: + file: docker-compose.base.yml + service: druid-historical + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + depends_on: + - druid-zookeeper-kafka + + druid-middlemanager: + extends: + file: docker-compose.base.yml + service: druid-middlemanager + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-overlord:druid-overlord + depends_on: + - druid-zookeeper-kafka + - druid-overlord + + druid-broker: + extends: + file: docker-compose.base.yml + service: druid-broker + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-middlemanager:druid-middlemanager + - druid-historical:druid-historical + depends_on: + - druid-zookeeper-kafka + - druid-middlemanager + - druid-historical + + druid-router: + extends: + file: docker-compose.base.yml + service: druid-router + environment: + - DRUID_INTEGRATION_TEST_GROUP=${DRUID_INTEGRATION_TEST_GROUP} + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-coordinator:druid-coordinator + - druid-broker:druid-broker + depends_on: + - druid-zookeeper-kafka + - druid-coordinator + - druid-broker + + druid-router-permissive-tls: + extends: + file: docker-compose.base.yml + service: druid-router-permissive-tls + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-coordinator:druid-coordinator + - druid-broker:druid-broker + depends_on: + - druid-zookeeper-kafka + - druid-metadata-storage + - druid-overlord + - druid-coordinator + - druid-historical + - druid-middlemanager + - druid-broker + - druid-router + + druid-router-no-client-auth-tls: + extends: + file: docker-compose.base.yml + service: druid-router-no-client-auth-tls + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-coordinator:druid-coordinator + - druid-broker:druid-broker + depends_on: + - druid-zookeeper-kafka + - druid-metadata-storage + - druid-overlord + - druid-coordinator + - druid-historical + - druid-middlemanager + - druid-broker + - druid-router + + druid-router-custom-check-tls: + extends: + file: docker-compose.base.yml + service: druid-router-custom-check-tls + links: + - druid-zookeeper-kafka:druid-zookeeper-kafka + - druid-coordinator:druid-coordinator + - druid-broker:druid-broker + depends_on: + - druid-zookeeper-kafka + - druid-metadata-storage + - druid-overlord + - druid-coordinator + - druid-historical + - druid-middlemanager + - druid-broker + - druid-router + +networks: + druid-it-net: + name: druid-it-net + ipam: + config: + - subnet: 172.172.172.0/24 \ No newline at end of file diff --git a/integration-tests/docker/environment-configs/override-examples/s3 b/integration-tests/docker/environment-configs/override-examples/s3 index cdca76490625..9146e985757f 100644 --- a/integration-tests/docker/environment-configs/override-examples/s3 +++ b/integration-tests/docker/environment-configs/override-examples/s3 @@ -27,4 +27,4 @@ druid_storage_baseKey=druid/segments druid_s3_accessKey= druid_s3_secretKey= AWS_REGION= -druid_extensions_loadList=["druid-s3-extensions"] \ No newline at end of file +druid_extensions_loadList=["druid-s3-extensions","druid-hdfs-storage"] \ No newline at end of file diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 9d10680dc957..5aa63025d27d 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -361,8 +361,8 @@ integration-tests false - false - false + false + false \ Россия\ 한국\ 中国!? @@ -374,21 +374,22 @@ exec-maven-plugin - build-and-start-druid-cluster + docker-package exec pre-integration-test - ${start.hadoop.docker} - ${skip.start.docker} - ${jvm.runtime} - ${groups} - ${override.config.path} - ${resource.file.dir.path}> + ${start.hadoop.docker} + ${jvm.runtime} + ${groups} + ${override.config.path} + ${resource.file.dir.path} + ${docker.build.skip} + ${docker.run.skip} - ${project.basedir}/run_cluster.sh + ${project.basedir}/build_run_cluster.sh @@ -399,13 +400,14 @@ post-integration-test - ${skip.stop.docker} + ${docker.run.skip} ${project.basedir}/stop_cluster.sh + org.apache.maven.plugins maven-failsafe-plugin @@ -446,6 +448,7 @@ + de.thetaphi forbiddenapis @@ -460,6 +463,7 @@ + int-tests-config-file diff --git a/integration-tests/run_cluster.sh b/integration-tests/run_cluster.sh deleted file mode 100755 index faaa4eaa1eed..000000000000 --- a/integration-tests/run_cluster.sh +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Skip starting docker if flag set (For use during development) -if [ -n "$DRUID_INTEGRATION_TEST_SKIP_START_DOCKER" ] && [ "$DRUID_INTEGRATION_TEST_SKIP_START_DOCKER" == true ] - then - exit 0 - fi - -# Cleanup old images/containers -{ - for node in druid-historical druid-coordinator druid-overlord druid-router druid-router-permissive-tls druid-router-no-client-auth-tls druid-router-custom-check-tls druid-broker druid-middlemanager druid-zookeeper-kafka druid-metadata-storage druid-it-hadoop; - do - docker stop $node - docker rm $node - done - - docker network rm druid-it-net -} - -# Druid environment and jars setup -{ - # environment variables - DIR=$(cd $(dirname $0) && pwd) - HADOOP_DOCKER_DIR=$DIR/../examples/quickstart/tutorial/hadoop/docker - DOCKERDIR=$DIR/docker - SERVICE_SUPERVISORDS_DIR=$DOCKERDIR/service-supervisords - ENVIRONMENT_CONFIGS_DIR=$DOCKERDIR/environment-configs - SHARED_DIR=${HOME}/shared - SUPERVISORDIR=/usr/lib/druid/conf - RESOURCEDIR=$DIR/src/test/resources - - # so docker IP addr will be known during docker build - echo ${DOCKER_IP:=127.0.0.1} > $DOCKERDIR/docker_ip - - # setup client keystore - ./docker/tls/generate-client-certs-and-keystores.sh - rm -rf docker/client_tls - cp -r client_tls docker/client_tls - - # Make directories if they dont exist - mkdir -p $SHARED_DIR/hadoop_xml - mkdir -p $SHARED_DIR/hadoop-dependencies - mkdir -p $SHARED_DIR/logs - mkdir -p $SHARED_DIR/tasklogs - mkdir -p $SHARED_DIR/docker/extensions - mkdir -p $SHARED_DIR/docker/credentials - - # install druid jars - rm -rf $SHARED_DIR/docker - cp -R docker $SHARED_DIR/docker - mvn -B dependency:copy-dependencies -DoutputDirectory=$SHARED_DIR/docker/lib - - # move extensions into a seperate extension folder - # For druid-s3-extensions - mkdir -p $SHARED_DIR/docker/extensions/druid-s3-extensions - mv $SHARED_DIR/docker/lib/druid-s3-extensions-* $SHARED_DIR/docker/extensions/druid-s3-extensions - # For druid-azure-extensions - mkdir -p $SHARED_DIR/docker/extensions/druid-azure-extensions - mv $SHARED_DIR/docker/lib/druid-azure-extensions-* $SHARED_DIR/docker/extensions/druid-azure-extensions - # For druid-google-extensions - mkdir -p $SHARED_DIR/docker/extensions/druid-google-extensions - mv $SHARED_DIR/docker/lib/druid-google-extensions-* $SHARED_DIR/docker/extensions/druid-google-extensions - # For druid-hdfs-storage - mkdir -p $SHARED_DIR/docker/extensions/druid-hdfs-storage - mv $SHARED_DIR/docker/lib/druid-hdfs-storage-* $SHARED_DIR/docker/extensions/druid-hdfs-storage - # For druid-kinesis-indexing-service - mkdir -p $SHARED_DIR/docker/extensions/druid-kinesis-indexing-service - mv $SHARED_DIR/docker/lib/druid-kinesis-indexing-service-* $SHARED_DIR/docker/extensions/druid-kinesis-indexing-service - # For druid-parquet-extensions - mkdir -p $SHARED_DIR/docker/extensions/druid-parquet-extensions - mv $SHARED_DIR/docker/lib/druid-parquet-extensions-* $SHARED_DIR/docker/extensions/druid-parquet-extensions - # For druid-orc-extensions - mkdir -p $SHARED_DIR/docker/extensions/druid-orc-extensions - mv $SHARED_DIR/docker/lib/druid-orc-extensions-* $SHARED_DIR/docker/extensions/druid-orc-extensions - - # Pull Hadoop dependency if needed - if [ -n "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" ] && [ "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" == true ] - then - java -cp "$SHARED_DIR/docker/lib/*" -Ddruid.extensions.hadoopDependenciesDir="$SHARED_DIR/hadoop-dependencies" org.apache.druid.cli.Main tools pull-deps -h org.apache.hadoop:hadoop-client:2.8.5 -h org.apache.hadoop:hadoop-aws:2.8.5 -h org.apache.hadoop:hadoop-azure:2.8.5 - curl https://storage.googleapis.com/hadoop-lib/gcs/gcs-connector-hadoop2-latest.jar --output $SHARED_DIR/docker/lib/gcs-connector-hadoop2-latest.jar - fi - - # install logging config - cp src/main/resources/log4j2.xml $SHARED_DIR/docker/lib/log4j2.xml - - # copy the integration test jar, it provides test-only extension implementations - cp target/druid-integration-tests*.jar $SHARED_DIR/docker/lib - - # one of the integration tests needs the wikiticker sample data - mkdir -p $SHARED_DIR/wikiticker-it - cp ../examples/quickstart/tutorial/wikiticker-2015-09-12-sampled.json.gz $SHARED_DIR/wikiticker-it/wikiticker-2015-09-12-sampled.json.gz - cp docker/wiki-simple-lookup.json $SHARED_DIR/wikiticker-it/wiki-simple-lookup.json - - # copy other files if needed - if [ -n "$DRUID_INTEGRATION_TEST_RESOURCE_FILE_DIR_PATH" ] - then - cp -a $DRUID_INTEGRATION_TEST_RESOURCE_FILE_DIR_PATH/. $SHARED_DIR/docker/credentials/ - fi - - # setup all enviornment variables to be pass to the containers - COMMON_ENV="--env-file=$ENVIRONMENT_CONFIGS_DIR/common -e DRUID_INTEGRATION_TEST_GROUP" - BROKER_ENV="--env-file=$ENVIRONMENT_CONFIGS_DIR/broker" - COORDINATOR_ENV="--env-file=$ENVIRONMENT_CONFIGS_DIR/coordinator" - HISTORICAL_ENV="--env-file=$ENVIRONMENT_CONFIGS_DIR/historical" - MIDDLEMANAGER_ENV="--env-file=$ENVIRONMENT_CONFIGS_DIR/middlemanager" - OVERLORD_ENV="--env-file=$ENVIRONMENT_CONFIGS_DIR/overlord" - ROUTER_ENV="--env-file=$ENVIRONMENT_CONFIGS_DIR/router" - ROUTER_CUSTOM_CHECK_TLS_ENV="--env-file=$ENVIRONMENT_CONFIGS_DIR/router-custom-check-tls" - ROUTER_NO_CLIENT_AUTH_TLS_ENV="--env-file=$ENVIRONMENT_CONFIGS_DIR/router-no-client-auth-tls" - ROUTER_PERMISSIVE_TLS_ENV="--env-file=$ENVIRONMENT_CONFIGS_DIR/router-permissive-tls" - - OVERRIDE_ENV="" - if [ -z "$DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH" ] - then - echo "\$DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH is not set. No override config file provided" - if [ "$DRUID_INTEGRATION_TEST_GROUP" = "s3-deep-storage" ] || \ - [ "$DRUID_INTEGRATION_TEST_GROUP" = "gcs-deep-storage" ] || \ - [ "$DRUID_INTEGRATION_TEST_GROUP" = "azure-deep-storage" ]; then - echo "Test group $DRUID_INTEGRATION_TEST_GROUP requires override config file. Stopping test..." - exit 1 - fi - else - echo "\$DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH is set with value ${DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH}" - OVERRIDE_ENV="--env-file=$DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH" - fi -} - -# Create docker network -{ - docker network create --subnet=172.172.172.0/24 druid-it-net -} - -# Build Druid Cluster Image -if [ -z "$DRUID_INTEGRATION_TEST_JVM_RUNTIME" ] -then - echo "\$DRUID_INTEGRATION_TEST_JVM_RUNTIME is not set. Running integration test with image running Java 8" - docker build -t druid/cluster --build-arg DOCKER_IMAGE=imply/druiditbase:openjdk-1.8.0_191-1 $SHARED_DIR/docker -else - echo "\$DRUID_INTEGRATION_TEST_JVM_RUNTIME is set with value ${DRUID_INTEGRATION_TEST_JVM_RUNTIME}" - case "${DRUID_INTEGRATION_TEST_JVM_RUNTIME}" in - 8) - echo "Running integration test with image running Java 8" - docker build -t druid/cluster --build-arg DOCKER_IMAGE=imply/druiditbase:openjdk-1.8.0_191-1 $SHARED_DIR/docker - ;; - 11) - echo "Running integration test with image running Java 11" - docker build -t druid/cluster --build-arg DOCKER_IMAGE=imply/druiditbase:openjdk-11.0.5-1 $SHARED_DIR/docker - ;; - *) - echo "Invalid JVM Runtime given. Stopping" - exit 1 - ;; - esac -fi - -# Build Hadoop docker if needed -if [ -n "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" ] && [ "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" == true ] -then - docker build -t druid-it/hadoop:2.8.5 $HADOOP_DOCKER_DIR -fi - - -# Start docker containers for all Druid processes and dependencies -{ - # Start Hadoop docker if needed - if [ -n "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" ] && [ "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" == true ] - then - # Start Hadoop docker container - docker run -d --privileged --net druid-it-net --ip 172.172.172.13 -h druid-it-hadoop --name druid-it-hadoop -p 2049:2049 -p 2122:2122 -p 8020:8020 -p 8021:8021 -p 8030:8030 -p 8031:8031 -p 8032:8032 -p 8033:8033 -p 8040:8040 -p 8042:8042 -p 8088:8088 -p 8443:8443 -p 9000:9000 -p 10020:10020 -p 19888:19888 -p 34455:34455 -p 49707:49707 -p 50010:50010 -p 50020:50020 -p 50030:50030 -p 50060:50060 -p 50070:50070 -p 50075:50075 -p 50090:50090 -p 51111:51111 -v $RESOURCEDIR:/resources -v $SHARED_DIR:/shared druid-it/hadoop:2.8.5 sh -c "/etc/bootstrap.sh && tail -f /dev/null" - - # wait for hadoop namenode to be up - echo "Waiting for hadoop namenode to be up" - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -mkdir -p /druid" - while [ $? -ne 0 ] - do - sleep 2 - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -mkdir -p /druid" - done - echo "Finished waiting for Hadoop namenode" - - # Setup hadoop druid dirs - echo "Setting up druid hadoop dirs" - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -mkdir -p /druid" - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -mkdir -p /druid/segments" - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -mkdir -p /quickstart" - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -chmod 777 /druid" - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -chmod 777 /druid/segments" - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -chmod 777 /quickstart" - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -chmod -R 777 /tmp" - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -chmod -R 777 /user" - # Copy data files to Hadoop container - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -put /shared/wikiticker-it/wikiticker-2015-09-12-sampled.json.gz /quickstart/wikiticker-2015-09-12-sampled.json.gz" - docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -put /resources/data/batch_index /batch_index" - echo "Finished setting up druid hadoop dirs" - - echo "Copying Hadoop XML files to shared" - docker exec -t druid-it-hadoop sh -c "cp /usr/local/hadoop/etc/hadoop/*.xml /shared/hadoop_xml" - echo "Copied Hadoop XML files to shared" - fi - - # Start zookeeper and kafka - docker run -d --privileged --net druid-it-net --ip 172.172.172.2 ${COMMON_ENV} --name druid-zookeeper-kafka -p 2181:2181 -p 9092:9092 -p 9093:9093 -v $SHARED_DIR:/shared -v $SERVICE_SUPERVISORDS_DIR/zookeeper.conf:$SUPERVISORDIR/zookeeper.conf -v $SERVICE_SUPERVISORDS_DIR/kafka.conf:$SUPERVISORDIR/kafka.conf druid/cluster - - # Start MYSQL - docker run -d --privileged --net druid-it-net --ip 172.172.172.3 ${COMMON_ENV} --name druid-metadata-storage -p 3306:3306 -v $SHARED_DIR:/shared -v $SERVICE_SUPERVISORDS_DIR/metadata-storage.conf:$SUPERVISORDIR/metadata-storage.conf druid/cluster - - # Start Overlord - docker run -d --privileged --net druid-it-net --ip 172.172.172.4 ${COMMON_ENV} ${OVERLORD_ENV} ${OVERRIDE_ENV} --name druid-overlord -p 5009:5009 -p 8090:8090 -p 8290:8290 -v $SHARED_DIR:/shared -v $SERVICE_SUPERVISORDS_DIR/druid.conf:$SUPERVISORDIR/druid.conf --link druid-metadata-storage:druid-metadata-storage --link druid-zookeeper-kafka:druid-zookeeper-kafka druid/cluster - - # Start Coordinator - docker run -d --privileged --net druid-it-net --ip 172.172.172.5 ${COMMON_ENV} ${COORDINATOR_ENV} ${OVERRIDE_ENV} --name druid-coordinator -p 5006:5006 -p 8081:8081 -p 8281:8281 -v $SHARED_DIR:/shared -v $SERVICE_SUPERVISORDS_DIR/druid.conf:$SUPERVISORDIR/druid.conf --link druid-overlord:druid-overlord --link druid-metadata-storage:druid-metadata-storage --link druid-zookeeper-kafka:druid-zookeeper-kafka druid/cluster - - # Start Historical - docker run -d --privileged --net druid-it-net --ip 172.172.172.6 ${COMMON_ENV} ${HISTORICAL_ENV} ${OVERRIDE_ENV} --name druid-historical -p 5007:5007 -p 8083:8083 -p 8283:8283 -v $SHARED_DIR:/shared -v $SERVICE_SUPERVISORDS_DIR/druid.conf:$SUPERVISORDIR/druid.conf --link druid-zookeeper-kafka:druid-zookeeper-kafka druid/cluster - - # Start Middlemanger - docker run -d --privileged --net druid-it-net --ip 172.172.172.7 ${COMMON_ENV} ${MIDDLEMANAGER_ENV} ${OVERRIDE_ENV} --name druid-middlemanager -p 5008:5008 -p 8091:8091 -p 8291:8291 -p 8100:8100 -p 8101:8101 -p 8102:8102 -p 8103:8103 -p 8104:8104 -p 8105:8105 -p 8300:8300 -p 8301:8301 -p 8302:8302 -p 8303:8303 -p 8304:8304 -p 8305:8305 -v $RESOURCEDIR:/resources -v $SHARED_DIR:/shared -v $SERVICE_SUPERVISORDS_DIR/druid.conf:$SUPERVISORDIR/druid.conf --link druid-zookeeper-kafka:druid-zookeeper-kafka --link druid-overlord:druid-overlord druid/cluster - - # Start Broker - docker run -d --privileged --net druid-it-net --ip 172.172.172.8 ${COMMON_ENV} ${BROKER_ENV} ${OVERRIDE_ENV} --name druid-broker -p 5005:5005 -p 8082:8082 -p 8282:8282 -v $SHARED_DIR:/shared -v $SERVICE_SUPERVISORDS_DIR/druid.conf:$SUPERVISORDIR/druid.conf --link druid-zookeeper-kafka:druid-zookeeper-kafka --link druid-middlemanager:druid-middlemanager --link druid-historical:druid-historical druid/cluster - - # Start Router - docker run -d --privileged --net druid-it-net --ip 172.172.172.9 ${COMMON_ENV} ${ROUTER_ENV} ${OVERRIDE_ENV} --name druid-router -p 8888:8888 -p 5004:5004 -p 9088:9088 -v $SHARED_DIR:/shared -v $SERVICE_SUPERVISORDS_DIR/druid.conf:$SUPERVISORDIR/druid.conf --link druid-zookeeper-kafka:druid-zookeeper-kafka --link druid-coordinator:druid-coordinator --link druid-broker:druid-broker druid/cluster - - # Start Router with permissive TLS settings (client auth enabled, no hostname verification, no revocation check) - docker run -d --privileged --net druid-it-net --ip 172.172.172.10 ${COMMON_ENV} ${ROUTER_PERMISSIVE_TLS_ENV} ${OVERRIDE_ENV} --name druid-router-permissive-tls -p 5001:5001 -p 8889:8889 -p 9089:9089 -v $SHARED_DIR:/shared -v $SERVICE_SUPERVISORDS_DIR/druid.conf:$SUPERVISORDIR/druid.conf --link druid-zookeeper-kafka:druid-zookeeper-kafka --link druid-coordinator:druid-coordinator --link druid-broker:druid-broker druid/cluster - - # Start Router with TLS but no client auth - docker run -d --privileged --net druid-it-net --ip 172.172.172.11 ${COMMON_ENV} ${ROUTER_NO_CLIENT_AUTH_TLS_ENV} ${OVERRIDE_ENV} --name druid-router-no-client-auth-tls -p 5002:5002 -p 8890:8890 -p 9090:9090 -v $SHARED_DIR:/shared -v $SERVICE_SUPERVISORDS_DIR/druid.conf:$SUPERVISORDIR/druid.conf --link druid-zookeeper-kafka:druid-zookeeper-kafka --link druid-coordinator:druid-coordinator --link druid-broker:druid-broker druid/cluster - - # Start Router with custom TLS cert checkers - docker run -d --privileged --net druid-it-net --ip 172.172.172.12 ${COMMON_ENV} ${ROUTER_CUSTOM_CHECK_TLS_ENV} ${OVERRIDE_ENV} --hostname druid-router-custom-check-tls --name druid-router-custom-check-tls -p 5003:5003 -p 8891:8891 -p 9091:9091 -v $SHARED_DIR:/shared -v $SERVICE_SUPERVISORDS_DIR/druid.conf:$SUPERVISORDIR/druid.conf --link druid-zookeeper-kafka:druid-zookeeper-kafka --link druid-coordinator:druid-coordinator --link druid-broker:druid-broker druid/cluster - } \ No newline at end of file diff --git a/integration-tests/script/copy_hadoop_resources.sh b/integration-tests/script/copy_hadoop_resources.sh new file mode 100755 index 000000000000..82dd0d023d59 --- /dev/null +++ b/integration-tests/script/copy_hadoop_resources.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# wait for hadoop namenode to be up +echo "Waiting for hadoop namenode to be up" +docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -mkdir -p /druid" +while [ $? -ne 0 ] +do + sleep 2 + docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -mkdir -p /druid" +done +echo "Finished waiting for Hadoop namenode" + +# Setup hadoop druid dirs +echo "Setting up druid hadoop dirs" +docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -mkdir -p /druid" +docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -mkdir -p /druid/segments" +docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -mkdir -p /quickstart" +docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -chmod 777 /druid" +docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -chmod 777 /druid/segments" +docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -chmod 777 /quickstart" +docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -chmod -R 777 /tmp" +docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -chmod -R 777 /user" +# Copy data files to Hadoop container +docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -put /shared/wikiticker-it/wikiticker-2015-09-12-sampled.json.gz /quickstart/wikiticker-2015-09-12-sampled.json.gz" +docker exec -t druid-it-hadoop sh -c "./usr/local/hadoop/bin/hdfs dfs -put /resources/data/batch_index /batch_index" +echo "Finished setting up druid hadoop dirs" + +echo "Copying Hadoop XML files to shared" +docker exec -t druid-it-hadoop sh -c "cp /usr/local/hadoop/etc/hadoop/*.xml /shared/hadoop_xml" +echo "Copied Hadoop XML files to shared" \ No newline at end of file diff --git a/integration-tests/script/copy_resources.sh b/integration-tests/script/copy_resources.sh new file mode 100755 index 000000000000..eb3a1b594d64 --- /dev/null +++ b/integration-tests/script/copy_resources.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# setup client keystore +./docker/tls/generate-client-certs-and-keystores.sh +rm -rf docker/client_tls +cp -r client_tls docker/client_tls + +# Make directories if they dont exist +mkdir -p $SHARED_DIR/hadoop_xml +mkdir -p $SHARED_DIR/hadoop-dependencies +mkdir -p $SHARED_DIR/logs +mkdir -p $SHARED_DIR/tasklogs +mkdir -p $SHARED_DIR/docker/extensions +mkdir -p $SHARED_DIR/docker/credentials + +# install druid jars +rm -rf $SHARED_DIR/docker +cp -R docker $SHARED_DIR/docker +mvn -B dependency:copy-dependencies -DoutputDirectory=$SHARED_DIR/docker/lib + +# move extensions into a seperate extension folder +# For druid-s3-extensions +mkdir -p $SHARED_DIR/docker/extensions/druid-s3-extensions +mv $SHARED_DIR/docker/lib/druid-s3-extensions-* $SHARED_DIR/docker/extensions/druid-s3-extensions +# For druid-azure-extensions +mkdir -p $SHARED_DIR/docker/extensions/druid-azure-extensions +mv $SHARED_DIR/docker/lib/druid-azure-extensions-* $SHARED_DIR/docker/extensions/druid-azure-extensions +# For druid-google-extensions +mkdir -p $SHARED_DIR/docker/extensions/druid-google-extensions +mv $SHARED_DIR/docker/lib/druid-google-extensions-* $SHARED_DIR/docker/extensions/druid-google-extensions +# For druid-hdfs-storage +mkdir -p $SHARED_DIR/docker/extensions/druid-hdfs-storage +mv $SHARED_DIR/docker/lib/druid-hdfs-storage-* $SHARED_DIR/docker/extensions/druid-hdfs-storage +# For druid-kinesis-indexing-service +mkdir -p $SHARED_DIR/docker/extensions/druid-kinesis-indexing-service +mv $SHARED_DIR/docker/lib/druid-kinesis-indexing-service-* $SHARED_DIR/docker/extensions/druid-kinesis-indexing-service +# For druid-parquet-extensions +mkdir -p $SHARED_DIR/docker/extensions/druid-parquet-extensions +mv $SHARED_DIR/docker/lib/druid-parquet-extensions-* $SHARED_DIR/docker/extensions/druid-parquet-extensions +# For druid-orc-extensions +mkdir -p $SHARED_DIR/docker/extensions/druid-orc-extensions +mv $SHARED_DIR/docker/lib/druid-orc-extensions-* $SHARED_DIR/docker/extensions/druid-orc-extensions + +# Pull Hadoop dependency if needed +if [ -n "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" ] && [ "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" == true ] +then + java -cp "$SHARED_DIR/docker/lib/*" -Ddruid.extensions.hadoopDependenciesDir="$SHARED_DIR/hadoop-dependencies" org.apache.druid.cli.Main tools pull-deps -h org.apache.hadoop:hadoop-client:2.8.5 -h org.apache.hadoop:hadoop-aws:2.8.5 -h org.apache.hadoop:hadoop-azure:2.8.5 + curl https://storage.googleapis.com/hadoop-lib/gcs/gcs-connector-hadoop2-latest.jar --output $SHARED_DIR/docker/lib/gcs-connector-hadoop2-latest.jar +fi + +# install logging config +cp src/main/resources/log4j2.xml $SHARED_DIR/docker/lib/log4j2.xml + +# copy the integration test jar, it provides test-only extension implementations +cp target/druid-integration-tests*.jar $SHARED_DIR/docker/lib + +# one of the integration tests needs the wikiticker sample data +mkdir -p $SHARED_DIR/wikiticker-it +cp ../examples/quickstart/tutorial/wikiticker-2015-09-12-sampled.json.gz $SHARED_DIR/wikiticker-it/wikiticker-2015-09-12-sampled.json.gz +cp docker/wiki-simple-lookup.json $SHARED_DIR/wikiticker-it/wiki-simple-lookup.json + +# copy other files if needed +if [ -n "$DRUID_INTEGRATION_TEST_RESOURCE_FILE_DIR_PATH" ] +then + cp -a $DRUID_INTEGRATION_TEST_RESOURCE_FILE_DIR_PATH/. $SHARED_DIR/docker/credentials/ +fi \ No newline at end of file diff --git a/integration-tests/script/docker_build_containers.sh b/integration-tests/script/docker_build_containers.sh new file mode 100755 index 000000000000..30aa56a8de8f --- /dev/null +++ b/integration-tests/script/docker_build_containers.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Build Druid Cluster Image +if [ -z "$DRUID_INTEGRATION_TEST_JVM_RUNTIME" ] +then + echo "\$DRUID_INTEGRATION_TEST_JVM_RUNTIME is not set. Build druid-cluster with Java 8" + docker build -t druid/cluster --build-arg DOCKER_IMAGE=imply/druiditbase:openjdk-1.8.0_191-1 $SHARED_DIR/docker +else + echo "\$DRUID_INTEGRATION_TEST_JVM_RUNTIME is set with value ${DRUID_INTEGRATION_TEST_JVM_RUNTIME}" + case "${DRUID_INTEGRATION_TEST_JVM_RUNTIME}" in + 8) + echo "Build druid-cluster with Java 8" + docker build -t druid/cluster --build-arg DOCKER_IMAGE=imply/druiditbase:openjdk-1.8.0_191-1 $SHARED_DIR/docker + ;; + 11) + echo "Build druid-cluster with Java 11" + docker build -t druid/cluster --build-arg DOCKER_IMAGE=imply/druiditbase:openjdk-11.0.5-1 $SHARED_DIR/docker + ;; + *) + echo "Invalid JVM Runtime given. Stopping" + exit 1 + ;; + esac +fi + +# Build Hadoop docker if needed +if [ -n "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" ] && [ "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" == true ] +then + docker build -t druid-it/hadoop:2.8.5 $HADOOP_DOCKER_DIR +fi diff --git a/integration-tests/script/docker_run_cluster.sh b/integration-tests/script/docker_run_cluster.sh new file mode 100755 index 000000000000..48fcd02c923e --- /dev/null +++ b/integration-tests/script/docker_run_cluster.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Create docker network +{ + docker network create --subnet=172.172.172.0/24 druid-it-net +} + +if [ -z "$DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH" ] +then + echo "\$DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH is not set. No override config file provided" + if [ "$DRUID_INTEGRATION_TEST_GROUP" = "s3-deep-storage" ] || \ + [ "$DRUID_INTEGRATION_TEST_GROUP" = "gcs-deep-storage" ] || \ + [ "$DRUID_INTEGRATION_TEST_GROUP" = "azure-deep-storage" ] || \ + [ "$DRUID_INTEGRATION_TEST_GROUP" = "hdfs-deep-storage" ] || \ + [ "$DRUID_INTEGRATION_TEST_GROUP" = "s3-ingestion" ] || \ + [ "$DRUID_INTEGRATION_TEST_GROUP" = "kinesis-index" ] || \ + [ "$DRUID_INTEGRATION_TEST_GROUP" = "kinesis-data-format" ]; then + echo "Test group $DRUID_INTEGRATION_TEST_GROUP requires override config file. Stopping test..." + exit 1 + fi +else + echo "\$DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH is set with value ${DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH}" +fi + +# Start docker containers for all Druid processes and dependencies +{ + # Start Hadoop docker if needed + if [ -n "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" ] && [ "$DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER" == true ] + then + # Start Hadoop docker container + docker-compose -f ${DOCKERDIR}/docker-compose.druid-hadoop.yml up -d + fi + + if [ -z "$DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH" ] + then + docker-compose -f ${DOCKERDIR}/docker-compose.yml up -d + else + # run druid cluster with override config + OVERRIDE_ENV=$DRUID_INTEGRATION_TEST_OVERRIDE_CONFIG_PATH docker-compose -f ${DOCKERDIR}/docker-compose.override-env.yml up -d + fi +} diff --git a/integration-tests/stop_cluster.sh b/integration-tests/stop_cluster.sh index 2828a0ff8a96..d75e73faca5e 100755 --- a/integration-tests/stop_cluster.sh +++ b/integration-tests/stop_cluster.sh @@ -15,16 +15,15 @@ # limitations under the License. # Skip stopping docker if flag set (For use during development) -if [ -n "$DRUID_INTEGRATION_TEST_SKIP_STOP_DOCKER" ] && [ "$DRUID_INTEGRATION_TEST_SKIP_STOP_DOCKER" == true ] +if [ -n "$DRUID_INTEGRATION_TEST_SKIP_RUN_DOCKER" ] && [ "$DRUID_INTEGRATION_TEST_SKIP_RUN_DOCKER" == true ] then exit 0 fi for node in druid-historical druid-coordinator druid-overlord druid-router druid-router-permissive-tls druid-router-no-client-auth-tls druid-router-custom-check-tls druid-broker druid-middlemanager druid-zookeeper-kafka druid-metadata-storage druid-it-hadoop; - do -docker stop $node -docker rm $node + docker stop $node + docker rm $node done docker network rm druid-it-net From c4f3043701454d30dcb71d03416051327a758186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20L=C3=A9aut=C3=A9?= Date: Tue, 2 Jun 2020 10:13:54 -0700 Subject: [PATCH 049/107] fix nullhandling exceptions related to test ordering (#9964) follow-up to https://github.com/apache/druid/pull/9570 --- .../java/org/apache/druid/query/topn/TopNUnionQueryTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/processing/src/test/java/org/apache/druid/query/topn/TopNUnionQueryTest.java b/processing/src/test/java/org/apache/druid/query/topn/TopNUnionQueryTest.java index 7a2607662380..f5797dba3c9f 100644 --- a/processing/src/test/java/org/apache/druid/query/topn/TopNUnionQueryTest.java +++ b/processing/src/test/java/org/apache/druid/query/topn/TopNUnionQueryTest.java @@ -33,6 +33,7 @@ import org.apache.druid.query.aggregation.DoubleMaxAggregatorFactory; import org.apache.druid.query.aggregation.DoubleMinAggregatorFactory; import org.apache.druid.segment.TestHelper; +import org.apache.druid.testing.InitializedNullHandlingTest; import org.junit.AfterClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,7 +47,7 @@ import java.util.Map; @RunWith(Parameterized.class) -public class TopNUnionQueryTest +public class TopNUnionQueryTest extends InitializedNullHandlingTest { private static final Closer RESOURCE_CLOSER = Closer.create(); From 09efa25df5fb511ae3203a1f9135afbbe5545ca0 Mon Sep 17 00:00:00 2001 From: Chi Cao Minh Date: Tue, 2 Jun 2020 15:34:58 -0700 Subject: [PATCH 050/107] Adjust code coverage check (#9969) Since there is not currently a good way to have fine-grain code coverage check exclusions, lower the coverage thresholds to make the check more lenient for now. Also, display the code coverage report in the Travis CI logs to make it easier to understand how to improve coverage. --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 401af9c99520..9b3c755107c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -175,9 +175,13 @@ jobs: | node_modules/.bin/diff-test-coverage --coverage "**/target/site/jacoco/jacoco.xml" --type jacoco - --line-coverage 65 - --branch-coverage 65 + --line-coverage 50 + --branch-coverage 50 --function-coverage 0 + --log-template "coverage-lines-complete" + --log-template "coverage-files-complete" + --log-template "totals-complete" + --log-template "errors" -- || { printf "\nDiff code coverage check failed. To view coverage report, run 'mvn clean test jacoco:report' and open 'target/site/jacoco/index.html'\n" && false; } fi From de6c60c73d07e13d84296da0dab7b638d85a6039 Mon Sep 17 00:00:00 2001 From: Gian Merlino Date: Tue, 2 Jun 2020 18:26:06 -0700 Subject: [PATCH 051/107] Fix various Yielder leaks. (#9934) * Fix various Yielder leaks. - CombiningSequence leaked the input yielder from "toYielder" if it ran into an exception while accumulating the last value from the input yielder. - MergeSequence leaked input yielders from "toYielder" if it ran into an exception while building the initial priority queue. - ScanQueryRunnerFactory leaked the input yielder in its "priorityQueueSortAndLimit" strategy if it ran into an exception while scanning and sorting. - YieldingSequenceBase.accumulate chomped IOExceptions thrown in "accumulate" during yielder closing. * Add tests. * Fix braces. --- .../druid/common/guava/CombiningSequence.java | 19 ++- .../util/common/guava/ExplodingSequence.java | 95 +++++++++++ .../java/util/common/guava/MergeSequence.java | 109 +++++++++---- .../common/guava/YieldingSequenceBase.java | 25 ++- .../common/guava/CombiningSequenceTest.java | 39 +++++ .../util/common/guava/MergeSequenceTest.java | 147 +++++++++++++++++- .../guava/YieldingSequenceBaseTest.java | 110 +++++++++++++ .../query/scan/ScanQueryRunnerFactory.java | 112 +++++++------ .../scan/ScanQueryRunnerFactoryTest.java | 3 +- 9 files changed, 563 insertions(+), 96 deletions(-) create mode 100644 core/src/main/java/org/apache/druid/java/util/common/guava/ExplodingSequence.java create mode 100644 core/src/test/java/org/apache/druid/java/util/common/guava/YieldingSequenceBaseTest.java diff --git a/core/src/main/java/org/apache/druid/common/guava/CombiningSequence.java b/core/src/main/java/org/apache/druid/common/guava/CombiningSequence.java index 9e9a7d77df57..b779fc29d356 100644 --- a/core/src/main/java/org/apache/druid/common/guava/CombiningSequence.java +++ b/core/src/main/java/org/apache/druid/common/guava/CombiningSequence.java @@ -29,8 +29,6 @@ import java.util.Comparator; import java.util.function.BinaryOperator; -/** - */ public class CombiningSequence implements Sequence { public static CombiningSequence create( @@ -76,9 +74,22 @@ public Yielder toYielder(OutType initValue, final YieldingAcc new CombiningYieldingAccumulator<>(ordering, mergeFn, accumulator); combiningAccumulator.setRetVal(initValue); - Yielder baseYielder = baseSequence.toYielder(null, combiningAccumulator); - return makeYielder(baseYielder, combiningAccumulator, false); + final Yielder baseYielder = baseSequence.toYielder(null, combiningAccumulator); + + try { + return makeYielder(baseYielder, combiningAccumulator, false); + } + catch (Throwable t1) { + try { + baseYielder.close(); + } + catch (Throwable t2) { + t1.addSuppressed(t2); + } + + throw t1; + } } private Yielder makeYielder( diff --git a/core/src/main/java/org/apache/druid/java/util/common/guava/ExplodingSequence.java b/core/src/main/java/org/apache/druid/java/util/common/guava/ExplodingSequence.java new file mode 100644 index 000000000000..5468a5d86033 --- /dev/null +++ b/core/src/main/java/org/apache/druid/java/util/common/guava/ExplodingSequence.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.java.util.common.guava; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Wraps an underlying sequence and allows us to force it to explode at various points. + */ +public class ExplodingSequence extends YieldingSequenceBase +{ + private final Sequence baseSequence; + private final boolean getThrowsException; + private final boolean closeThrowsException; + private final AtomicLong closed = new AtomicLong(); + + public ExplodingSequence(Sequence baseSequence, boolean getThrowsException, boolean closeThrowsException) + { + this.baseSequence = baseSequence; + this.getThrowsException = getThrowsException; + this.closeThrowsException = closeThrowsException; + } + + @Override + public Yielder toYielder( + final OutType initValue, + final YieldingAccumulator accumulator + ) + { + return wrapYielder(baseSequence.toYielder(initValue, accumulator)); + } + + public long getCloseCount() + { + return closed.get(); + } + + private Yielder wrapYielder(final Yielder baseYielder) + { + return new Yielder() + { + @Override + public OutType get() + { + if (getThrowsException) { + throw new RuntimeException("get"); + } else { + return baseYielder.get(); + } + } + + @Override + public Yielder next(OutType initValue) + { + return wrapYielder(baseYielder.next(initValue)); + } + + @Override + public boolean isDone() + { + return baseYielder.isDone(); + } + + @Override + public void close() throws IOException + { + closed.incrementAndGet(); + + if (closeThrowsException) { + throw new IOException("close"); + } else { + baseYielder.close(); + } + } + }; + } +} diff --git a/core/src/main/java/org/apache/druid/java/util/common/guava/MergeSequence.java b/core/src/main/java/org/apache/druid/java/util/common/guava/MergeSequence.java index 912775cba8ed..90f9b9a48ff3 100644 --- a/core/src/main/java/org/apache/druid/java/util/common/guava/MergeSequence.java +++ b/core/src/main/java/org/apache/druid/java/util/common/guava/MergeSequence.java @@ -58,38 +58,62 @@ public Yielder toYielder(OutType initValue, YieldingAccumulat ) ); - pQueue = baseSequences.accumulate( - pQueue, - (queue, in) -> { - final Yielder yielder = in.toYielder( - null, - new YieldingAccumulator() - { - @Override - public T accumulate(T accumulated, T in) + try { + pQueue = baseSequences.accumulate( + pQueue, + (queue, in) -> { + final Yielder yielder = in.toYielder( + null, + new YieldingAccumulator() { - yield(); - return in; + @Override + public T accumulate(T accumulated, T in) + { + yield(); + return in; + } } + ); + + if (!yielder.isDone()) { + try { + queue.add(yielder); } - ); + catch (Throwable t1) { + try { + yielder.close(); + } + catch (Throwable t2) { + t1.addSuppressed(t2); + } - if (!yielder.isDone()) { - queue.add(yielder); - } else { - try { - yielder.close(); - } - catch (IOException e) { - throw new RuntimeException(e); + throw t1; + } + } else { + try { + yielder.close(); + } + catch (IOException e) { + throw new RuntimeException(e); + } } + + return queue; } + ); - return queue; - } - ); + return makeYielder(pQueue, initValue, accumulator); + } + catch (Throwable t1) { + try { + closeAll(pQueue); + } + catch (Throwable t2) { + t1.addSuppressed(t2); + } - return makeYielder(pQueue, initValue, accumulator); + throw t1; + } } private Yielder makeYielder( @@ -101,8 +125,22 @@ private Yielder makeYielder( OutType retVal = initVal; while (!accumulator.yielded() && !pQueue.isEmpty()) { Yielder yielder = pQueue.remove(); - retVal = accumulator.accumulate(retVal, yielder.get()); - yielder = yielder.next(null); + + try { + retVal = accumulator.accumulate(retVal, yielder.get()); + yielder = yielder.next(null); + } + catch (Throwable t1) { + try { + yielder.close(); + } + catch (Throwable t2) { + t1.addSuppressed(t2); + } + + throw t1; + } + if (yielder.isDone()) { try { yielder.close(); @@ -144,12 +182,21 @@ public boolean isDone() @Override public void close() throws IOException { - Closer closer = Closer.create(); - while (!pQueue.isEmpty()) { - closer.register(pQueue.remove()); - } - closer.close(); + closeAll(pQueue); } }; } + + private static void closeAll(final PriorityQueue> pQueue) throws IOException + { + Closer closer = Closer.create(); + while (!pQueue.isEmpty()) { + final Yielder yielder = pQueue.poll(); + if (yielder != null) { + // Note: yielder can be null if our comparator threw an exception during queue.add. + closer.register(yielder); + } + } + closer.close(); + } } diff --git a/core/src/main/java/org/apache/druid/java/util/common/guava/YieldingSequenceBase.java b/core/src/main/java/org/apache/druid/java/util/common/guava/YieldingSequenceBase.java index c5ed0111d074..9247989f5f64 100644 --- a/core/src/main/java/org/apache/druid/java/util/common/guava/YieldingSequenceBase.java +++ b/core/src/main/java/org/apache/druid/java/util/common/guava/YieldingSequenceBase.java @@ -19,6 +19,8 @@ package org.apache.druid.java.util.common.guava; +import java.io.IOException; + /** * A Sequence that is based entirely on the Yielder implementation. *

@@ -29,16 +31,33 @@ public abstract class YieldingSequenceBase implements Sequence @Override public OutType accumulate(OutType initValue, Accumulator accumulator) { + final OutType retVal; Yielder yielder = toYielder(initValue, YieldingAccumulators.fromAccumulator(accumulator)); try { while (!yielder.isDone()) { yielder = yielder.next(yielder.get()); } - return yielder.get(); + retVal = yielder.get(); } - finally { - CloseQuietly.close(yielder); + catch (Throwable t1) { + try { + yielder.close(); + } + catch (Throwable t2) { + t1.addSuppressed(t2); + } + + throw t1; } + + try { + yielder.close(); + } + catch (IOException e) { + throw new RuntimeException(e); + } + + return retVal; } } diff --git a/core/src/test/java/org/apache/druid/common/guava/CombiningSequenceTest.java b/core/src/test/java/org/apache/druid/common/guava/CombiningSequenceTest.java index 9f64d6732863..b8872f08d385 100644 --- a/core/src/test/java/org/apache/druid/common/guava/CombiningSequenceTest.java +++ b/core/src/test/java/org/apache/druid/common/guava/CombiningSequenceTest.java @@ -20,17 +20,21 @@ package org.apache.druid.common.guava; import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; import org.apache.druid.java.util.common.Pair; +import org.apache.druid.java.util.common.guava.ExplodingSequence; import org.apache.druid.java.util.common.guava.Sequence; import org.apache.druid.java.util.common.guava.Sequences; import org.apache.druid.java.util.common.guava.Yielder; import org.apache.druid.java.util.common.guava.YieldingAccumulator; +import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.Test; +import org.junit.internal.matchers.ThrowableMessageMatcher; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -39,6 +43,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -194,6 +199,40 @@ public void testNothing() throws Exception testCombining(Collections.emptyList(), Collections.emptyList()); } + @Test + public void testExplodingSequence() + { + final ExplodingSequence bomb = + new ExplodingSequence<>(Sequences.simple(ImmutableList.of(1, 2, 2)), false, true); + + final CombiningSequence combiningSequence = + CombiningSequence.create(bomb, Comparator.naturalOrder(), (a, b) -> a); + + try { + combiningSequence.toYielder( + null, + new YieldingAccumulator() + { + @Override + public Integer accumulate(Integer accumulated, Integer in) + { + if (in > 1) { + throw new RuntimeException("boom"); + } + + return in; + } + } + ); + Assert.fail("Expected exception"); + } + catch (Exception e) { + Assert.assertThat(e, ThrowableMessageMatcher.hasMessage(CoreMatchers.equalTo("boom"))); + } + + Assert.assertEquals("Closes resources", 1, bomb.getCloseCount()); + } + private void testCombining(List> pairs, List> expected) throws Exception { diff --git a/core/src/test/java/org/apache/druid/java/util/common/guava/MergeSequenceTest.java b/core/src/test/java/org/apache/druid/java/util/common/guava/MergeSequenceTest.java index 61af63869487..f7817c75293a 100644 --- a/core/src/test/java/org/apache/druid/java/util/common/guava/MergeSequenceTest.java +++ b/core/src/test/java/org/apache/druid/java/util/common/guava/MergeSequenceTest.java @@ -19,10 +19,13 @@ package org.apache.druid.java.util.common.guava; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; -import junit.framework.Assert; +import org.hamcrest.CoreMatchers; +import org.junit.Assert; import org.junit.Test; +import org.junit.internal.matchers.ThrowableMessageMatcher; import java.util.ArrayList; import java.util.Arrays; @@ -41,7 +44,10 @@ public void testSanity() throws Exception TestSequence.create(4, 6, 8) ); - MergeSequence seq = new MergeSequence<>(Ordering.natural(), (Sequence) Sequences.simple(testSeqs)); + MergeSequence seq = new MergeSequence<>( + Ordering.natural(), + (Sequence) Sequences.simple(testSeqs) + ); SequenceTestHelper.testAll(seq, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 8, 9)); for (TestSequence sequence : testSeqs) { @@ -58,7 +64,10 @@ public void testWorksWhenBeginningOutOfOrder() throws Exception TestSequence.create(4, 6, 8) ); - MergeSequence seq = new MergeSequence<>(Ordering.natural(), (Sequence) Sequences.simple(testSeqs)); + MergeSequence seq = new MergeSequence<>( + Ordering.natural(), + (Sequence) Sequences.simple(testSeqs) + ); SequenceTestHelper.testAll(seq, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 8, 9)); for (TestSequence sequence : testSeqs) { @@ -76,7 +85,10 @@ public void testMergeEmpties() throws Exception TestSequence.create(4, 6, 8) ); - MergeSequence seq = new MergeSequence<>(Ordering.natural(), (Sequence) Sequences.simple(testSeqs)); + MergeSequence seq = new MergeSequence<>( + Ordering.natural(), + (Sequence) Sequences.simple(testSeqs) + ); SequenceTestHelper.testAll(seq, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 8, 9)); for (TestSequence sequence : testSeqs) { @@ -94,7 +106,10 @@ public void testMergeEmpties1() throws Exception TestSequence.create(4, 6, 8) ); - MergeSequence seq = new MergeSequence<>(Ordering.natural(), (Sequence) Sequences.simple(testSeqs)); + MergeSequence seq = new MergeSequence<>( + Ordering.natural(), + (Sequence) Sequences.simple(testSeqs) + ); SequenceTestHelper.testAll(seq, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 8, 9)); for (TestSequence sequence : testSeqs) { @@ -113,7 +128,10 @@ public void testMergeEmpties2() throws Exception TestSequence.create() ); - MergeSequence seq = new MergeSequence<>(Ordering.natural(), (Sequence) Sequences.simple(testSeqs)); + MergeSequence seq = new MergeSequence<>( + Ordering.natural(), + (Sequence) Sequences.simple(testSeqs) + ); SequenceTestHelper.testAll(seq, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 8, 9)); for (TestSequence sequence : testSeqs) { @@ -130,7 +148,10 @@ public void testScrewsUpOnOutOfOrder() throws Exception TestSequence.create(4, 6) ); - MergeSequence seq = new MergeSequence<>(Ordering.natural(), (Sequence) Sequences.simple(testSeqs)); + MergeSequence seq = new MergeSequence<>( + Ordering.natural(), + (Sequence) Sequences.simple(testSeqs) + ); SequenceTestHelper.testAll(seq, Arrays.asList(1, 2, 3, 4, 5, 4, 6, 7, 8, 9)); for (TestSequence sequence : testSeqs) { @@ -169,4 +190,116 @@ public void testMergeOne() throws Exception SequenceTestHelper.testAll(mergeOne, Collections.singletonList(1)); } + @Test + public void testTwoExplodingOnGetSequences() + { + final ExplodingSequence bomb1 = + new ExplodingSequence<>(Sequences.simple(ImmutableList.of(1, 2, 2)), true, false); + final ExplodingSequence bomb2 = + new ExplodingSequence<>(Sequences.simple(ImmutableList.of(1, 2, 2)), true, false); + + final MergeSequence mergeSequence = + new MergeSequence<>( + Ordering.natural(), + Sequences.simple(ImmutableList.of(bomb1, bomb2)) + ); + + try { + mergeSequence.toYielder( + null, + new YieldingAccumulator() + { + @Override + public Integer accumulate(Integer accumulated, Integer in) + { + return in; + } + } + ); + Assert.fail("Expected exception"); + } + catch (Exception e) { + Assert.assertThat(e, ThrowableMessageMatcher.hasMessage(CoreMatchers.equalTo("get"))); + } + + Assert.assertEquals("Closes resources (1)", 1, bomb1.getCloseCount()); + Assert.assertEquals("Closes resources (2)", 1, bomb2.getCloseCount()); + } + + @Test + public void testTwoExplodingOnCloseSequences() + { + final ExplodingSequence bomb1 = + new ExplodingSequence<>(Sequences.simple(ImmutableList.of(1, 2, 2)), false, true); + final ExplodingSequence bomb2 = + new ExplodingSequence<>(Sequences.simple(ImmutableList.of(1, 2, 2)), false, true); + + final MergeSequence mergeSequence = + new MergeSequence<>( + Ordering.natural(), + Sequences.simple(ImmutableList.of(bomb1, bomb2)) + ); + + try { + mergeSequence.toYielder( + null, + new YieldingAccumulator() + { + @Override + public Integer accumulate(Integer accumulated, Integer in) + { + if (in > 1) { + throw new RuntimeException("boom"); + } + + return in; + } + } + ); + Assert.fail("Expected exception"); + } + catch (Exception e) { + Assert.assertThat(e, ThrowableMessageMatcher.hasMessage(CoreMatchers.equalTo("boom"))); + } + + Assert.assertEquals("Closes resources (1)", 1, bomb1.getCloseCount()); + Assert.assertEquals("Closes resources (2)", 1, bomb2.getCloseCount()); + } + + @Test + public void testOneEmptyOneExplodingSequence() + { + final ExplodingSequence bomb = + new ExplodingSequence<>(Sequences.simple(ImmutableList.of(1, 2, 2)), false, true); + + final MergeSequence mergeSequence = + new MergeSequence<>( + Ordering.natural(), + Sequences.simple(ImmutableList.of(Sequences.empty(), bomb)) + ); + + try { + mergeSequence.toYielder( + null, + new YieldingAccumulator() + { + @Override + public Integer accumulate(Integer accumulated, Integer in) + { + if (in > 1) { + throw new RuntimeException("boom"); + } + + return in; + } + } + ); + Assert.fail("Expected exception"); + } + catch (Exception e) { + Assert.assertThat(e, ThrowableMessageMatcher.hasMessage(CoreMatchers.equalTo("boom"))); + } + + Assert.assertEquals("Closes resources", 1, bomb.getCloseCount()); + } } diff --git a/core/src/test/java/org/apache/druid/java/util/common/guava/YieldingSequenceBaseTest.java b/core/src/test/java/org/apache/druid/java/util/common/guava/YieldingSequenceBaseTest.java new file mode 100644 index 000000000000..19e396bc064c --- /dev/null +++ b/core/src/test/java/org/apache/druid/java/util/common/guava/YieldingSequenceBaseTest.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.java.util.common.guava; + +import com.google.common.collect.ImmutableList; +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Test; +import org.junit.internal.matchers.ThrowableCauseMatcher; +import org.junit.internal.matchers.ThrowableMessageMatcher; + +import java.util.ArrayList; + +public class YieldingSequenceBaseTest +{ + @Test + public void testAccumulate() + { + final ExplodingSequence sequence = new ExplodingSequence<>( + Sequences.simple(ImmutableList.of(1, 2, 3)), + false, + false + ); + + Assert.assertEquals(ImmutableList.of(1, 2, 3), sequence.accumulate(new ArrayList<>(), Accumulators.list())); + Assert.assertEquals("Closes resources", 1, sequence.getCloseCount()); + } + + @Test + public void testExceptionDuringGet() + { + final ExplodingSequence sequence = new ExplodingSequence<>( + Sequences.simple(ImmutableList.of(1, 2, 3)), + true, + false + ); + + try { + sequence.accumulate(new ArrayList<>(), Accumulators.list()); + Assert.fail("Expected exception"); + } + catch (Exception e) { + Assert.assertThat(e, ThrowableMessageMatcher.hasMessage(CoreMatchers.equalTo("get"))); + } + + Assert.assertEquals("Closes resources", 1, sequence.getCloseCount()); + } + + @Test + public void testExceptionDuringClose() + { + final ExplodingSequence sequence = new ExplodingSequence<>( + Sequences.simple(ImmutableList.of(1, 2, 3)), + false, + true + ); + + try { + sequence.accumulate(new ArrayList<>(), Accumulators.list()); + Assert.fail("Expected exception"); + } + catch (Exception e) { + Assert.assertThat( + e, + + // Wrapped one level deep because it's an IOException + ThrowableCauseMatcher.hasCause(ThrowableMessageMatcher.hasMessage(CoreMatchers.equalTo("close"))) + ); + } + + Assert.assertEquals("Closes resources", 1, sequence.getCloseCount()); + } + + @Test + public void testExceptionDuringGetAndClose() + { + final ExplodingSequence sequence = new ExplodingSequence<>( + Sequences.simple(ImmutableList.of(1, 2, 3)), + true, + true + ); + + try { + sequence.accumulate(new ArrayList<>(), Accumulators.list()); + Assert.fail("Expected exception"); + } + catch (Exception e) { + Assert.assertThat(e, ThrowableMessageMatcher.hasMessage(CoreMatchers.equalTo("get"))); + } + + Assert.assertEquals("Closes resources", 1, sequence.getCloseCount()); + } +} diff --git a/processing/src/main/java/org/apache/druid/query/scan/ScanQueryRunnerFactory.java b/processing/src/main/java/org/apache/druid/query/scan/ScanQueryRunnerFactory.java index 8233315d5e83..52066ab1f25e 100644 --- a/processing/src/main/java/org/apache/druid/query/scan/ScanQueryRunnerFactory.java +++ b/processing/src/main/java/org/apache/druid/query/scan/ScanQueryRunnerFactory.java @@ -46,6 +46,7 @@ import org.apache.druid.segment.Segment; import org.joda.time.Interval; +import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; @@ -83,7 +84,7 @@ public QueryRunner createRunner(Segment segment) @Override public QueryRunner mergeRunners( - ExecutorService queryExecutor, + final ExecutorService queryExecutor, final Iterable> queryRunners ) { @@ -122,14 +123,19 @@ public QueryRunner mergeRunners( : query.getMaxRowsQueuedForOrdering()); if (query.getScanRowsLimit() <= maxRowsQueuedForOrdering) { // Use priority queue strategy - return priorityQueueSortAndLimit( - Sequences.concat(Sequences.map( - Sequences.simple(queryRunnersOrdered), - input -> input.run(queryPlus, responseContext) - )), - query, - intervalsOrdered - ); + try { + return priorityQueueSortAndLimit( + Sequences.concat(Sequences.map( + Sequences.simple(queryRunnersOrdered), + input -> input.run(queryPlus, responseContext) + )), + query, + intervalsOrdered + ); + } + catch (IOException e) { + throw new RuntimeException(e); + } } else { // Use n-way merge strategy List>> intervalsAndRunnersOrdered = new ArrayList<>(); @@ -149,11 +155,11 @@ public QueryRunner mergeRunners( // query runners for that segment LinkedHashMap>>> partitionsGroupedByInterval = intervalsAndRunnersOrdered.stream() - .collect(Collectors.groupingBy( - x -> x.lhs, - LinkedHashMap::new, - Collectors.toList() - )); + .collect(Collectors.groupingBy( + x -> x.lhs, + LinkedHashMap::new, + Collectors.toList() + )); // Find the segment with the largest numbers of partitions. This will be used to compare with the // maxSegmentPartitionsOrderedInMemory limit to determine if the query is at risk of consuming too much memory. @@ -203,7 +209,7 @@ Sequence priorityQueueSortAndLimit( Sequence inputSequence, ScanQuery scanQuery, List intervalsOrdered - ) + ) throws IOException { Comparator priorityQComparator = new ScanResultValueTimestampComparator(scanQuery); @@ -232,48 +238,54 @@ public ScanResultValue accumulate(ScanResultValue accumulated, ScanResultValue i } } ); - boolean doneScanning = yielder.isDone(); - // We need to scan limit elements and anything else in the last segment - int numRowsScanned = 0; - Interval finalInterval = null; - while (!doneScanning) { - ScanResultValue next = yielder.get(); - List singleEventScanResultValues = next.toSingleEventScanResultValues(); - for (ScanResultValue srv : singleEventScanResultValues) { - numRowsScanned++; - // Using an intermediate unbatched ScanResultValue is not that great memory-wise, but the column list - // needs to be preserved for queries using the compactedList result format - q.offer(srv); - if (q.size() > limit) { - q.poll(); - } - // Finish scanning the interval containing the limit row - if (numRowsScanned > limit && finalInterval == null) { - long timestampOfLimitRow = srv.getFirstEventTimestamp(scanQuery.getResultFormat()); - for (Interval interval : intervalsOrdered) { - if (interval.contains(timestampOfLimitRow)) { - finalInterval = interval; - } + try { + boolean doneScanning = yielder.isDone(); + // We need to scan limit elements and anything else in the last segment + int numRowsScanned = 0; + Interval finalInterval = null; + while (!doneScanning) { + ScanResultValue next = yielder.get(); + List singleEventScanResultValues = next.toSingleEventScanResultValues(); + for (ScanResultValue srv : singleEventScanResultValues) { + numRowsScanned++; + // Using an intermediate unbatched ScanResultValue is not that great memory-wise, but the column list + // needs to be preserved for queries using the compactedList result format + q.offer(srv); + if (q.size() > limit) { + q.poll(); } - if (finalInterval == null) { - throw new ISE("WTH??? Row came from an unscanned interval?"); + + // Finish scanning the interval containing the limit row + if (numRowsScanned > limit && finalInterval == null) { + long timestampOfLimitRow = srv.getFirstEventTimestamp(scanQuery.getResultFormat()); + for (Interval interval : intervalsOrdered) { + if (interval.contains(timestampOfLimitRow)) { + finalInterval = interval; + } + } + if (finalInterval == null) { + throw new ISE("WTH??? Row came from an unscanned interval?"); + } } } + yielder = yielder.next(null); + doneScanning = yielder.isDone() || + (finalInterval != null && + !finalInterval.contains(next.getFirstEventTimestamp(scanQuery.getResultFormat()))); + } + // Need to convert to a Deque because Priority Queue's iterator doesn't guarantee that the sorted order + // will be maintained. Deque was chosen over list because its addFirst is O(1). + final Deque sortedElements = new ArrayDeque<>(q.size()); + while (q.size() != 0) { + // addFirst is used since PriorityQueue#poll() dequeues the low-priority (timestamp-wise) events first. + sortedElements.addFirst(q.poll()); } - yielder = yielder.next(null); - doneScanning = yielder.isDone() || - (finalInterval != null && - !finalInterval.contains(next.getFirstEventTimestamp(scanQuery.getResultFormat()))); + return Sequences.simple(sortedElements); } - // Need to convert to a Deque because Priority Queue's iterator doesn't guarantee that the sorted order - // will be maintained. Deque was chosen over list because its addFirst is O(1). - final Deque sortedElements = new ArrayDeque<>(q.size()); - while (q.size() != 0) { - // addFirst is used since PriorityQueue#poll() dequeues the low-priority (timestamp-wise) events first. - sortedElements.addFirst(q.poll()); + finally { + yielder.close(); } - return Sequences.simple(sortedElements); } @VisibleForTesting diff --git a/processing/src/test/java/org/apache/druid/query/scan/ScanQueryRunnerFactoryTest.java b/processing/src/test/java/org/apache/druid/query/scan/ScanQueryRunnerFactoryTest.java index 8038d68a2528..801324847d70 100644 --- a/processing/src/test/java/org/apache/druid/query/scan/ScanQueryRunnerFactoryTest.java +++ b/processing/src/test/java/org/apache/druid/query/scan/ScanQueryRunnerFactoryTest.java @@ -43,6 +43,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -114,7 +115,7 @@ public static Iterable constructorFeeder() } @Test - public void testSortAndLimitScanResultValues() + public void testSortAndLimitScanResultValues() throws IOException { List srvs = new ArrayList<>(numElements); List expectedEventTimestamps = new ArrayList<>(); From c934d97fe37c2e7fb754ede9e5f3121eb1edc2ce Mon Sep 17 00:00:00 2001 From: Gian Merlino Date: Tue, 2 Jun 2020 18:26:18 -0700 Subject: [PATCH 052/107] Fix various processing buffer leaks and simplify BlockingPool. (#9928) * - GroupByQueryEngineV2: Fix leak of intermediate processing buffer when exceptions are thrown before result sequence is created. - PooledTopNAlgorithm: Fix leak of intermediate processing buffer when exceptions are thrown before the PooledTopNParams object is created. - BlockingPool: Remove unused "take" methods. * Add tests to verify that buffers have been returned. --- .../druid/collections/BlockingPool.java | 19 ---- .../collections/DefaultBlockingPool.java | 41 --------- .../druid/collections/DummyBlockingPool.java | 12 --- .../apache/druid/collections/StupidPool.java | 18 +++- .../druid/collections/BlockingPoolTest.java | 11 ++- .../epinephelinae/GroupByQueryEngineV2.java | 92 ++++++++++--------- .../druid/query/topn/PooledTopNAlgorithm.java | 54 ++++++----- .../groupby/GroupByQueryMergeBufferTest.java | 11 --- .../query/groupby/GroupByQueryRunnerTest.java | 6 +- .../druid/query/topn/TopNQueryRunnerTest.java | 9 ++ 10 files changed, 116 insertions(+), 157 deletions(-) diff --git a/core/src/main/java/org/apache/druid/collections/BlockingPool.java b/core/src/main/java/org/apache/druid/collections/BlockingPool.java index 91c3b35b6549..c17329917cd2 100644 --- a/core/src/main/java/org/apache/druid/collections/BlockingPool.java +++ b/core/src/main/java/org/apache/druid/collections/BlockingPool.java @@ -19,31 +19,12 @@ package org.apache.druid.collections; -import javax.annotation.Nullable; import java.util.List; public interface BlockingPool { int maxSize(); - /** - * Take a resource from the pool, waiting up to the - * specified wait time if necessary for an element to become available. - * - * @param timeoutMs maximum time to wait for a resource, in milliseconds. - * - * @return a resource, or null if the timeout was reached - */ - @Nullable - ReferenceCountingResourceHolder take(long timeoutMs); - - /** - * Take a resource from the pool, waiting if necessary until an element becomes available. - * - * @return a resource - */ - ReferenceCountingResourceHolder take(); - /** * Take resources from the pool, waiting up to the * specified wait time if necessary for elements of the given number to become available. diff --git a/core/src/main/java/org/apache/druid/collections/DefaultBlockingPool.java b/core/src/main/java/org/apache/druid/collections/DefaultBlockingPool.java index b1b43fd22477..a32236db20db 100644 --- a/core/src/main/java/org/apache/druid/collections/DefaultBlockingPool.java +++ b/core/src/main/java/org/apache/druid/collections/DefaultBlockingPool.java @@ -76,32 +76,6 @@ public int getPoolSize() return objects.size(); } - @Override - @Nullable - public ReferenceCountingResourceHolder take(final long timeoutMs) - { - Preconditions.checkArgument(timeoutMs >= 0, "timeoutMs must be a non-negative value, but was [%s]", timeoutMs); - checkInitialized(); - try { - return wrapObject(timeoutMs > 0 ? pollObject(timeoutMs) : pollObject()); - } - catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - @Override - public ReferenceCountingResourceHolder take() - { - checkInitialized(); - try { - return wrapObject(takeObject()); - } - catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - @Nullable private ReferenceCountingResourceHolder wrapObject(T theObject) { @@ -144,21 +118,6 @@ private T pollObject(long timeoutMs) throws InterruptedException } } - private T takeObject() throws InterruptedException - { - final ReentrantLock lock = this.lock; - lock.lockInterruptibly(); - try { - while (objects.isEmpty()) { - notEnough.await(); - } - return objects.pop(); - } - finally { - lock.unlock(); - } - } - @Override public List> takeBatch(final int elementNum, final long timeoutMs) { diff --git a/core/src/main/java/org/apache/druid/collections/DummyBlockingPool.java b/core/src/main/java/org/apache/druid/collections/DummyBlockingPool.java index 037a489e1f19..dcd6cea07aa7 100644 --- a/core/src/main/java/org/apache/druid/collections/DummyBlockingPool.java +++ b/core/src/main/java/org/apache/druid/collections/DummyBlockingPool.java @@ -44,18 +44,6 @@ public int maxSize() return 0; } - @Override - public ReferenceCountingResourceHolder take(long timeoutMs) - { - throw new UnsupportedOperationException(); - } - - @Override - public ReferenceCountingResourceHolder take() - { - throw new UnsupportedOperationException(); - } - @Override public List> takeBatch(int elementNum, long timeoutMs) { diff --git a/core/src/main/java/org/apache/druid/collections/StupidPool.java b/core/src/main/java/org/apache/druid/collections/StupidPool.java index a69a8fde30a9..d1d6a9b9b7b1 100644 --- a/core/src/main/java/org/apache/druid/collections/StupidPool.java +++ b/core/src/main/java/org/apache/druid/collections/StupidPool.java @@ -34,6 +34,7 @@ import java.util.concurrent.atomic.AtomicReference; /** + * */ public class StupidPool implements NonBlockingPool { @@ -61,6 +62,7 @@ public class StupidPool implements NonBlockingPool private final String name; private final Supplier generator; + private final AtomicLong createdObjectsCounter = new AtomicLong(0); private final AtomicLong leakedObjectsCounter = new AtomicLong(0); //note that this is just the max entries in the cache, pool can still create as many buffers as needed. @@ -114,6 +116,7 @@ public ResourceHolder take() private ObjectResourceHolder makeObjectWithHandler() { T object = generator.get(); + createdObjectsCounter.incrementAndGet(); ObjectId objectId = new ObjectId(); ObjectLeakNotifier notifier = new ObjectLeakNotifier(this); // Using objectId as referent for Cleaner, because if the object itself (e. g. ByteBuffer) is leaked after taken @@ -122,7 +125,7 @@ private ObjectResourceHolder makeObjectWithHandler() } @VisibleForTesting - long poolSize() + public long poolSize() { return poolSize.get(); } @@ -133,6 +136,12 @@ long leakedObjectsCount() return leakedObjectsCounter.get(); } + @VisibleForTesting + public long objectsCreatedCount() + { + return createdObjectsCounter.get(); + } + private void tryReturnToPool(T object, ObjectId objectId, Cleaners.Cleanable cleanable, ObjectLeakNotifier notifier) { long currentPoolSize; @@ -160,7 +169,12 @@ private void tryReturnToPool(T object, ObjectId objectId, Cleaners.Cleanable cle * This should be impossible, because {@link ConcurrentLinkedQueue#offer(Object)} event don't have `return false;` in * it's body in OpenJDK 8. */ - private void impossibleOffsetFailed(T object, ObjectId objectId, Cleaners.Cleanable cleanable, ObjectLeakNotifier notifier) + private void impossibleOffsetFailed( + T object, + ObjectId objectId, + Cleaners.Cleanable cleanable, + ObjectLeakNotifier notifier + ) { poolSize.decrementAndGet(); notifier.disable(); diff --git a/core/src/test/java/org/apache/druid/collections/BlockingPoolTest.java b/core/src/test/java/org/apache/druid/collections/BlockingPoolTest.java index 9efb9b8341ee..cc5b82ba26e0 100644 --- a/core/src/test/java/org/apache/druid/collections/BlockingPoolTest.java +++ b/core/src/test/java/org/apache/druid/collections/BlockingPoolTest.java @@ -20,6 +20,7 @@ package org.apache.druid.collections; import com.google.common.base.Suppliers; +import com.google.common.collect.Iterables; import org.apache.druid.java.util.common.concurrent.Execs; import org.junit.After; import org.junit.Assert; @@ -66,7 +67,7 @@ public void testTakeFromEmptyPool() { expectedException.expect(IllegalStateException.class); expectedException.expectMessage("Pool was initialized with limit = 0, there are no objects to take."); - emptyPool.take(0); + emptyPool.takeBatch(1, 0); } @Test @@ -80,7 +81,7 @@ public void testDrainFromEmptyPool() @Test(timeout = 60_000L) public void testTake() { - final ReferenceCountingResourceHolder holder = pool.take(100); + final ReferenceCountingResourceHolder holder = Iterables.getOnlyElement(pool.takeBatch(1, 100), null); Assert.assertNotNull(holder); Assert.assertEquals(9, pool.getPoolSize()); holder.close(); @@ -91,7 +92,7 @@ public void testTake() public void testTakeTimeout() { final List> batchHolder = pool.takeBatch(10, 100L); - final ReferenceCountingResourceHolder holder = pool.take(100); + final ReferenceCountingResourceHolder holder = Iterables.getOnlyElement(pool.takeBatch(1, 100), null); Assert.assertNull(holder); batchHolder.forEach(ReferenceCountingResourceHolder::close); } @@ -147,7 +148,7 @@ public void testConcurrentTake() throws ExecutionException, InterruptedException () -> { List> result = new ArrayList<>(); for (int i = 0; i < limit1; i++) { - result.add(pool.take(10)); + result.add(Iterables.getOnlyElement(pool.takeBatch(1, 10), null)); } return result; } @@ -156,7 +157,7 @@ public void testConcurrentTake() throws ExecutionException, InterruptedException () -> { List> result = new ArrayList<>(); for (int i = 0; i < limit2; i++) { - result.add(pool.take(10)); + result.add(Iterables.getOnlyElement(pool.takeBatch(1, 10), null)); } return result; } diff --git a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java index 28399e0dbd35..9a417c9d6290 100644 --- a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java +++ b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java @@ -131,47 +131,53 @@ public static Sequence process( final ResourceHolder bufferHolder = intermediateResultsBufferPool.take(); - final String fudgeTimestampString = NullHandling.emptyToNullIfNeeded( - query.getContextValue(GroupByStrategyV2.CTX_KEY_FUDGE_TIMESTAMP, null) - ); - - final DateTime fudgeTimestamp = fudgeTimestampString == null - ? null - : DateTimes.utc(Long.parseLong(fudgeTimestampString)); + try { + final String fudgeTimestampString = NullHandling.emptyToNullIfNeeded( + query.getContextValue(GroupByStrategyV2.CTX_KEY_FUDGE_TIMESTAMP, null) + ); - final Filter filter = Filters.convertToCNFFromQueryContext(query, Filters.toFilter(query.getFilter())); - final Interval interval = Iterables.getOnlyElement(query.getIntervals()); + final DateTime fudgeTimestamp = fudgeTimestampString == null + ? null + : DateTimes.utc(Long.parseLong(fudgeTimestampString)); - final boolean doVectorize = queryConfig.getVectorize().shouldVectorize( - VectorGroupByEngine.canVectorize(query, storageAdapter, filter) - ); + final Filter filter = Filters.convertToCNFFromQueryContext(query, Filters.toFilter(query.getFilter())); + final Interval interval = Iterables.getOnlyElement(query.getIntervals()); - final Sequence result; - - if (doVectorize) { - result = VectorGroupByEngine.process( - query, - storageAdapter, - bufferHolder.get(), - fudgeTimestamp, - filter, - interval, - querySpecificConfig, - queryConfig + final boolean doVectorize = queryConfig.getVectorize().shouldVectorize( + VectorGroupByEngine.canVectorize(query, storageAdapter, filter) ); - } else { - result = processNonVectorized( - query, - storageAdapter, - bufferHolder.get(), - fudgeTimestamp, - querySpecificConfig, - filter, - interval - ); - } - return result.withBaggage(bufferHolder); + final Sequence result; + + if (doVectorize) { + result = VectorGroupByEngine.process( + query, + storageAdapter, + bufferHolder.get(), + fudgeTimestamp, + filter, + interval, + querySpecificConfig, + queryConfig + ); + } else { + result = processNonVectorized( + query, + storageAdapter, + bufferHolder.get(), + fudgeTimestamp, + querySpecificConfig, + filter, + interval + ); + } + + return result.withBaggage(bufferHolder); + } + catch (Throwable e) { + bufferHolder.close(); + throw e; + } } private static Sequence processNonVectorized( @@ -965,13 +971,13 @@ public Grouper.BufferComparator bufferComparatorWithAggregators( DefaultLimitSpec limitSpec = (DefaultLimitSpec) query.getLimitSpec(); return GrouperBufferComparatorUtils.bufferComparatorWithAggregators( - query.getAggregatorSpecs().toArray(new AggregatorFactory[0]), - aggregatorOffsets, - limitSpec, - query.getDimensions(), - getDimensionComparators(limitSpec), - query.getResultRowHasTimestamp(), - query.getContextSortByDimsFirst() + query.getAggregatorSpecs().toArray(new AggregatorFactory[0]), + aggregatorOffsets, + limitSpec, + query.getDimensions(), + getDimensionComparators(limitSpec), + query.getResultRowHasTimestamp(), + query.getContextSortByDimsFirst() ); } diff --git a/processing/src/main/java/org/apache/druid/query/topn/PooledTopNAlgorithm.java b/processing/src/main/java/org/apache/druid/query/topn/PooledTopNAlgorithm.java index c546b6f97c67..7cd93401b1c0 100644 --- a/processing/src/main/java/org/apache/druid/query/topn/PooledTopNAlgorithm.java +++ b/processing/src/main/java/org/apache/druid/query/topn/PooledTopNAlgorithm.java @@ -49,6 +49,7 @@ import java.util.List; /** + * */ public class PooledTopNAlgorithm extends BaseTopNAlgorithm @@ -211,10 +212,6 @@ public PooledTopNAlgorithm( @Override public PooledTopNParams makeInitParams(ColumnSelectorPlus selectorPlus, Cursor cursor) { - ResourceHolder resultsBufHolder = bufferPool.take(); - ByteBuffer resultsBuf = resultsBufHolder.get(); - resultsBuf.clear(); - final DimensionSelector dimSelector = (DimensionSelector) selectorPlus.getSelector(); final int cardinality = dimSelector.getValueCardinality(); @@ -243,27 +240,38 @@ public int[] build() } }; - final int numBytesToWorkWith = resultsBuf.remaining(); - final int[] aggregatorSizes = new int[query.getAggregatorSpecs().size()]; - int numBytesPerRecord = 0; + final ResourceHolder resultsBufHolder = bufferPool.take(); - for (int i = 0; i < query.getAggregatorSpecs().size(); ++i) { - aggregatorSizes[i] = query.getAggregatorSpecs().get(i).getMaxIntermediateSizeWithNulls(); - numBytesPerRecord += aggregatorSizes[i]; - } + try { + final ByteBuffer resultsBuf = resultsBufHolder.get(); + resultsBuf.clear(); + + final int numBytesToWorkWith = resultsBuf.remaining(); + final int[] aggregatorSizes = new int[query.getAggregatorSpecs().size()]; + int numBytesPerRecord = 0; - final int numValuesPerPass = numBytesPerRecord > 0 ? numBytesToWorkWith / numBytesPerRecord : cardinality; - - return PooledTopNParams.builder() - .withSelectorPlus(selectorPlus) - .withCursor(cursor) - .withResultsBufHolder(resultsBufHolder) - .withResultsBuf(resultsBuf) - .withArrayProvider(arrayProvider) - .withNumBytesPerRecord(numBytesPerRecord) - .withNumValuesPerPass(numValuesPerPass) - .withAggregatorSizes(aggregatorSizes) - .build(); + for (int i = 0; i < query.getAggregatorSpecs().size(); ++i) { + aggregatorSizes[i] = query.getAggregatorSpecs().get(i).getMaxIntermediateSizeWithNulls(); + numBytesPerRecord += aggregatorSizes[i]; + } + + final int numValuesPerPass = numBytesPerRecord > 0 ? numBytesToWorkWith / numBytesPerRecord : cardinality; + + return PooledTopNParams.builder() + .withSelectorPlus(selectorPlus) + .withCursor(cursor) + .withResultsBufHolder(resultsBufHolder) + .withResultsBuf(resultsBuf) + .withArrayProvider(arrayProvider) + .withNumBytesPerRecord(numBytesPerRecord) + .withNumValuesPerPass(numValuesPerPass) + .withAggregatorSizes(aggregatorSizes) + .build(); + } + catch (Throwable e) { + resultsBufHolder.close(); + throw e; + } } diff --git a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryMergeBufferTest.java b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryMergeBufferTest.java index 221bc321b4aa..fa18a0abbe05 100644 --- a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryMergeBufferTest.java +++ b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryMergeBufferTest.java @@ -71,17 +71,6 @@ private static class TestBlockingPool extends CloseableDefaultBlockingPool take(final long timeout) - { - final ReferenceCountingResourceHolder holder = super.take(timeout); - final int poolSize = getPoolSize(); - if (minRemainBufferNum > poolSize) { - minRemainBufferNum = poolSize; - } - return holder; - } - @Override public List> takeBatch(final int maxElements, final long timeout) { diff --git a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java index 1b8519d68b49..0417d4a430ba 100644 --- a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java +++ b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java @@ -404,7 +404,11 @@ public ByteBuffer get() ); final GroupByQueryQueryToolChest toolChest = new GroupByQueryQueryToolChest(strategySelector); final Closer closer = Closer.create(); - closer.register(bufferPool); + closer.register(() -> { + // Verify that all objects have been returned to the pool. + Assert.assertEquals(bufferPool.poolSize(), bufferPool.objectsCreatedCount()); + bufferPool.close(); + }); closer.register(mergeBufferPool); return Pair.of(new GroupByQueryRunnerFactory(strategySelector, toolChest), closer); } diff --git a/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java b/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java index 48fd49c0d17d..3f5b41db0a15 100644 --- a/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java +++ b/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java @@ -175,6 +175,15 @@ public static List>> queryRunners() ) ) ); + + RESOURCE_CLOSER.register(() -> { + // Verify that all objects have been returned to the pool. + Assert.assertEquals("defaultPool objects created", defaultPool.poolSize(), defaultPool.objectsCreatedCount()); + Assert.assertEquals("customPool objects created", customPool.poolSize(), customPool.objectsCreatedCount()); + defaultPool.close(); + customPool.close(); + }); + return retVal; } From 6de5fad915dcad7236365fa84b188de86349db88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20L=C3=A9aut=C3=A9?= Date: Wed, 3 Jun 2020 10:46:03 -0700 Subject: [PATCH 053/107] remove ListenableFutures and revert to using the Guava implementation (#9944) This change removes ListenableFutures.transformAsync in favor of the existing Guava Futures.transform implementation. Our own implementation had a bug which did not fail the future if the applied function threw an exception, resulting in the future never completing. An attempt was made to fix this bug, however when running againts Guava's own tests, our version failed another half dozen tests, so it was decided to not continue down that path and scrap our own implementation. Explanation for how was this bug manifested itself: An exception thrown in BaseAppenderatorDriver.publishInBackground when invoked via transformAsync in StreamAppenderatorDriver.publish will cause the resulting future to never complete. This explains why when encountering https://github.com/apache/druid/issues/9845 the task will never complete, forever waiting for the publishFuture to register the handoff. As a result, the corresponding "Error while publishing segments ..." message only gets logged once the index task times out and is forcefully shutdown when the future is force-cancelled by the executor. --- codestyle/druid-forbidden-apis.txt | 1 - .../common/concurrent/ListenableFutures.java | 75 ------------------- .../indexing/kafka/KafkaIndexTaskTest.java | 7 +- .../kinesis/KinesisIndexTaskTest.java | 7 +- .../AppenderatorDriverRealtimeIndexTask.java | 7 +- .../appenderator/BatchAppenderatorDriver.java | 7 +- .../StreamAppenderatorDriver.java | 10 +-- 7 files changed, 22 insertions(+), 92 deletions(-) delete mode 100644 core/src/main/java/org/apache/druid/java/util/common/concurrent/ListenableFutures.java diff --git a/codestyle/druid-forbidden-apis.txt b/codestyle/druid-forbidden-apis.txt index 0ae33f5459ad..95c7d770326d 100644 --- a/codestyle/druid-forbidden-apis.txt +++ b/codestyle/druid-forbidden-apis.txt @@ -24,7 +24,6 @@ com.google.common.io.Files#createTempDir() @ Use org.apache.druid.java.util.comm com.google.common.util.concurrent.MoreExecutors#sameThreadExecutor() @ Use org.apache.druid.java.util.common.concurrent.Execs#directExecutor() com.google.common.util.concurrent.MoreExecutors#newDirectExecutorService() @ Use org.apache.druid.java.util.common.concurrent.Execs#directExecutor() com.google.common.util.concurrent.MoreExecutors#directExecutor() @ Use org.apache.druid.java.util.common.concurrent.Execs#directExecutor() -com.google.common.util.concurrent.Futures#transform(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.AsyncFunction) @ Use org.apache.druid.java.util.common.concurrent.ListenableFutures#transformAsync java.io.File#toURL() @ Use java.io.File#toURI() and java.net.URI#toURL() instead java.lang.String#matches(java.lang.String) @ Use startsWith(), endsWith(), contains(), or compile and cache a Pattern explicitly java.lang.String#replace(java.lang.CharSequence,java.lang.CharSequence) @ Use one of the appropriate methods in StringUtils instead diff --git a/core/src/main/java/org/apache/druid/java/util/common/concurrent/ListenableFutures.java b/core/src/main/java/org/apache/druid/java/util/common/concurrent/ListenableFutures.java deleted file mode 100644 index 1722e4a9c663..000000000000 --- a/core/src/main/java/org/apache/druid/java/util/common/concurrent/ListenableFutures.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.java.util.common.concurrent; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; - -import javax.annotation.Nullable; -import java.util.function.Function; - -public class ListenableFutures -{ - /** - * Guava 19 changes the Futures.transform signature so that the async form is different. This is here as a - * compatability layer until such a time as druid only supports Guava 19 or later, in which case - * Futures.transformAsync should be used - * - * This is NOT copied from guava. - */ - public static ListenableFuture transformAsync( - final ListenableFuture inFuture, - final Function> transform - ) - { - final SettableFuture finalFuture = SettableFuture.create(); - Futures.addCallback(inFuture, new FutureCallback() - { - @Override - public void onSuccess(@Nullable I result) - { - final ListenableFuture transformFuture = transform.apply(result); - Futures.addCallback(transformFuture, new FutureCallback() - { - @Override - public void onSuccess(@Nullable O result) - { - finalFuture.set(result); - } - - @Override - public void onFailure(Throwable t) - { - finalFuture.setException(t); - } - }); - } - - @Override - public void onFailure(Throwable t) - { - finalFuture.setException(t); - } - }); - return finalFuture; - } -} diff --git a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/KafkaIndexTaskTest.java b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/KafkaIndexTaskTest.java index eaa2f99a1a54..46a6a0418ba9 100644 --- a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/KafkaIndexTaskTest.java +++ b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/KafkaIndexTaskTest.java @@ -27,6 +27,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import org.apache.curator.test.TestingCluster; @@ -74,7 +76,6 @@ import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.java.util.common.concurrent.Execs; -import org.apache.druid.java.util.common.concurrent.ListenableFutures; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.java.util.emitter.EmittingLogger; import org.apache.druid.java.util.emitter.core.NoopEmitter; @@ -900,12 +901,12 @@ public void testIncrementalHandOffReadsThroughEndOffsets() throws Exception final ListenableFuture normalReplicaFuture = runTask(normalReplica); // Simulating one replica is slower than the other - final ListenableFuture staleReplicaFuture = ListenableFutures.transformAsync( + final ListenableFuture staleReplicaFuture = Futures.transform( taskExec.submit(() -> { Thread.sleep(1000); return staleReplica; }), - this::runTask + (AsyncFunction) this::runTask ); while (normalReplica.getRunner().getStatus() != Status.PAUSED) { diff --git a/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisIndexTaskTest.java b/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisIndexTaskTest.java index 4daa2fdc5d9c..142f81f21016 100644 --- a/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisIndexTaskTest.java +++ b/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisIndexTaskTest.java @@ -30,6 +30,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import com.google.inject.name.Named; @@ -78,7 +80,6 @@ import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.java.util.common.concurrent.Execs; -import org.apache.druid.java.util.common.concurrent.ListenableFutures; import org.apache.druid.java.util.emitter.EmittingLogger; import org.apache.druid.java.util.emitter.core.NoopEmitter; import org.apache.druid.java.util.emitter.service.ServiceEmitter; @@ -2429,12 +2430,12 @@ public void testIncrementalHandOffReadsThroughEndOffsets() throws Exception ((TestableKinesisIndexTask) staleReplica).setLocalSupplier(recordSupplier2); final ListenableFuture normalReplicaFuture = runTask(normalReplica); // Simulating one replica is slower than the other - final ListenableFuture staleReplicaFuture = ListenableFutures.transformAsync( + final ListenableFuture staleReplicaFuture = Futures.transform( taskExec.submit(() -> { Thread.sleep(1000); return staleReplica; }), - this::runTask + (AsyncFunction) this::runTask ); while (normalReplica.getRunner().getStatus() != SeekableStreamIndexTaskRunner.Status.PAUSED) { diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTask.java index 1f9865d69d74..c96b7f5b9002 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTask.java @@ -29,6 +29,7 @@ import com.google.common.base.Supplier; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.apache.commons.io.FileUtils; @@ -65,7 +66,6 @@ import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.StringUtils; -import org.apache.druid.java.util.common.concurrent.ListenableFutures; import org.apache.druid.java.util.common.guava.CloseQuietly; import org.apache.druid.java.util.common.parsers.ParseException; import org.apache.druid.java.util.emitter.EmittingLogger; @@ -682,7 +682,10 @@ private void publishSegments( committerSupplier.get(), Collections.singletonList(sequenceName) ); - pendingHandoffs.add(ListenableFutures.transformAsync(publishFuture, driver::registerHandoff)); + pendingHandoffs.add(Futures.transform( + publishFuture, + (AsyncFunction) driver::registerHandoff + )); } private void waitForSegmentPublishAndHandoff(long timeout) throws InterruptedException, ExecutionException, diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BatchAppenderatorDriver.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BatchAppenderatorDriver.java index 7d1f3e2f393d..7be1a0b1865d 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BatchAppenderatorDriver.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/BatchAppenderatorDriver.java @@ -22,10 +22,11 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.apache.druid.data.input.InputRow; import org.apache.druid.java.util.common.ISE; -import org.apache.druid.java.util.common.concurrent.ListenableFutures; import org.apache.druid.segment.loading.DataSegmentKiller; import org.apache.druid.segment.realtime.appenderator.SegmentWithState.SegmentState; import org.apache.druid.timeline.DataSegment; @@ -138,9 +139,9 @@ private SegmentsAndCommitMetadata pushAndClear( { final Set requestedSegmentIdsForSequences = getAppendingSegments(sequenceNames); - final ListenableFuture future = ListenableFutures.transformAsync( + final ListenableFuture future = Futures.transform( pushInBackground(null, requestedSegmentIdsForSequences, false), - this::dropInBackground + (AsyncFunction) this::dropInBackground ); final SegmentsAndCommitMetadata segmentsAndCommitMetadata = diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/StreamAppenderatorDriver.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/StreamAppenderatorDriver.java index 454ea96f1dcd..0555f2fd37ea 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/StreamAppenderatorDriver.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/StreamAppenderatorDriver.java @@ -23,6 +23,7 @@ import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; +import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -32,7 +33,6 @@ import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.Pair; import org.apache.druid.java.util.common.concurrent.Execs; -import org.apache.druid.java.util.common.concurrent.ListenableFutures; import org.apache.druid.java.util.common.guava.Comparators; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.query.SegmentDescriptor; @@ -273,11 +273,11 @@ public ListenableFuture publish( { final List theSegments = getSegmentIdsWithShardSpecs(sequenceNames); - final ListenableFuture publishFuture = ListenableFutures.transformAsync( + final ListenableFuture publishFuture = Futures.transform( // useUniquePath=true prevents inconsistencies in segment data when task failures or replicas leads to a second // version of a segment with the same identifier containing different data; see DataSegmentPusher.push() docs pushInBackground(wrapCommitter(committer), theSegments, true), - sam -> publishInBackground( + (AsyncFunction) sam -> publishInBackground( null, sam, publisher @@ -386,9 +386,9 @@ public ListenableFuture publishAndRegisterHandoff( final Collection sequenceNames ) { - return ListenableFutures.transformAsync( + return Futures.transform( publish(publisher, committer, sequenceNames), - this::registerHandoff + (AsyncFunction) this::registerHandoff ); } From 0e218bc8138302a3ec47b5538a478a0fa4f14355 Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn <52679095+maytasm@users.noreply.github.com> Date: Wed, 3 Jun 2020 09:55:52 -1000 Subject: [PATCH 054/107] Document unsupported Join on multi-value column (#9948) * Document Unsupported Join on multi-value column * Document Unsupported Join on multi-value column * address comments * Add unit tests * address comments * add tests --- docs/querying/datasource.md | 2 + docs/querying/sql.md | 2 + .../query/QueryUnsupportedException.java | 70 ++++++++++++++++ .../join/lookup/LookupJoinMatcher.java | 8 +- .../join/table/IndexedTableJoinMatcher.java | 15 +++- .../table/IndexedTableJoinMatcherTest.java | 83 +++++++++++++++++++ .../join/table/LookupJoinMatcherTest.java | 36 ++++++++ .../apache/druid/server/QueryResource.java | 13 +++ .../druid/server/QueryResourceTest.java | 28 +++++++ .../apache/druid/sql/http/SqlResource.java | 6 ++ .../druid/sql/calcite/CalciteQueryTest.java | 18 ++++ .../druid/sql/http/SqlResourceTest.java | 17 ++++ 12 files changed, 292 insertions(+), 6 deletions(-) create mode 100644 processing/src/main/java/org/apache/druid/query/QueryUnsupportedException.java diff --git a/docs/querying/datasource.md b/docs/querying/datasource.md index 66cc9058841a..d6c478d110e4 100644 --- a/docs/querying/datasource.md +++ b/docs/querying/datasource.md @@ -348,3 +348,5 @@ future versions: always be correct. - Performance-related optimizations as mentioned in the [previous section](#join-performance). - Join algorithms other than broadcast hash-joins. +- Join condition on a column compared to a constant value. +- Join conditions on a column containing a multi-value dimension. diff --git a/docs/querying/sql.md b/docs/querying/sql.md index 9a49009a9bcd..7b84e4a23370 100644 --- a/docs/querying/sql.md +++ b/docs/querying/sql.md @@ -709,6 +709,8 @@ Druid does not support all SQL features. In particular, the following features a - JOIN between native datasources (table, lookup, subquery) and system tables. - JOIN conditions that are not an equality between expressions from the left- and right-hand sides. +- JOIN conditions containing a constant value inside the condition. +- JOIN conditions on a column which contains a multi-value dimension. - OVER clauses, and analytic functions such as `LAG` and `LEAD`. - OFFSET clauses. - DDL and DML. diff --git a/processing/src/main/java/org/apache/druid/query/QueryUnsupportedException.java b/processing/src/main/java/org/apache/druid/query/QueryUnsupportedException.java new file mode 100644 index 000000000000..41126dcaf4f0 --- /dev/null +++ b/processing/src/main/java/org/apache/druid/query/QueryUnsupportedException.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query; + + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.annotation.Nullable; +import java.net.InetAddress; + +/** + * This exception is for the query engine to surface when a query cannot be run. This can be due to the + * following reasons: 1) The query is not supported yet. 2) The query is not something Druid would ever supports. + * For these cases, the exact causes and details should also be documented in Druid user facing documents. + * + * As a {@link QueryException} it is expected to be serialied to a json response, but will be mapped to + * {@link #STATUS_CODE} instead of the default HTTP 500 status. + */ +public class QueryUnsupportedException extends QueryException +{ + private static final String ERROR_CLASS = QueryUnsupportedException.class.getName(); + public static final String ERROR_CODE = "Unsupported query"; + public static final int STATUS_CODE = 400; + + @JsonCreator + public QueryUnsupportedException( + @JsonProperty("error") @Nullable String errorCode, + @JsonProperty("errorMessage") String errorMessage, + @JsonProperty("errorClass") @Nullable String errorClass, + @JsonProperty("host") @Nullable String host + ) + { + super(errorCode, errorMessage, errorClass, host); + } + + public QueryUnsupportedException(String errorMessage) + { + super(ERROR_CODE, errorMessage, ERROR_CLASS, resolveHostname()); + } + + private static String resolveHostname() + { + String host; + try { + host = InetAddress.getLocalHost().getCanonicalHostName(); + } + catch (Exception e) { + host = null; + } + return host; + } +} diff --git a/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinMatcher.java b/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinMatcher.java index 624b6bc408bc..a97739b632e2 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinMatcher.java +++ b/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinMatcher.java @@ -26,6 +26,7 @@ import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.Pair; import org.apache.druid.math.expr.Expr; +import org.apache.druid.query.QueryUnsupportedException; import org.apache.druid.query.lookup.LookupExtractor; import org.apache.druid.segment.BaseDoubleColumnValueSelector; import org.apache.druid.segment.BaseFloatColumnValueSelector; @@ -71,9 +72,12 @@ public Supplier makeDimensionProcessor(DimensionSelector selector, boole if (row.size() == 1) { return selector.lookupName(row.get(0)); - } else { - // Multi-valued rows are not handled by the join system right now; treat them as nulls. + } else if (row.size() == 0) { return null; + } else { + // Multi-valued rows are not handled by the join system right now + // TODO: Remove when https://github.com/apache/druid/issues/9924 is done + throw new QueryUnsupportedException("Joining against a multi-value dimension is not supported."); } }; } diff --git a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcher.java b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcher.java index 685be4bb7673..455f4f12afca 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcher.java +++ b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcher.java @@ -30,6 +30,7 @@ import it.unimi.dsi.fastutil.ints.IntSet; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.IAE; +import org.apache.druid.query.QueryUnsupportedException; import org.apache.druid.segment.BaseDoubleColumnValueSelector; import org.apache.druid.segment.BaseFloatColumnValueSelector; import org.apache.druid.segment.BaseLongColumnValueSelector; @@ -326,9 +327,12 @@ public Supplier makeDimensionProcessor(DimensionSelector selector, int dimensionId = row.get(0); IntList rowNumbers = getRowNumbers(selector, dimensionId); return rowNumbers.iterator(); - } else { - // Multi-valued rows are not handled by the join system right now; treat them as nulls. + } else if (row.size() == 0) { return IntIterators.EMPTY_ITERATOR; + } else { + // Multi-valued rows are not handled by the join system right now + // TODO: Remove when https://github.com/apache/druid/issues/9924 is done + throw new QueryUnsupportedException("Joining against a multi-value dimension is not supported."); } }; } else { @@ -341,9 +345,12 @@ public Supplier makeDimensionProcessor(DimensionSelector selector, int dimensionId = row.get(0); IntList rowNumbers = getAndCacheRowNumbers(selector, dimensionId); return rowNumbers.iterator(); - } else { - // Multi-valued rows are not handled by the join system right now; treat them as nulls. + } else if (row.size() == 0) { return IntIterators.EMPTY_ITERATOR; + } else { + // Multi-valued rows are not handled by the join system right now + // TODO: Remove when https://github.com/apache/druid/issues/9924 is done + throw new QueryUnsupportedException("Joining against a multi-value dimension is not supported."); } }; } diff --git a/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcherTest.java b/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcherTest.java index e7baaea1edd7..45367b2374e8 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcherTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcherTest.java @@ -23,14 +23,20 @@ import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntList; import org.apache.druid.common.config.NullHandling; +import org.apache.druid.query.QueryUnsupportedException; import org.apache.druid.segment.ConstantDimensionSelector; import org.apache.druid.segment.DimensionDictionarySelector; +import org.apache.druid.segment.DimensionSelector; import org.apache.druid.segment.column.ValueType; +import org.apache.druid.segment.data.ArrayBasedIndexedInts; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; import java.util.Collections; import java.util.concurrent.atomic.AtomicLong; @@ -48,12 +54,89 @@ public static class ConditionMatcherFactoryTest { public static class MakeDimensionProcessorTest { + @Mock + private DimensionSelector dimensionSelector; + private static final String KEY = "key"; static { NullHandling.initializeForTests(); } + @SuppressWarnings("ReturnValueIgnored") + @Test(expected = QueryUnsupportedException.class) + public void testMatchMultiValuedRowCardinalityUnknownShouldThrowException() + { + MockitoAnnotations.initMocks(this); + ArrayBasedIndexedInts row = new ArrayBasedIndexedInts(new int[]{2, 4, 6}); + Mockito.doReturn(row).when(dimensionSelector).getRow(); + Mockito.doReturn(DimensionDictionarySelector.CARDINALITY_UNKNOWN).when(dimensionSelector).getValueCardinality(); + + IndexedTableJoinMatcher.ConditionMatcherFactory conditionMatcherFactory = + new IndexedTableJoinMatcher.ConditionMatcherFactory( + ValueType.STRING, + IndexedTableJoinMatcherTest::createSingletonIntList + ); + Supplier dimensionProcessor = conditionMatcherFactory.makeDimensionProcessor(dimensionSelector, false); + // Test match should throw exception + dimensionProcessor.get(); + } + + @SuppressWarnings("ReturnValueIgnored") + @Test(expected = QueryUnsupportedException.class) + public void testMatchMultiValuedRowCardinalityKnownShouldThrowException() + { + MockitoAnnotations.initMocks(this); + ArrayBasedIndexedInts row = new ArrayBasedIndexedInts(new int[]{2, 4, 6}); + Mockito.doReturn(row).when(dimensionSelector).getRow(); + Mockito.doReturn(3).when(dimensionSelector).getValueCardinality(); + + IndexedTableJoinMatcher.ConditionMatcherFactory conditionMatcherFactory = + new IndexedTableJoinMatcher.ConditionMatcherFactory( + ValueType.STRING, + IndexedTableJoinMatcherTest::createSingletonIntList + ); + Supplier dimensionProcessor = conditionMatcherFactory.makeDimensionProcessor(dimensionSelector, false); + // Test match should throw exception + dimensionProcessor.get(); + } + + @Test + public void testMatchEmptyRowCardinalityUnknown() + { + MockitoAnnotations.initMocks(this); + ArrayBasedIndexedInts row = new ArrayBasedIndexedInts(new int[]{}); + Mockito.doReturn(row).when(dimensionSelector).getRow(); + Mockito.doReturn(DimensionDictionarySelector.CARDINALITY_UNKNOWN).when(dimensionSelector).getValueCardinality(); + + IndexedTableJoinMatcher.ConditionMatcherFactory conditionMatcherFactory = + new IndexedTableJoinMatcher.ConditionMatcherFactory( + ValueType.STRING, + IndexedTableJoinMatcherTest::createSingletonIntList + ); + Supplier dimensionProcessor = conditionMatcherFactory.makeDimensionProcessor(dimensionSelector, false); + Assert.assertNotNull(dimensionProcessor.get()); + Assert.assertFalse(dimensionProcessor.get().hasNext()); + } + + @Test + public void testMatchEmptyRowCardinalityKnown() + { + MockitoAnnotations.initMocks(this); + ArrayBasedIndexedInts row = new ArrayBasedIndexedInts(new int[]{}); + Mockito.doReturn(row).when(dimensionSelector).getRow(); + Mockito.doReturn(0).when(dimensionSelector).getValueCardinality(); + + IndexedTableJoinMatcher.ConditionMatcherFactory conditionMatcherFactory = + new IndexedTableJoinMatcher.ConditionMatcherFactory( + ValueType.STRING, + IndexedTableJoinMatcherTest::createSingletonIntList + ); + Supplier dimensionProcessor = conditionMatcherFactory.makeDimensionProcessor(dimensionSelector, false); + Assert.assertNotNull(dimensionProcessor.get()); + Assert.assertFalse(dimensionProcessor.get().hasNext()); + } + @Test public void getsCorrectResultWhenSelectorCardinalityUnknown() { diff --git a/processing/src/test/java/org/apache/druid/segment/join/table/LookupJoinMatcherTest.java b/processing/src/test/java/org/apache/druid/segment/join/table/LookupJoinMatcherTest.java index f5f7f3ff0d17..08a1a452af9c 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/table/LookupJoinMatcherTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/table/LookupJoinMatcherTest.java @@ -22,11 +22,13 @@ import com.google.common.collect.ImmutableMap; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.math.expr.ExprMacroTable; +import org.apache.druid.query.QueryUnsupportedException; import org.apache.druid.query.dimension.DefaultDimensionSpec; import org.apache.druid.query.dimension.DimensionSpec; import org.apache.druid.query.lookup.LookupExtractor; import org.apache.druid.segment.ColumnSelectorFactory; import org.apache.druid.segment.DimensionSelector; +import org.apache.druid.segment.data.ArrayBasedIndexedInts; import org.apache.druid.segment.data.SingleIndexedInt; import org.apache.druid.segment.join.JoinConditionAnalysis; import org.apache.druid.segment.join.lookup.LookupJoinMatcher; @@ -150,6 +152,40 @@ public void testMatchConditionSometimesTrueSometimesFalse() Assert.assertFalse(target.hasMatch()); } + @Test(expected = QueryUnsupportedException.class) + public void testMatchMultiValuedRowShouldThrowException() + { + ArrayBasedIndexedInts row = new ArrayBasedIndexedInts(new int[]{2, 4, 6}); + Mockito.doReturn(dimensionSelector).when(leftSelectorFactory).makeDimensionSelector(ArgumentMatchers.any(DimensionSpec.class)); + Mockito.doReturn(row).when(dimensionSelector).getRow(); + + JoinConditionAnalysis condition = JoinConditionAnalysis.forExpression( + StringUtils.format("\"%sk\" == foo", PREFIX), + PREFIX, + ExprMacroTable.nil() + ); + target = LookupJoinMatcher.create(extractor, leftSelectorFactory, condition, true); + // Test match should throw exception + target.matchCondition(); + } + + @Test + public void testMatchEmptyRow() + { + ArrayBasedIndexedInts row = new ArrayBasedIndexedInts(new int[]{}); + Mockito.doReturn(dimensionSelector).when(leftSelectorFactory).makeDimensionSelector(ArgumentMatchers.any(DimensionSpec.class)); + Mockito.doReturn(row).when(dimensionSelector).getRow(); + + JoinConditionAnalysis condition = JoinConditionAnalysis.forExpression( + StringUtils.format("\"%sk\" == foo", PREFIX), + PREFIX, + ExprMacroTable.nil() + ); + target = LookupJoinMatcher.create(extractor, leftSelectorFactory, condition, true); + target.matchCondition(); + Assert.assertFalse(target.hasMatch()); + } + private void verifyMatch(String expectedKey, String expectedValue) { DimensionSelector selector = target.getColumnSelectorFactory() diff --git a/server/src/main/java/org/apache/druid/server/QueryResource.java b/server/src/main/java/org/apache/druid/server/QueryResource.java index d270799b62af..131ebf134f45 100644 --- a/server/src/main/java/org/apache/druid/server/QueryResource.java +++ b/server/src/main/java/org/apache/druid/server/QueryResource.java @@ -44,6 +44,7 @@ import org.apache.druid.query.QueryContexts; import org.apache.druid.query.QueryInterruptedException; import org.apache.druid.query.QueryToolChest; +import org.apache.druid.query.QueryUnsupportedException; import org.apache.druid.query.context.ResponseContext; import org.apache.druid.server.metrics.QueryCountStatsProvider; import org.apache.druid.server.security.Access; @@ -315,6 +316,11 @@ public void write(OutputStream outputStream) throws WebApplicationException queryLifecycle.emitLogsAndMetrics(cap, req.getRemoteAddr(), -1); return ioReaderWriter.gotLimited(cap); } + catch (QueryUnsupportedException unsupported) { + failedQueryCount.incrementAndGet(); + queryLifecycle.emitLogsAndMetrics(unsupported, req.getRemoteAddr(), -1); + return ioReaderWriter.gotUnsupported(unsupported); + } catch (ForbiddenException e) { // don't do anything for an authorization failure, ForbiddenExceptionMapper will catch this later and // send an error response if this is thrown. @@ -446,6 +452,13 @@ Response gotLimited(QueryCapacityExceededException e) throws IOException .entity(newOutputWriter(null, null, false).writeValueAsBytes(e)) .build(); } + + Response gotUnsupported(QueryUnsupportedException e) throws IOException + { + return Response.status(QueryUnsupportedException.STATUS_CODE) + .entity(newOutputWriter(null, null, false).writeValueAsBytes(e)) + .build(); + } } @Override diff --git a/server/src/test/java/org/apache/druid/server/QueryResourceTest.java b/server/src/test/java/org/apache/druid/server/QueryResourceTest.java index eface4047405..35c3a84153cf 100644 --- a/server/src/test/java/org/apache/druid/server/QueryResourceTest.java +++ b/server/src/test/java/org/apache/druid/server/QueryResourceTest.java @@ -39,6 +39,7 @@ import org.apache.druid.query.QueryRunner; import org.apache.druid.query.QuerySegmentWalker; import org.apache.druid.query.QueryToolChestWarehouse; +import org.apache.druid.query.QueryUnsupportedException; import org.apache.druid.query.Result; import org.apache.druid.query.SegmentDescriptor; import org.apache.druid.query.timeboundary.TimeBoundaryResultValue; @@ -339,6 +340,33 @@ public void testBadQuery() throws IOException Assert.assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus()); } + @Test + public void testUnsupportedQueryThrowsException() throws IOException + { + String errorMessage = "This will be support in Druid 9999"; + ByteArrayInputStream badQuery = EasyMock.createMock(ByteArrayInputStream.class); + EasyMock.expect(badQuery.read(EasyMock.anyObject(), EasyMock.anyInt(), EasyMock.anyInt())).andThrow( + new QueryUnsupportedException(errorMessage)); + EasyMock.replay(badQuery); + EasyMock.replay(testServletRequest); + Response response = queryResource.doPost( + badQuery, + null /*pretty*/, + testServletRequest + ); + Assert.assertNotNull(response); + Assert.assertEquals(QueryUnsupportedException.STATUS_CODE, response.getStatus()); + QueryUnsupportedException ex; + try { + ex = JSON_MAPPER.readValue((byte[]) response.getEntity(), QueryUnsupportedException.class); + } + catch (IOException e) { + throw new RuntimeException(e); + } + Assert.assertEquals(errorMessage, ex.getMessage()); + Assert.assertEquals(QueryUnsupportedException.ERROR_CODE, ex.getErrorCode()); + } + @Test public void testSecuredQuery() throws Exception { diff --git a/sql/src/main/java/org/apache/druid/sql/http/SqlResource.java b/sql/src/main/java/org/apache/druid/sql/http/SqlResource.java index c09aa858da6a..1c950c94819c 100644 --- a/sql/src/main/java/org/apache/druid/sql/http/SqlResource.java +++ b/sql/src/main/java/org/apache/druid/sql/http/SqlResource.java @@ -33,6 +33,7 @@ import org.apache.druid.java.util.common.guava.Yielders; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.query.QueryInterruptedException; +import org.apache.druid.query.QueryUnsupportedException; import org.apache.druid.server.QueryCapacityExceededException; import org.apache.druid.server.security.ForbiddenException; import org.apache.druid.sql.SqlLifecycle; @@ -176,6 +177,11 @@ public Response doPost( lifecycle.emitLogsAndMetrics(cap, remoteAddr, -1); return Response.status(QueryCapacityExceededException.STATUS_CODE).entity(jsonMapper.writeValueAsBytes(cap)).build(); } + catch (QueryUnsupportedException unsupported) { + log.warn(unsupported, "Failed to handle query: %s", sqlQuery); + lifecycle.emitLogsAndMetrics(unsupported, remoteAddr, -1); + return Response.status(QueryUnsupportedException.STATUS_CODE).entity(jsonMapper.writeValueAsBytes(unsupported)).build(); + } catch (ForbiddenException e) { throw e; // let ForbiddenExceptionMapper handle this } diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index 94edd222dc67..a26d790ef160 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -42,6 +42,7 @@ import org.apache.druid.query.LookupDataSource; import org.apache.druid.query.QueryContexts; import org.apache.druid.query.QueryDataSource; +import org.apache.druid.query.QueryException; import org.apache.druid.query.ResourceLimitExceededException; import org.apache.druid.query.TableDataSource; import org.apache.druid.query.aggregation.CountAggregatorFactory; @@ -1790,6 +1791,23 @@ public void testFirstLatestAggregatorsSkipNulls() throws Exception ); } + // This query is expected to fail as we do not support join on multi valued column + // (see issue https://github.com/apache/druid/issues/9924 for more information) + // TODO: Remove expected Exception when https://github.com/apache/druid/issues/9924 is fixed + @Test(expected = QueryException.class) + @Parameters(source = QueryContextForJoinProvider.class) + public void testJoinOnMultiValuedColumnShouldThrowException(Map queryContext) throws Exception + { + final String query = "SELECT dim3, l.v from druid.foo f inner join lookup.lookyloo l on f.dim3 = l.k\n"; + + testQuery( + query, + queryContext, + ImmutableList.of(), + ImmutableList.of() + ); + } + @Test public void testAnyAggregatorsDoesNotSkipNulls() throws Exception { diff --git a/sql/src/test/java/org/apache/druid/sql/http/SqlResourceTest.java b/sql/src/test/java/org/apache/druid/sql/http/SqlResourceTest.java index 1f2f286d1a88..c4e2de59c95c 100644 --- a/sql/src/test/java/org/apache/druid/sql/http/SqlResourceTest.java +++ b/sql/src/test/java/org/apache/druid/sql/http/SqlResourceTest.java @@ -41,6 +41,7 @@ import org.apache.druid.query.QueryException; import org.apache.druid.query.QueryInterruptedException; import org.apache.druid.query.QueryRunnerFactoryConglomerate; +import org.apache.druid.query.QueryUnsupportedException; import org.apache.druid.query.ResourceLimitExceededException; import org.apache.druid.server.QueryCapacityExceededException; import org.apache.druid.server.QueryScheduler; @@ -717,6 +718,22 @@ public void testResourceLimitExceeded() throws Exception checkSqlRequestLog(false); } + @Test + public void testUnsupportedQueryThrowsException() throws Exception + { + String errorMessage = "This will be support in Druid 9999"; + SqlQuery badQuery = EasyMock.createMock(SqlQuery.class); + EasyMock.expect(badQuery.getQuery()).andReturn("SELECT ANSWER TO LIFE"); + EasyMock.expect(badQuery.getContext()).andReturn(ImmutableMap.of()); + EasyMock.expect(badQuery.getParameterList()).andThrow(new QueryUnsupportedException(errorMessage)); + EasyMock.replay(badQuery); + final QueryException exception = doPost(badQuery).lhs; + + Assert.assertNotNull(exception); + Assert.assertEquals(exception.getErrorCode(), QueryUnsupportedException.ERROR_CODE); + Assert.assertEquals(exception.getErrorClass(), QueryUnsupportedException.class.getName()); + } + @Test public void testTooManyRequests() throws Exception { From ca210a156b78100dc1a50a85ce6a28d2eb5fd79d Mon Sep 17 00:00:00 2001 From: Gian Merlino Date: Wed, 3 Jun 2020 14:31:37 -0700 Subject: [PATCH 055/107] Add REGEXP_LIKE, fix bugs in REGEXP_EXTRACT. (#9893) * Add REGEXP_LIKE, fix empty-pattern bug in REGEXP_EXTRACT. - Add REGEXP_LIKE function that returns a boolean, and is useful in WHERE clauses. - Fix REGEXP_EXTRACT return type (should be nullable; causes incorrect filter elision). - Fix REGEXP_EXTRACT behavior for empty patterns: should always match (previously, they threw errors). - Improve error behavior when REGEXP_EXTRACT and REGEXP_LIKE are passed non-literal patterns. - Improve documentation of REGEXP_EXTRACT. * Changes based on PR review. * Fix arg check. * Important fixes! * Add speller. * wip * Additional tests. * Fix up tests. * Add validation error tests. * Additional tests. * Remove useless call. --- docs/misc/math-expr.md | 3 +- docs/querying/sql.md | 9 +- ...mateWithErrorBoundsOperatorConversion.java | 2 +- .../HllSketchToStringOperatorConversion.java | 2 +- ...ublesSketchQuantileOperatorConversion.java | 2 +- .../DoublesSketchRankOperatorConversion.java | 2 +- ...oublesSketchSummaryOperatorConversion.java | 2 +- ...mateWithErrorBoundsOperatorConversion.java | 2 +- .../druid/query/expression/ExprUtils.java | 27 ++ .../expression/RegexpExtractExprMacro.java | 26 +- .../query/expression/RegexpLikeExprMacro.java | 101 +++++ .../IPv4AddressMatchExprMacroTest.java | 18 +- .../IPv4AddressParseExprMacroTest.java | 14 +- .../IPv4AddressStringifyExprMacroTest.java | 14 +- .../druid/query/expression/MacroTestBase.java | 64 +++- .../RegexpExtractExprMacroTest.java | 141 +++++++ .../expression/RegexpLikeExprMacroTest.java | 142 +++++++ .../apache/druid/guice/ExpressionModule.java | 4 +- .../expression/OperatorConversions.java | 164 ++++++-- .../ArrayLengthOperatorConversion.java | 2 +- .../ArrayOffsetOfOperatorConversion.java | 2 +- .../ArrayOffsetOperatorConversion.java | 2 +- .../ArrayOrdinalOfOperatorConversion.java | 2 +- .../ArrayOrdinalOperatorConversion.java | 2 +- .../ArrayToStringOperatorConversion.java | 2 +- .../builtin/BTrimOperatorConversion.java | 2 +- .../builtin/DateTruncOperatorConversion.java | 2 +- ...Pv4AddressStringifyOperatorConversion.java | 2 +- .../builtin/LPadOperatorConversion.java | 2 +- .../builtin/LTrimOperatorConversion.java | 2 +- .../builtin/LeftOperatorConversion.java | 2 +- .../MillisToTimestampOperatorConversion.java | 2 +- ...tiValueStringAppendOperatorConversion.java | 2 +- ...tiValueStringConcatOperatorConversion.java | 2 +- ...iValueStringPrependOperatorConversion.java | 2 +- ...ltiValueStringSliceOperatorConversion.java | 2 +- .../builtin/ParseLongOperatorConversion.java | 2 +- .../QueryLookupOperatorConversion.java | 2 +- .../builtin/RPadOperatorConversion.java | 2 +- .../builtin/RTrimOperatorConversion.java | 2 +- .../RegexpExtractOperatorConversion.java | 9 +- .../builtin/RegexpLikeOperatorConversion.java | 116 ++++++ .../builtin/RepeatOperatorConversion.java | 2 +- .../builtin/ReverseOperatorConversion.java | 2 +- .../builtin/RightOperatorConversion.java | 2 +- .../StringFormatOperatorConversion.java | 2 +- ...gToMultiValueStringOperatorConversion.java | 2 +- .../builtin/StrposOperatorConversion.java | 2 +- .../builtin/TextcatOperatorConversion.java | 2 +- .../builtin/TimeCeilOperatorConversion.java | 2 +- .../TimeExtractOperatorConversion.java | 2 +- .../builtin/TimeFloorOperatorConversion.java | 2 +- .../builtin/TimeFormatOperatorConversion.java | 2 +- .../builtin/TimeParseOperatorConversion.java | 2 +- .../builtin/TimeShiftOperatorConversion.java | 2 +- .../TimestampToMillisOperatorConversion.java | 2 +- .../calcite/planner/DruidOperatorTable.java | 2 + .../druid/sql/calcite/CalciteQueryTest.java | 105 ++++++ .../expression/ExpressionTestHelper.java | 88 ++++- .../calcite/expression/ExpressionsTest.java | 352 ++++++++++++++++++ website/.spelling | 1 + 61 files changed, 1345 insertions(+), 137 deletions(-) create mode 100644 processing/src/main/java/org/apache/druid/query/expression/RegexpLikeExprMacro.java create mode 100644 processing/src/test/java/org/apache/druid/query/expression/RegexpExtractExprMacroTest.java create mode 100644 processing/src/test/java/org/apache/druid/query/expression/RegexpLikeExprMacroTest.java create mode 100644 sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RegexpLikeOperatorConversion.java diff --git a/docs/misc/math-expr.md b/docs/misc/math-expr.md index e4ad694738f8..249eb1ef209c 100644 --- a/docs/misc/math-expr.md +++ b/docs/misc/math-expr.md @@ -76,7 +76,8 @@ The following built-in functions are available. |like|like(expr, pattern[, escape]) is equivalent to SQL `expr LIKE pattern`| |lookup|lookup(expr, lookup-name) looks up expr in a registered [query-time lookup](../querying/lookups.md)| |parse_long|parse_long(string[, radix]) parses a string as a long with the given radix, or 10 (decimal) if a radix is not provided.| -|regexp_extract|regexp_extract(expr, pattern[, index]) applies a regular expression pattern and extracts a capture group index, or null if there is no match. If index is unspecified or zero, returns the substring that matched the pattern.| +|regexp_extract|regexp_extract(expr, pattern[, index]) applies a regular expression pattern and extracts a capture group index, or null if there is no match. If index is unspecified or zero, returns the substring that matched the pattern. The pattern may match anywhere inside `expr`; if you want to match the entire string instead, use the `^` and `$` markers at the start and end of your pattern.| +|regexp_like|regexp_like(expr, pattern) returns whether `expr` matches regular expression `pattern`. The pattern may match anywhere inside `expr`; if you want to match the entire string instead, use the `^` and `$` markers at the start and end of your pattern. | |replace|replace(expr, pattern, replacement) replaces pattern with replacement| |substring|substring(expr, index, length) behaves like java.lang.String's substring| |right|right(expr, length) returns the rightmost length characters from a string| diff --git a/docs/querying/sql.md b/docs/querying/sql.md index 7b84e4a23370..dff5c7176e1f 100644 --- a/docs/querying/sql.md +++ b/docs/querying/sql.md @@ -322,7 +322,8 @@ String functions accept strings, and return a type appropriate to the function. |`LOWER(expr)`|Returns expr in all lowercase.| |`PARSE_LONG(string[, radix])`|Parses a string into a long (BIGINT) with the given radix, or 10 (decimal) if a radix is not provided.| |`POSITION(needle IN haystack [FROM fromIndex])`|Returns the index of needle within haystack, with indexes starting from 1. The search will begin at fromIndex, or 1 if fromIndex is not specified. If the needle is not found, returns 0.| -|`REGEXP_EXTRACT(expr, pattern, [index])`|Apply regular expression pattern and extract a capture group, or null if there is no match. If index is unspecified or zero, returns the substring that matched the pattern.| +|`REGEXP_EXTRACT(expr, pattern, [index])`|Apply regular expression `pattern` to `expr` and extract a capture group, or `NULL` if there is no match. If index is unspecified or zero, returns the first substring that matched the pattern. The pattern may match anywhere inside `expr`; if you want to match the entire string instead, use the `^` and `$` markers at the start and end of your pattern. Note: when `druid.generic.useDefaultValueForNull = true`, it is not possible to differentiate an empty-string match from a non-match (both will return `NULL`).| +|`REGEXP_LIKE(expr, pattern)`|Returns whether `expr` matches regular expression `pattern`. The pattern may match anywhere inside `expr`; if you want to match the entire string instead, use the `^` and `$` markers at the start and end of your pattern. Similar to [`LIKE`](#comparison-operators), but uses regexps instead of LIKE patterns. Especially useful in WHERE clauses.| |`REPLACE(expr, pattern, replacement)`|Replaces pattern with replacement in expr, and returns the result.| |`STRPOS(haystack, needle)`|Returns the index of needle within haystack, with indexes starting from 1. If the needle is not found, returns 0.| |`SUBSTRING(expr, index, [length])`|Returns a substring of expr starting at index, with a max length, both measured in UTF-16 code units.| @@ -330,9 +331,9 @@ String functions accept strings, and return a type appropriate to the function. |`LEFT(expr, [length])`|Returns the leftmost length characters from expr.| |`SUBSTR(expr, index, [length])`|Synonym for SUBSTRING.| |TRIM([BOTH | LEADING | TRAILING] [ FROM] expr)|Returns expr with characters removed from the leading, trailing, or both ends of "expr" if they are in "chars". If "chars" is not provided, it defaults to " " (a space). If the directional argument is not provided, it defaults to "BOTH".| -|`BTRIM(expr[, chars])`|Alternate form of `TRIM(BOTH FROM `).| -|`LTRIM(expr[, chars])`|Alternate form of `TRIM(LEADING FROM `).| -|`RTRIM(expr[, chars])`|Alternate form of `TRIM(TRAILING FROM `).| +|`BTRIM(expr[, chars])`|Alternate form of `TRIM(BOTH FROM )`.| +|`LTRIM(expr[, chars])`|Alternate form of `TRIM(LEADING FROM )`.| +|`RTRIM(expr[, chars])`|Alternate form of `TRIM(TRAILING FROM )`.| |`UPPER(expr)`|Returns expr in all uppercase.| |`REVERSE(expr)`|Reverses expr.| |`REPEAT(expr, [N])`|Repeats expr N times| diff --git a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchEstimateWithErrorBoundsOperatorConversion.java b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchEstimateWithErrorBoundsOperatorConversion.java index a0435430c02c..c645ca724a57 100644 --- a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchEstimateWithErrorBoundsOperatorConversion.java +++ b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchEstimateWithErrorBoundsOperatorConversion.java @@ -47,7 +47,7 @@ public class HllSketchEstimateWithErrorBoundsOperatorConversion extends DirectOp .operatorBuilder(StringUtils.toUpperCase(FUNCTION_NAME)) .operandTypes(SqlTypeFamily.ANY, SqlTypeFamily.INTEGER) .requiredOperands(1) - .returnType(SqlTypeName.OTHER) + .returnTypeNonNull(SqlTypeName.OTHER) .build(); diff --git a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchToStringOperatorConversion.java b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchToStringOperatorConversion.java index 189b3b7c916e..fe0c56b56adc 100644 --- a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchToStringOperatorConversion.java +++ b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchToStringOperatorConversion.java @@ -44,7 +44,7 @@ public class HllSketchToStringOperatorConversion extends DirectOperatorConversio private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder(StringUtils.toUpperCase(FUNCTION_NAME)) .operandTypes(SqlTypeFamily.ANY) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); public HllSketchToStringOperatorConversion() diff --git a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchQuantileOperatorConversion.java b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchQuantileOperatorConversion.java index c571be13e403..2387fe8cccb9 100644 --- a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchQuantileOperatorConversion.java +++ b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchQuantileOperatorConversion.java @@ -34,7 +34,7 @@ public class DoublesSketchQuantileOperatorConversion extends DoublesSketchSingle private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder(StringUtils.toUpperCase(FUNCTION_NAME)) .operandTypes(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC) - .returnType(SqlTypeName.DOUBLE) + .returnTypeNonNull(SqlTypeName.DOUBLE) .build(); diff --git a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchRankOperatorConversion.java b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchRankOperatorConversion.java index ab54cb1441e1..327f757a7182 100644 --- a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchRankOperatorConversion.java +++ b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchRankOperatorConversion.java @@ -34,7 +34,7 @@ public class DoublesSketchRankOperatorConversion extends DoublesSketchSingleArgB private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder(StringUtils.toUpperCase(FUNCTION_NAME)) .operandTypes(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC) - .returnType(SqlTypeName.DOUBLE) + .returnTypeNonNull(SqlTypeName.DOUBLE) .build(); public DoublesSketchRankOperatorConversion() diff --git a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSummaryOperatorConversion.java b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSummaryOperatorConversion.java index 6465ef71f45e..4dd01fd85402 100644 --- a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSummaryOperatorConversion.java +++ b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSummaryOperatorConversion.java @@ -44,7 +44,7 @@ public class DoublesSketchSummaryOperatorConversion extends DirectOperatorConver private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder(StringUtils.toUpperCase(FUNCTION_NAME)) .operandTypes(SqlTypeFamily.ANY) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); public DoublesSketchSummaryOperatorConversion() diff --git a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchEstimateWithErrorBoundsOperatorConversion.java b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchEstimateWithErrorBoundsOperatorConversion.java index 7ddd74ee80c7..c54f2c59b454 100644 --- a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchEstimateWithErrorBoundsOperatorConversion.java +++ b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchEstimateWithErrorBoundsOperatorConversion.java @@ -46,7 +46,7 @@ public class ThetaSketchEstimateWithErrorBoundsOperatorConversion extends Direct private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder(StringUtils.toUpperCase(FUNCTION_NAME)) .operandTypes(SqlTypeFamily.ANY, SqlTypeFamily.INTEGER) - .returnType(SqlTypeName.OTHER) + .returnTypeNonNull(SqlTypeName.OTHER) .build(); diff --git a/processing/src/main/java/org/apache/druid/query/expression/ExprUtils.java b/processing/src/main/java/org/apache/druid/query/expression/ExprUtils.java index 75fe2740057c..23ef6847ec06 100644 --- a/processing/src/main/java/org/apache/druid/query/expression/ExprUtils.java +++ b/processing/src/main/java/org/apache/druid/query/expression/ExprUtils.java @@ -98,4 +98,31 @@ static void checkLiteralArgument(String functionName, Expr arg, String argName) { Preconditions.checkArgument(arg.isLiteral(), createErrMsg(functionName, argName + " arg must be a literal")); } + + /** + * True if Expr is a string literal. + * + * In non-SQL-compliant null handling mode, this method will return true for null literals as well (because they are + * treated equivalently to empty strings, and we cannot tell the difference.) + * + * In SQL-compliant null handling mode, this method will return true for actual strings only, not nulls. + */ + static boolean isStringLiteral(final Expr expr) + { + return (expr.isLiteral() && expr.getLiteralValue() instanceof String) + || (NullHandling.replaceWithDefault() && isNullLiteral(expr)); + } + + /** + * True if Expr is a null literal. + * + * In non-SQL-compliant null handling mode, this method will return true for either a null literal or an empty string + * literal (they are treated equivalently and we cannot tell the difference). + * + * In SQL-compliant null handling mode, this method will only return true for an actual null literal. + */ + static boolean isNullLiteral(final Expr expr) + { + return expr.isLiteral() && expr.getLiteralValue() == null; + } } diff --git a/processing/src/main/java/org/apache/druid/query/expression/RegexpExtractExprMacro.java b/processing/src/main/java/org/apache/druid/query/expression/RegexpExtractExprMacro.java index 7428ede84647..9bef704a663e 100644 --- a/processing/src/main/java/org/apache/druid/query/expression/RegexpExtractExprMacro.java +++ b/processing/src/main/java/org/apache/druid/query/expression/RegexpExtractExprMacro.java @@ -52,12 +52,18 @@ public Expr apply(final List args) final Expr patternExpr = args.get(1); final Expr indexExpr = args.size() > 2 ? args.get(2) : null; - if (!patternExpr.isLiteral() || (indexExpr != null && !indexExpr.isLiteral())) { - throw new IAE("Function[%s] pattern and index must be literals", name()); + if (!ExprUtils.isStringLiteral(patternExpr)) { + throw new IAE("Function[%s] pattern must be a string literal", name()); + } + + if (indexExpr != null && (!indexExpr.isLiteral() || !(indexExpr.getLiteralValue() instanceof Number))) { + throw new IAE("Function[%s] index must be a numeric literal", name()); } // Precompile the pattern. - final Pattern pattern = Pattern.compile(String.valueOf(patternExpr.getLiteralValue())); + final Pattern pattern = Pattern.compile( + StringUtils.nullToEmptyNonDruidDataString((String) patternExpr.getLiteralValue()) + ); final int index = indexExpr == null ? 0 : ((Number) indexExpr.getLiteralValue()).intValue(); @@ -72,10 +78,16 @@ private RegexpExtractExpr(Expr arg) @Override public ExprEval eval(final ObjectBinding bindings) { - String s = arg.eval(bindings).asString(); - final Matcher matcher = pattern.matcher(NullHandling.nullToEmptyIfNeeded(s)); - final String retVal = matcher.find() ? matcher.group(index) : null; - return ExprEval.of(NullHandling.emptyToNullIfNeeded(retVal)); + final String s = NullHandling.nullToEmptyIfNeeded(arg.eval(bindings).asString()); + + if (s == null) { + // True nulls do not match anything. Note: this branch only executes in SQL-compatible null handling mode. + return ExprEval.of(null); + } else { + final Matcher matcher = pattern.matcher(NullHandling.nullToEmptyIfNeeded(s)); + final String retVal = matcher.find() ? matcher.group(index) : null; + return ExprEval.of(retVal); + } } @Override diff --git a/processing/src/main/java/org/apache/druid/query/expression/RegexpLikeExprMacro.java b/processing/src/main/java/org/apache/druid/query/expression/RegexpLikeExprMacro.java new file mode 100644 index 000000000000..83735e863494 --- /dev/null +++ b/processing/src/main/java/org/apache/druid/query/expression/RegexpLikeExprMacro.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.expression; + +import org.apache.druid.common.config.NullHandling; +import org.apache.druid.java.util.common.IAE; +import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.math.expr.Expr; +import org.apache.druid.math.expr.ExprEval; +import org.apache.druid.math.expr.ExprMacroTable; +import org.apache.druid.math.expr.ExprType; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class RegexpLikeExprMacro implements ExprMacroTable.ExprMacro +{ + private static final String FN_NAME = "regexp_like"; + + @Override + public String name() + { + return FN_NAME; + } + + @Override + public Expr apply(final List args) + { + if (args.size() != 2) { + throw new IAE("Function[%s] must have 2 arguments", name()); + } + + final Expr arg = args.get(0); + final Expr patternExpr = args.get(1); + + if (!ExprUtils.isStringLiteral(patternExpr)) { + throw new IAE("Function[%s] pattern must be a string literal", name()); + } + + // Precompile the pattern. + final Pattern pattern = Pattern.compile( + StringUtils.nullToEmptyNonDruidDataString((String) patternExpr.getLiteralValue()) + ); + + class RegexpLikeExpr extends ExprMacroTable.BaseScalarUnivariateMacroFunctionExpr + { + private RegexpLikeExpr(Expr arg) + { + super(FN_NAME, arg); + } + + @Nonnull + @Override + public ExprEval eval(final ObjectBinding bindings) + { + final String s = NullHandling.nullToEmptyIfNeeded(arg.eval(bindings).asString()); + + if (s == null) { + // True nulls do not match anything. Note: this branch only executes in SQL-compatible null handling mode. + return ExprEval.of(false, ExprType.LONG); + } else { + final Matcher matcher = pattern.matcher(s); + return ExprEval.of(matcher.find(), ExprType.LONG); + } + } + + @Override + public Expr visit(Shuttle shuttle) + { + Expr newArg = arg.visit(shuttle); + return shuttle.visit(new RegexpLikeExpr(newArg)); + } + + @Override + public String stringify() + { + return StringUtils.format("%s(%s, %s)", FN_NAME, arg.stringify(), patternExpr.stringify()); + } + } + return new RegexpLikeExpr(arg); + } +} diff --git a/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressMatchExprMacroTest.java b/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressMatchExprMacroTest.java index 0b63a720b840..aa5bd917bf13 100644 --- a/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressMatchExprMacroTest.java +++ b/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressMatchExprMacroTest.java @@ -23,7 +23,6 @@ import org.apache.druid.math.expr.ExprEval; import org.apache.druid.math.expr.ExprMacroTable; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import java.util.Arrays; @@ -42,12 +41,9 @@ public class IPv4AddressMatchExprMacroTest extends MacroTestBase private static final Expr SUBNET_10 = ExprEval.of("10.0.0.0/8").toExpr(); private static final Expr NOT_LITERAL = new NotLiteralExpr(null); - private IPv4AddressMatchExprMacro target; - - @Before - public void setUp() + public IPv4AddressMatchExprMacroTest() { - target = new IPv4AddressMatchExprMacro(); + super(new IPv4AddressMatchExprMacro()); } @Test @@ -55,7 +51,7 @@ public void testTooFewArgs() { expectException(IllegalArgumentException.class, "must have 2 arguments"); - target.apply(Collections.emptyList()); + apply(Collections.emptyList()); } @Test @@ -63,7 +59,7 @@ public void testTooManyArgs() { expectException(IllegalArgumentException.class, "must have 2 arguments"); - target.apply(Arrays.asList(IPV4, SUBNET_192_168, NOT_LITERAL)); + apply(Arrays.asList(IPV4, SUBNET_192_168, NOT_LITERAL)); } @Test @@ -71,7 +67,7 @@ public void testSubnetArgNotLiteral() { expectException(IllegalArgumentException.class, "subnet arg must be a literal"); - target.apply(Arrays.asList(IPV4, NOT_LITERAL)); + apply(Arrays.asList(IPV4, NOT_LITERAL)); } @Test @@ -80,7 +76,7 @@ public void testSubnetArgInvalid() expectException(IllegalArgumentException.class, "subnet arg has an invalid format"); Expr invalidSubnet = ExprEval.of("192.168.0.1/invalid").toExpr(); - target.apply(Arrays.asList(IPV4, invalidSubnet)); + apply(Arrays.asList(IPV4, invalidSubnet)); } @Test @@ -182,7 +178,7 @@ public void testInclusive() private boolean eval(Expr... args) { - Expr expr = target.apply(Arrays.asList(args)); + Expr expr = apply(Arrays.asList(args)); ExprEval eval = expr.eval(ExprUtils.nilBindings()); return eval.asBoolean(); } diff --git a/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressParseExprMacroTest.java b/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressParseExprMacroTest.java index 2bf392141d51..0d70b2cce886 100644 --- a/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressParseExprMacroTest.java +++ b/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressParseExprMacroTest.java @@ -23,7 +23,6 @@ import org.apache.druid.math.expr.Expr; import org.apache.druid.math.expr.ExprEval; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import java.util.Arrays; @@ -35,12 +34,9 @@ public class IPv4AddressParseExprMacroTest extends MacroTestBase private static final long EXPECTED = 3232235521L; private static final Long NULL = NullHandling.replaceWithDefault() ? NullHandling.ZERO_LONG : null; - private IPv4AddressParseExprMacro target; - - @Before - public void setUp() + public IPv4AddressParseExprMacroTest() { - target = new IPv4AddressParseExprMacro(); + super(new IPv4AddressParseExprMacro()); } @Test @@ -48,7 +44,7 @@ public void testTooFewArgs() { expectException(IllegalArgumentException.class, "must have 1 argument"); - target.apply(Collections.emptyList()); + apply(Collections.emptyList()); } @Test @@ -56,7 +52,7 @@ public void testTooManyArgs() { expectException(IllegalArgumentException.class, "must have 1 argument"); - target.apply(Arrays.asList(VALID, VALID)); + apply(Arrays.asList(VALID, VALID)); } @Test @@ -154,7 +150,7 @@ public void testValidLongArg() private Object eval(Expr arg) { - Expr expr = target.apply(Collections.singletonList(arg)); + Expr expr = apply(Collections.singletonList(arg)); ExprEval eval = expr.eval(ExprUtils.nilBindings()); return eval.value(); } diff --git a/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressStringifyExprMacroTest.java b/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressStringifyExprMacroTest.java index 602d00cfcc4e..1b4235b30686 100644 --- a/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressStringifyExprMacroTest.java +++ b/processing/src/test/java/org/apache/druid/query/expression/IPv4AddressStringifyExprMacroTest.java @@ -23,7 +23,6 @@ import org.apache.druid.math.expr.Expr; import org.apache.druid.math.expr.ExprEval; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import java.util.Arrays; @@ -35,12 +34,9 @@ public class IPv4AddressStringifyExprMacroTest extends MacroTestBase private static final String EXPECTED = "192.168.0.1"; private static final String NULL = NullHandling.replaceWithDefault() ? "0.0.0.0" : null; - private IPv4AddressStringifyExprMacro target; - - @Before - public void setUp() + public IPv4AddressStringifyExprMacroTest() { - target = new IPv4AddressStringifyExprMacro(); + super(new IPv4AddressStringifyExprMacro()); } @Test @@ -48,7 +44,7 @@ public void testTooFewArgs() { expectException(IllegalArgumentException.class, "must have 1 argument"); - target.apply(Collections.emptyList()); + apply(Collections.emptyList()); } @Test @@ -56,7 +52,7 @@ public void testTooManyArgs() { expectException(IllegalArgumentException.class, "must have 1 argument"); - target.apply(Arrays.asList(VALID, VALID)); + apply(Arrays.asList(VALID, VALID)); } @Test @@ -150,7 +146,7 @@ public void testValidStringArgUnsignedInt() private Object eval(Expr arg) { - Expr expr = target.apply(Collections.singletonList(arg)); + Expr expr = apply(Collections.singletonList(arg)); ExprEval eval = expr.eval(ExprUtils.nilBindings()); return eval.value(); } diff --git a/processing/src/test/java/org/apache/druid/query/expression/MacroTestBase.java b/processing/src/test/java/org/apache/druid/query/expression/MacroTestBase.java index 38e607c2b8f4..7cf79073d0c9 100644 --- a/processing/src/test/java/org/apache/druid/query/expression/MacroTestBase.java +++ b/processing/src/test/java/org/apache/druid/query/expression/MacroTestBase.java @@ -19,18 +19,80 @@ package org.apache.druid.query.expression; +import com.google.common.collect.ImmutableSet; +import org.apache.druid.math.expr.Expr; +import org.apache.druid.math.expr.ExprEval; +import org.apache.druid.math.expr.ExprMacroTable; +import org.apache.druid.math.expr.Parser; import org.apache.druid.testing.InitializedNullHandlingTest; +import org.junit.Assert; import org.junit.Rule; import org.junit.rules.ExpectedException; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + public abstract class MacroTestBase extends InitializedNullHandlingTest { @Rule public ExpectedException expectedException = ExpectedException.none(); - void expectException(Class type, String message) + private final ExprMacroTable.ExprMacro macro; + + protected MacroTestBase(ExprMacroTable.ExprMacro macro) + { + this.macro = macro; + } + + protected void expectException(Class type, String message) { expectedException.expect(type); expectedException.expectMessage(message); } + + protected Expr apply(final List args) + { + return macro.apply(args); + } + + /** + * Evalutes {@code expr} using our macro. + * + * @param expression expression to evalute + * @param bindings bindings for evaluation + * + * @throws AssertionError if {@link ExprMacroTable.ExprMacro#apply} is not called on our macro during parsing + */ + protected ExprEval eval( + final String expression, + final Expr.ObjectBinding bindings + ) + { + // WrappedExprMacro allows us to confirm that our ExprMacro was actually called. + class WrappedExprMacro implements ExprMacroTable.ExprMacro + { + private final AtomicLong calls = new AtomicLong(); + + @Override + public String name() + { + return macro.name(); + } + + @Override + public Expr apply(List args) + { + calls.incrementAndGet(); + return macro.apply(args); + } + } + + final WrappedExprMacro wrappedMacro = new WrappedExprMacro(); + final GuiceExprMacroTable macroTable = new GuiceExprMacroTable(ImmutableSet.of(wrappedMacro)); + final Expr expr = Parser.parse(expression, macroTable); + + Assert.assertTrue("Calls made to macro.apply", wrappedMacro.calls.get() > 0); + + return expr.eval(bindings); + } } diff --git a/processing/src/test/java/org/apache/druid/query/expression/RegexpExtractExprMacroTest.java b/processing/src/test/java/org/apache/druid/query/expression/RegexpExtractExprMacroTest.java new file mode 100644 index 000000000000..2f811d788b4f --- /dev/null +++ b/processing/src/test/java/org/apache/druid/query/expression/RegexpExtractExprMacroTest.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.expression; + +import com.google.common.collect.ImmutableMap; +import org.apache.druid.common.config.NullHandling; +import org.apache.druid.math.expr.ExprEval; +import org.apache.druid.math.expr.Parser; +import org.junit.Assert; +import org.junit.Test; + +public class RegexpExtractExprMacroTest extends MacroTestBase +{ + public RegexpExtractExprMacroTest() + { + super(new RegexpExtractExprMacro()); + } + + @Test + public void testErrorZeroArguments() + { + expectException(IllegalArgumentException.class, "Function[regexp_extract] must have 2 to 3 arguments"); + eval("regexp_extract()", Parser.withMap(ImmutableMap.of())); + } + + @Test + public void testErrorFourArguments() + { + expectException(IllegalArgumentException.class, "Function[regexp_extract] must have 2 to 3 arguments"); + eval("regexp_extract('a', 'b', 'c', 'd')", Parser.withMap(ImmutableMap.of())); + } + + @Test + public void testMatch() + { + final ExprEval result = eval("regexp_extract(a, 'f(.o)')", Parser.withMap(ImmutableMap.of("a", "foo"))); + Assert.assertEquals("foo", result.value()); + } + + @Test + public void testMatchGroup0() + { + final ExprEval result = eval("regexp_extract(a, 'f(.o)', 0)", Parser.withMap(ImmutableMap.of("a", "foo"))); + Assert.assertEquals("foo", result.value()); + } + + @Test + public void testMatchGroup1() + { + final ExprEval result = eval("regexp_extract(a, 'f(.o)', 1)", Parser.withMap(ImmutableMap.of("a", "foo"))); + Assert.assertEquals("oo", result.value()); + } + + @Test + public void testMatchGroup2() + { + expectedException.expectMessage("No group 2"); + final ExprEval result = eval("regexp_extract(a, 'f(.o)', 2)", Parser.withMap(ImmutableMap.of("a", "foo"))); + } + + @Test + public void testNoMatch() + { + final ExprEval result = eval("regexp_extract(a, 'f(.x)')", Parser.withMap(ImmutableMap.of("a", "foo"))); + Assert.assertNull(result.value()); + } + + @Test + public void testMatchInMiddle() + { + final ExprEval result = eval("regexp_extract(a, '.o$')", Parser.withMap(ImmutableMap.of("a", "foo"))); + Assert.assertEquals("oo", result.value()); + } + + @Test + public void testNullPattern() + { + if (NullHandling.sqlCompatible()) { + expectException(IllegalArgumentException.class, "Function[regexp_extract] pattern must be a string literal"); + } + + final ExprEval result = eval("regexp_extract(a, null)", Parser.withMap(ImmutableMap.of("a", "foo"))); + Assert.assertNull(result.value()); + } + + @Test + public void testEmptyStringPattern() + { + final ExprEval result = eval("regexp_extract(a, '')", Parser.withMap(ImmutableMap.of("a", "foo"))); + Assert.assertEquals(NullHandling.emptyToNullIfNeeded(""), result.value()); + } + + @Test + public void testNumericPattern() + { + expectException(IllegalArgumentException.class, "Function[regexp_extract] pattern must be a string literal"); + eval("regexp_extract(a, 1)", Parser.withMap(ImmutableMap.of("a", "foo"))); + } + + @Test + public void testNonLiteralPattern() + { + expectException(IllegalArgumentException.class, "Function[regexp_extract] pattern must be a string literal"); + eval("regexp_extract(a, a)", Parser.withMap(ImmutableMap.of("a", "foo"))); + } + + @Test + public void testNullPatternOnNull() + { + if (NullHandling.sqlCompatible()) { + expectException(IllegalArgumentException.class, "Function[regexp_extract] pattern must be a string literal"); + } + + final ExprEval result = eval("regexp_extract(a, null)", Parser.withSuppliers(ImmutableMap.of("a", () -> null))); + Assert.assertNull(result.value()); + } + + @Test + public void testEmptyStringPatternOnNull() + { + final ExprEval result = eval("regexp_extract(a, '')", Parser.withSuppliers(ImmutableMap.of("a", () -> null))); + Assert.assertNull(result.value()); + } +} diff --git a/processing/src/test/java/org/apache/druid/query/expression/RegexpLikeExprMacroTest.java b/processing/src/test/java/org/apache/druid/query/expression/RegexpLikeExprMacroTest.java new file mode 100644 index 000000000000..a6bdfb36a03a --- /dev/null +++ b/processing/src/test/java/org/apache/druid/query/expression/RegexpLikeExprMacroTest.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.expression; + +import com.google.common.collect.ImmutableMap; +import org.apache.druid.common.config.NullHandling; +import org.apache.druid.math.expr.ExprEval; +import org.apache.druid.math.expr.ExprType; +import org.apache.druid.math.expr.Parser; +import org.junit.Assert; +import org.junit.Test; + +public class RegexpLikeExprMacroTest extends MacroTestBase +{ + public RegexpLikeExprMacroTest() + { + super(new RegexpLikeExprMacro()); + } + + @Test + public void testErrorZeroArguments() + { + expectException(IllegalArgumentException.class, "Function[regexp_like] must have 2 arguments"); + eval("regexp_like()", Parser.withMap(ImmutableMap.of())); + } + + @Test + public void testErrorThreeArguments() + { + expectException(IllegalArgumentException.class, "Function[regexp_like] must have 2 arguments"); + eval("regexp_like('a', 'b', 'c')", Parser.withMap(ImmutableMap.of())); + } + + @Test + public void testMatch() + { + final ExprEval result = eval("regexp_like(a, 'f.o')", Parser.withMap(ImmutableMap.of("a", "foo"))); + Assert.assertEquals( + ExprEval.of(true, ExprType.LONG).value(), + result.value() + ); + } + + @Test + public void testNoMatch() + { + final ExprEval result = eval("regexp_like(a, 'f.x')", Parser.withMap(ImmutableMap.of("a", "foo"))); + Assert.assertEquals( + ExprEval.of(false, ExprType.LONG).value(), + result.value() + ); + } + + @Test + public void testNullPattern() + { + if (NullHandling.sqlCompatible()) { + expectException(IllegalArgumentException.class, "Function[regexp_like] pattern must be a string literal"); + } + + final ExprEval result = eval("regexp_like(a, null)", Parser.withMap(ImmutableMap.of("a", "foo"))); + Assert.assertEquals( + ExprEval.of(true, ExprType.LONG).value(), + result.value() + ); + } + + @Test + public void testEmptyStringPattern() + { + final ExprEval result = eval("regexp_like(a, '')", Parser.withMap(ImmutableMap.of("a", "foo"))); + Assert.assertEquals( + ExprEval.of(true, ExprType.LONG).value(), + result.value() + ); + } + + @Test + public void testNullPatternOnEmptyString() + { + if (NullHandling.sqlCompatible()) { + expectException(IllegalArgumentException.class, "Function[regexp_like] pattern must be a string literal"); + } + + final ExprEval result = eval("regexp_like(a, null)", Parser.withMap(ImmutableMap.of("a", ""))); + Assert.assertEquals( + ExprEval.of(true, ExprType.LONG).value(), + result.value() + ); + } + + @Test + public void testEmptyStringPatternOnEmptyString() + { + final ExprEval result = eval("regexp_like(a, '')", Parser.withMap(ImmutableMap.of("a", ""))); + Assert.assertEquals( + ExprEval.of(true, ExprType.LONG).value(), + result.value() + ); + } + + @Test + public void testNullPatternOnNull() + { + if (NullHandling.sqlCompatible()) { + expectException(IllegalArgumentException.class, "Function[regexp_like] pattern must be a string literal"); + } + + final ExprEval result = eval("regexp_like(a, null)", Parser.withSuppliers(ImmutableMap.of("a", () -> null))); + Assert.assertEquals( + ExprEval.of(true, ExprType.LONG).value(), + result.value() + ); + } + + @Test + public void testEmptyStringPatternOnNull() + { + final ExprEval result = eval("regexp_like(a, '')", Parser.withSuppliers(ImmutableMap.of("a", () -> null))); + Assert.assertEquals( + ExprEval.of(NullHandling.replaceWithDefault(), ExprType.LONG).value(), + result.value() + ); + } +} diff --git a/server/src/main/java/org/apache/druid/guice/ExpressionModule.java b/server/src/main/java/org/apache/druid/guice/ExpressionModule.java index f695563d352f..9e451e8f12f9 100644 --- a/server/src/main/java/org/apache/druid/guice/ExpressionModule.java +++ b/server/src/main/java/org/apache/druid/guice/ExpressionModule.java @@ -31,6 +31,7 @@ import org.apache.druid.query.expression.IPv4AddressStringifyExprMacro; import org.apache.druid.query.expression.LikeExprMacro; import org.apache.druid.query.expression.RegexpExtractExprMacro; +import org.apache.druid.query.expression.RegexpLikeExprMacro; import org.apache.druid.query.expression.TimestampCeilExprMacro; import org.apache.druid.query.expression.TimestampExtractExprMacro; import org.apache.druid.query.expression.TimestampFloorExprMacro; @@ -41,8 +42,6 @@ import java.util.List; -/** - */ public class ExpressionModule implements DruidModule { public static final List> EXPR_MACROS = @@ -52,6 +51,7 @@ public class ExpressionModule implements DruidModule .add(IPv4AddressStringifyExprMacro.class) .add(LikeExprMacro.class) .add(RegexpExtractExprMacro.class) + .add(RegexpLikeExprMacro.class) .add(TimestampCeilExprMacro.class) .add(TimestampExtractExprMacro.class) .add(TimestampFloorExprMacro.class) diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/OperatorConversions.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/OperatorConversions.java index 6b4a4539bee5..23c48df601b1 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/OperatorConversions.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/OperatorConversions.java @@ -24,11 +24,13 @@ import com.google.common.collect.Iterables; import it.unimi.dsi.fastutil.ints.IntArraySet; import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.IntSets; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexCall; import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.rex.RexNode; +import org.apache.calcite.runtime.CalciteException; import org.apache.calcite.sql.SqlCallBinding; import org.apache.calcite.sql.SqlFunction; import org.apache.calcite.sql.SqlFunctionCategory; @@ -222,6 +224,10 @@ public static PostAggregator toPostAggregator( } } + /** + * Returns a builder that helps {@link SqlOperatorConversion} implementations create the {@link SqlFunction} + * objects they need to return from {@link SqlOperatorConversion#calciteOperator()}. + */ public static OperatorBuilder operatorBuilder(final String name) { return new OperatorBuilder(name); @@ -238,6 +244,7 @@ public static class OperatorBuilder private SqlOperandTypeChecker operandTypeChecker; private List operandTypes; private Integer requiredOperands = null; + private int[] literalOperands = null; private SqlOperandTypeInference operandTypeInference; private OperatorBuilder(final String name) @@ -245,64 +252,123 @@ private OperatorBuilder(final String name) this.name = Preconditions.checkNotNull(name, "name"); } - public OperatorBuilder kind(final SqlKind kind) + /** + * Sets the return type of the operator to "typeName", marked as non-nullable. + * + * One of {@link #returnTypeNonNull}, {@link #returnTypeNullable}, or + * {@link #returnTypeInference(SqlReturnTypeInference)} must be used before calling {@link #build()}. These methods + * cannot be mixed; you must call exactly one. + */ + public OperatorBuilder returnTypeNonNull(final SqlTypeName typeName) { - this.kind = kind; - return this; - } + Preconditions.checkState(this.returnTypeInference == null, "Cannot set return type multiple times"); - public OperatorBuilder returnType(final SqlTypeName typeName) - { this.returnTypeInference = ReturnTypes.explicit( factory -> Calcites.createSqlType(factory, typeName) ); return this; } - public OperatorBuilder nullableReturnType(final SqlTypeName typeName) + /** + * Sets the return type of the operator to "typeName", marked as nullable. + * + * One of {@link #returnTypeNonNull}, {@link #returnTypeNullable}, or + * {@link #returnTypeInference(SqlReturnTypeInference)} must be used before calling {@link #build()}. These methods + * cannot be mixed; you must call exactly one. + */ + public OperatorBuilder returnTypeNullable(final SqlTypeName typeName) { + Preconditions.checkState(this.returnTypeInference == null, "Cannot set return type multiple times"); + this.returnTypeInference = ReturnTypes.explicit( factory -> Calcites.createSqlTypeWithNullability(factory, typeName, true) ); return this; } + /** + * Provides customized return type inference logic. + * + * One of {@link #returnTypeNonNull}, {@link #returnTypeNullable}, or + * {@link #returnTypeInference(SqlReturnTypeInference)} must be used before calling {@link #build()}. These methods + * cannot be mixed; you must call exactly one. + */ public OperatorBuilder returnTypeInference(final SqlReturnTypeInference returnTypeInference) { + Preconditions.checkState(this.returnTypeInference == null, "Cannot set return type multiple times"); + this.returnTypeInference = returnTypeInference; return this; } + /** + * Sets the {@link SqlKind} of the operator. + * + * The default, if not provided, is {@link SqlFunctionCategory#USER_DEFINED_FUNCTION}. + */ public OperatorBuilder functionCategory(final SqlFunctionCategory functionCategory) { this.functionCategory = functionCategory; return this; } + /** + * Provides customized operand type checking logic. + * + * One of {@link #operandTypes(SqlTypeFamily...)} or {@link #operandTypeChecker(SqlOperandTypeChecker)} must be used + * before calling {@link #build()}. These methods cannot be mixed; you must call exactly one. + */ public OperatorBuilder operandTypeChecker(final SqlOperandTypeChecker operandTypeChecker) { this.operandTypeChecker = operandTypeChecker; return this; } + /** + * Signifies that a function accepts operands of type family given by {@param operandTypes}. + * + * May be used in conjunction with {@link #requiredOperands(int)} and {@link #literalOperands(int...)} in order + * to further refine operand checking logic. + * + * For deeper control, use {@link #operandTypeChecker(SqlOperandTypeChecker)} instead. + */ public OperatorBuilder operandTypes(final SqlTypeFamily... operandTypes) { this.operandTypes = Arrays.asList(operandTypes); return this; } - public OperatorBuilder operandTypeInference(final SqlOperandTypeInference operandTypeInference) + /** + * Signifies that the first {@code requiredOperands} operands are required, and all later operands are optional. + * + * Required operands are not allowed to be null. Optional operands can either be skipped or explicitly provided as + * literal NULLs. For example, if {@code requiredOperands == 1}, then {@code F(x, NULL)} and {@code F(x)} are both + * accepted, and {@code x} must not be null. + * + * Must be used in conjunction with {@link #operandTypes(SqlTypeFamily...)}; this method is not compatible with + * {@link #operandTypeChecker(SqlOperandTypeChecker)}. + */ + public OperatorBuilder requiredOperands(final int requiredOperands) { - this.operandTypeInference = operandTypeInference; + this.requiredOperands = requiredOperands; return this; } - public OperatorBuilder requiredOperands(final int requiredOperands) + /** + * Signifies that the operands at positions given by {@code literalOperands} must be literals. + * + * Must be used in conjunction with {@link #operandTypes(SqlTypeFamily...)}; this method is not compatible with + * {@link #operandTypeChecker(SqlOperandTypeChecker)}. + */ + public OperatorBuilder literalOperands(final int... literalOperands) { - this.requiredOperands = requiredOperands; + this.literalOperands = literalOperands; return this; } + /** + * Creates a {@link SqlFunction} from this builder. + */ public SqlFunction build() { // Create "nullableOperands" set including all optional arguments. @@ -317,13 +383,14 @@ public SqlFunction build() theOperandTypeChecker = new DefaultOperandTypeChecker( operandTypes, requiredOperands == null ? operandTypes.size() : requiredOperands, - nullableOperands + nullableOperands, + literalOperands ); - } else if (operandTypes == null && requiredOperands == null) { + } else if (operandTypes == null && requiredOperands == null && literalOperands == null) { theOperandTypeChecker = operandTypeChecker; } else { throw new ISE( - "Cannot have both 'operandTypeChecker' and 'operandTypes' / 'requiredOperands'" + "Cannot have both 'operandTypeChecker' and 'operandTypes' / 'requiredOperands' / 'literalOperands'" ); } @@ -430,36 +497,56 @@ public void inferOperandTypes( /** * Operand type checker that is used in 'simple' situations: there are a particular number of operands, with - * particular types, some of which may be optional or nullable. + * particular types, some of which may be optional or nullable, and some of which may be required to be literals. */ private static class DefaultOperandTypeChecker implements SqlOperandTypeChecker { private final List operandTypes; private final int requiredOperands; private final IntSet nullableOperands; + private final IntSet literalOperands; DefaultOperandTypeChecker( final List operandTypes, final int requiredOperands, - final IntSet nullableOperands + final IntSet nullableOperands, + @Nullable final int[] literalOperands ) { Preconditions.checkArgument(requiredOperands <= operandTypes.size() && requiredOperands >= 0); this.operandTypes = Preconditions.checkNotNull(operandTypes, "operandTypes"); this.requiredOperands = requiredOperands; this.nullableOperands = Preconditions.checkNotNull(nullableOperands, "nullableOperands"); + + if (literalOperands == null) { + this.literalOperands = IntSets.EMPTY_SET; + } else { + this.literalOperands = new IntArraySet(); + Arrays.stream(literalOperands).forEach(this.literalOperands::add); + } } @Override public boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFailure) { - if (operandTypes.size() != callBinding.getOperandCount()) { - // Just like FamilyOperandTypeChecker: assume this is an inapplicable sub-rule of a composite rule; don't throw - return false; - } - for (int i = 0; i < callBinding.operands().size(); i++) { final SqlNode operand = callBinding.operands().get(i); + + if (literalOperands.contains(i)) { + // Verify that 'operand' is a literal. + if (!SqlUtil.isLiteral(operand)) { + return throwOrReturn( + throwOnFailure, + callBinding, + cb -> cb.getValidator() + .newValidationError( + operand, + Static.RESOURCE.argumentMustBeLiteral(callBinding.getOperator().getName()) + ) + ); + } + } + final RelDataType operandType = callBinding.getValidator().deriveType(callBinding.getScope(), operand); final SqlTypeFamily expectedFamily = operandTypes.get(i); @@ -467,21 +554,21 @@ public boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFail // ANY matches anything. This operand is all good; do nothing. } else if (expectedFamily.getTypeNames().contains(operandType.getSqlTypeName())) { // Operand came in with one of the expected types. - } else if (operandType.getSqlTypeName() == SqlTypeName.NULL) { + } else if (operandType.getSqlTypeName() == SqlTypeName.NULL || SqlUtil.isNullLiteral(operand, true)) { // Null came in, check if operand is a nullable type. if (!nullableOperands.contains(i)) { - if (throwOnFailure) { - throw callBinding.getValidator().newValidationError(operand, Static.RESOURCE.nullIllegal()); - } else { - return false; - } + return throwOrReturn( + throwOnFailure, + callBinding, + cb -> cb.getValidator().newValidationError(operand, Static.RESOURCE.nullIllegal()) + ); } } else { - if (throwOnFailure) { - throw callBinding.newValidationSignatureError(); - } else { - return false; - } + return throwOrReturn( + throwOnFailure, + callBinding, + SqlCallBinding::newValidationSignatureError + ); } } @@ -512,4 +599,17 @@ public boolean isOptional(int i) return i + 1 > requiredOperands; } } + + private static boolean throwOrReturn( + final boolean throwOnFailure, + final SqlCallBinding callBinding, + final Function exceptionMapper + ) + { + if (throwOnFailure) { + throw exceptionMapper.apply(callBinding); + } else { + return false; + } + } } diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayLengthOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayLengthOperatorConversion.java index bcafbb605c69..3bcd6ec0de06 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayLengthOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayLengthOperatorConversion.java @@ -44,7 +44,7 @@ public class ArrayLengthOperatorConversion implements SqlOperatorConversion ) ) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.INTEGER) + .returnTypeNonNull(SqlTypeName.INTEGER) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOffsetOfOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOffsetOfOperatorConversion.java index 2e06ef9aa290..8d8da7461bb0 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOffsetOfOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOffsetOfOperatorConversion.java @@ -48,7 +48,7 @@ public class ArrayOffsetOfOperatorConversion implements SqlOperatorConversion ) ) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.INTEGER) + .returnTypeNonNull(SqlTypeName.INTEGER) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOffsetOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOffsetOperatorConversion.java index 42a874da5e45..2fdee5d79b24 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOffsetOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOffsetOperatorConversion.java @@ -48,7 +48,7 @@ public class ArrayOffsetOperatorConversion implements SqlOperatorConversion ) ) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOrdinalOfOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOrdinalOfOperatorConversion.java index 367d80ff1cc4..14fa9d61f3fd 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOrdinalOfOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOrdinalOfOperatorConversion.java @@ -48,7 +48,7 @@ public class ArrayOrdinalOfOperatorConversion implements SqlOperatorConversion ) ) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.INTEGER) + .returnTypeNonNull(SqlTypeName.INTEGER) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOrdinalOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOrdinalOperatorConversion.java index 1dd9c4a800d0..c3d1302dc41e 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOrdinalOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayOrdinalOperatorConversion.java @@ -48,7 +48,7 @@ public class ArrayOrdinalOperatorConversion implements SqlOperatorConversion ) ) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayToStringOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayToStringOperatorConversion.java index 3d9331fa7100..802a95e5a369 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayToStringOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ArrayToStringOperatorConversion.java @@ -48,7 +48,7 @@ public class ArrayToStringOperatorConversion implements SqlOperatorConversion ) ) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/BTrimOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/BTrimOperatorConversion.java index 4e45c5a0fd97..d77c20b5d787 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/BTrimOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/BTrimOperatorConversion.java @@ -37,7 +37,7 @@ public class BTrimOperatorConversion implements SqlOperatorConversion private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder("BTRIM") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .functionCategory(SqlFunctionCategory.STRING) .requiredOperands(1) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/DateTruncOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/DateTruncOperatorConversion.java index 9e4bf3c3d50e..f496e0ab9671 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/DateTruncOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/DateTruncOperatorConversion.java @@ -67,7 +67,7 @@ public class DateTruncOperatorConversion implements SqlOperatorConversion .operatorBuilder("DATE_TRUNC") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.TIMESTAMP) .requiredOperands(2) - .returnType(SqlTypeName.TIMESTAMP) + .returnTypeNonNull(SqlTypeName.TIMESTAMP) .functionCategory(SqlFunctionCategory.TIMEDATE) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressStringifyOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressStringifyOperatorConversion.java index 6bd02b41221e..134723188d2e 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressStringifyOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressStringifyOperatorConversion.java @@ -39,7 +39,7 @@ public class IPv4AddressStringifyOperatorConversion extends DirectOperatorConver OperandTypes.family(SqlTypeFamily.INTEGER), OperandTypes.family(SqlTypeFamily.STRING) )) - .nullableReturnType(SqlTypeName.CHAR) + .returnTypeNullable(SqlTypeName.CHAR) .functionCategory(SqlFunctionCategory.USER_DEFINED_FUNCTION) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LPadOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LPadOperatorConversion.java index 6a2d4f59b6ee..2d13b02fcbbd 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LPadOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LPadOperatorConversion.java @@ -37,7 +37,7 @@ public class LPadOperatorConversion implements SqlOperatorConversion private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder("LPAD") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER, SqlTypeFamily.CHARACTER) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .functionCategory(SqlFunctionCategory.STRING) .requiredOperands(2) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LTrimOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LTrimOperatorConversion.java index d3ac45da0fc1..70ec0c97e621 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LTrimOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LTrimOperatorConversion.java @@ -37,7 +37,7 @@ public class LTrimOperatorConversion implements SqlOperatorConversion private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder("LTRIM") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .functionCategory(SqlFunctionCategory.STRING) .requiredOperands(1) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LeftOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LeftOperatorConversion.java index 7363974eb79e..252343cddba1 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LeftOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/LeftOperatorConversion.java @@ -39,7 +39,7 @@ public class LeftOperatorConversion implements SqlOperatorConversion .operatorBuilder("LEFT") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MillisToTimestampOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MillisToTimestampOperatorConversion.java index 227279f5a2f7..e8b8e748a640 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MillisToTimestampOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MillisToTimestampOperatorConversion.java @@ -39,7 +39,7 @@ public class MillisToTimestampOperatorConversion implements SqlOperatorConversio private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder("MILLIS_TO_TIMESTAMP") .operandTypes(SqlTypeFamily.EXACT_NUMERIC) - .returnType(SqlTypeName.TIMESTAMP) + .returnTypeNonNull(SqlTypeName.TIMESTAMP) .functionCategory(SqlFunctionCategory.TIMEDATE) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringAppendOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringAppendOperatorConversion.java index fb4745425ada..d57d0fcb1a4a 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringAppendOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringAppendOperatorConversion.java @@ -47,7 +47,7 @@ public class MultiValueStringAppendOperatorConversion implements SqlOperatorConv ) ) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringConcatOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringConcatOperatorConversion.java index 03e1c7c63bca..455715fe8f5c 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringConcatOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringConcatOperatorConversion.java @@ -44,7 +44,7 @@ public class MultiValueStringConcatOperatorConversion implements SqlOperatorConv ) ) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringPrependOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringPrependOperatorConversion.java index 4b7cc968df1f..6fa04ce176cd 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringPrependOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringPrependOperatorConversion.java @@ -47,7 +47,7 @@ public class MultiValueStringPrependOperatorConversion implements SqlOperatorCon ) ) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringSliceOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringSliceOperatorConversion.java index cba6ee66eec9..f278b3bf7ebb 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringSliceOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/MultiValueStringSliceOperatorConversion.java @@ -52,7 +52,7 @@ public class MultiValueStringSliceOperatorConversion implements SqlOperatorConve ) ) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ParseLongOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ParseLongOperatorConversion.java index 29b0906e4174..9fd710fb1cb4 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ParseLongOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ParseLongOperatorConversion.java @@ -38,7 +38,7 @@ public class ParseLongOperatorConversion implements SqlOperatorConversion private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder(NAME) .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER) - .returnType(SqlTypeName.BIGINT) + .returnTypeNonNull(SqlTypeName.BIGINT) .functionCategory(SqlFunctionCategory.STRING) .requiredOperands(1) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/QueryLookupOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/QueryLookupOperatorConversion.java index 4fa27af75037..5c1665c7083c 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/QueryLookupOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/QueryLookupOperatorConversion.java @@ -40,7 +40,7 @@ public class QueryLookupOperatorConversion implements SqlOperatorConversion private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder("LOOKUP") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .functionCategory(SqlFunctionCategory.STRING) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RPadOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RPadOperatorConversion.java index 68874ef86ba1..47c8eadc2f85 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RPadOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RPadOperatorConversion.java @@ -37,7 +37,7 @@ public class RPadOperatorConversion implements SqlOperatorConversion private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder("RPAD") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER, SqlTypeFamily.CHARACTER) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .functionCategory(SqlFunctionCategory.STRING) .requiredOperands(2) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RTrimOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RTrimOperatorConversion.java index 8361a8a5e8f6..6aa8f1b28a6f 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RTrimOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RTrimOperatorConversion.java @@ -37,7 +37,7 @@ public class RTrimOperatorConversion implements SqlOperatorConversion private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder("RTRIM") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .functionCategory(SqlFunctionCategory.STRING) .requiredOperands(1) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RegexpExtractOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RegexpExtractOperatorConversion.java index 18da1b31bff5..b6469718255e 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RegexpExtractOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RegexpExtractOperatorConversion.java @@ -39,7 +39,8 @@ public class RegexpExtractOperatorConversion implements SqlOperatorConversion .operatorBuilder("REGEXP_EXTRACT") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER) .requiredOperands(2) - .returnType(SqlTypeName.VARCHAR) + .literalOperands(1, 2) + .returnTypeNullable(SqlTypeName.VARCHAR) .functionCategory(SqlFunctionCategory.STRING) .build(); @@ -71,9 +72,13 @@ public DruidExpression toDruidExpression( : null; if (arg.isSimpleExtraction() && patternExpr.isLiteral() && (indexExpr == null || indexExpr.isLiteral())) { + final String pattern = (String) patternExpr.getLiteralValue(); + return arg.getSimpleExtraction().cascade( new RegexDimExtractionFn( - (String) patternExpr.getLiteralValue(), + // Undo the empty-to-null conversion from patternExpr parsing (patterns cannot be null, even in + // non-SQL-compliant null handling mode). + StringUtils.nullToEmptyNonDruidDataString(pattern), indexExpr == null ? DEFAULT_INDEX : ((Number) indexExpr.getLiteralValue()).intValue(), true, null diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RegexpLikeOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RegexpLikeOperatorConversion.java new file mode 100644 index 000000000000..ea699abe0215 --- /dev/null +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RegexpLikeOperatorConversion.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.sql.calcite.expression.builtin; + +import org.apache.calcite.rex.RexCall; +import org.apache.calcite.rex.RexLiteral; +import org.apache.calcite.rex.RexNode; +import org.apache.calcite.sql.SqlFunction; +import org.apache.calcite.sql.SqlFunctionCategory; +import org.apache.calcite.sql.type.SqlTypeFamily; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.druid.query.filter.DimFilter; +import org.apache.druid.query.filter.RegexDimFilter; +import org.apache.druid.segment.VirtualColumn; +import org.apache.druid.segment.column.RowSignature; +import org.apache.druid.sql.calcite.expression.DruidExpression; +import org.apache.druid.sql.calcite.expression.Expressions; +import org.apache.druid.sql.calcite.expression.OperatorConversions; +import org.apache.druid.sql.calcite.expression.SqlOperatorConversion; +import org.apache.druid.sql.calcite.planner.PlannerContext; +import org.apache.druid.sql.calcite.rel.VirtualColumnRegistry; + +import javax.annotation.Nullable; +import java.util.List; + +public class RegexpLikeOperatorConversion implements SqlOperatorConversion +{ + private static final SqlFunction SQL_FUNCTION = OperatorConversions + .operatorBuilder("REGEXP_LIKE") + .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER) + .requiredOperands(2) + .literalOperands(1) + .returnTypeNonNull(SqlTypeName.BOOLEAN) + .functionCategory(SqlFunctionCategory.STRING) + .build(); + + @Override + public SqlFunction calciteOperator() + { + return SQL_FUNCTION; + } + + @Override + public DruidExpression toDruidExpression( + final PlannerContext plannerContext, + final RowSignature rowSignature, + final RexNode rexNode + ) + { + return OperatorConversions.convertCall( + plannerContext, + rowSignature, + rexNode, + operands -> DruidExpression.fromFunctionCall("regexp_like", operands) + ); + } + + @Nullable + @Override + public DimFilter toDruidFilter( + final PlannerContext plannerContext, + final RowSignature rowSignature, + @Nullable final VirtualColumnRegistry virtualColumnRegistry, + final RexNode rexNode + ) + { + final List operands = ((RexCall) rexNode).getOperands(); + final DruidExpression druidExpression = Expressions.toDruidExpression( + plannerContext, + rowSignature, + operands.get(0) + ); + + if (druidExpression == null) { + return null; + } + + final String pattern = RexLiteral.stringValue(operands.get(1)); + + if (druidExpression.isSimpleExtraction()) { + return new RegexDimFilter( + druidExpression.getSimpleExtraction().getColumn(), + pattern, + druidExpression.getSimpleExtraction().getExtractionFn(), + null + ); + } else if (virtualColumnRegistry != null) { + VirtualColumn v = virtualColumnRegistry.getOrCreateVirtualColumnForExpression( + plannerContext, + druidExpression, + operands.get(0).getType().getSqlTypeName() + ); + + return new RegexDimFilter(v.getOutputName(), pattern, null, null); + } else { + return null; + } + } +} diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RepeatOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RepeatOperatorConversion.java index 318e95d9b4b0..9521a0443bcc 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RepeatOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RepeatOperatorConversion.java @@ -39,7 +39,7 @@ public class RepeatOperatorConversion implements SqlOperatorConversion .operatorBuilder("REPEAT") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ReverseOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ReverseOperatorConversion.java index 7cb0f0552976..70280abf2f98 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ReverseOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ReverseOperatorConversion.java @@ -37,7 +37,7 @@ public class ReverseOperatorConversion implements SqlOperatorConversion .operatorBuilder("REVERSE") .operandTypes(SqlTypeFamily.CHARACTER) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RightOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RightOperatorConversion.java index 9fc3736d9f60..863bbccd5578 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RightOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RightOperatorConversion.java @@ -39,7 +39,7 @@ public class RightOperatorConversion implements SqlOperatorConversion .operatorBuilder("RIGHT") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StringFormatOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StringFormatOperatorConversion.java index 3a535cdd9eab..b2aabbb2d11c 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StringFormatOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StringFormatOperatorConversion.java @@ -42,7 +42,7 @@ public class StringFormatOperatorConversion implements SqlOperatorConversion .operatorBuilder("STRING_FORMAT") .operandTypeChecker(new StringFormatOperandTypeChecker()) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StringToMultiValueStringOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StringToMultiValueStringOperatorConversion.java index bdb590888aaa..7a6442f2dc8c 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StringToMultiValueStringOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StringToMultiValueStringOperatorConversion.java @@ -45,7 +45,7 @@ public class StringToMultiValueStringOperatorConversion implements SqlOperatorCo ) ) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StrposOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StrposOperatorConversion.java index 336c9d3b3723..e18c0896a5d4 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StrposOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/StrposOperatorConversion.java @@ -38,7 +38,7 @@ public class StrposOperatorConversion implements SqlOperatorConversion .operatorBuilder("STRPOS") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER) .functionCategory(SqlFunctionCategory.STRING) - .returnType(SqlTypeName.INTEGER) + .returnTypeNonNull(SqlTypeName.INTEGER) .build(); @Override diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TextcatOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TextcatOperatorConversion.java index 09652ee08ff6..ee160d6b3ef6 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TextcatOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TextcatOperatorConversion.java @@ -36,7 +36,7 @@ public class TextcatOperatorConversion implements SqlOperatorConversion .operatorBuilder("textcat") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER) .requiredOperands(2) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .functionCategory(SqlFunctionCategory.STRING) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeCeilOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeCeilOperatorConversion.java index 8b2ee0c7e78b..81b2dfa12ae2 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeCeilOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeCeilOperatorConversion.java @@ -41,7 +41,7 @@ public class TimeCeilOperatorConversion implements SqlOperatorConversion .operatorBuilder("TIME_CEIL") .operandTypes(SqlTypeFamily.TIMESTAMP, SqlTypeFamily.CHARACTER, SqlTypeFamily.TIMESTAMP, SqlTypeFamily.CHARACTER) .requiredOperands(2) - .returnType(SqlTypeName.TIMESTAMP) + .returnTypeNonNull(SqlTypeName.TIMESTAMP) .functionCategory(SqlFunctionCategory.TIMEDATE) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeExtractOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeExtractOperatorConversion.java index ea041cfee2a9..35accd1f9b3f 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeExtractOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeExtractOperatorConversion.java @@ -44,7 +44,7 @@ public class TimeExtractOperatorConversion implements SqlOperatorConversion .operatorBuilder("TIME_EXTRACT") .operandTypes(SqlTypeFamily.TIMESTAMP, SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER) .requiredOperands(2) - .returnType(SqlTypeName.BIGINT) + .returnTypeNonNull(SqlTypeName.BIGINT) .functionCategory(SqlFunctionCategory.TIMEDATE) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeFloorOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeFloorOperatorConversion.java index af2716e39c63..87c07f25b7e5 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeFloorOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeFloorOperatorConversion.java @@ -56,7 +56,7 @@ public class TimeFloorOperatorConversion implements SqlOperatorConversion .operatorBuilder("TIME_FLOOR") .operandTypes(SqlTypeFamily.TIMESTAMP, SqlTypeFamily.CHARACTER, SqlTypeFamily.TIMESTAMP, SqlTypeFamily.CHARACTER) .requiredOperands(2) - .returnType(SqlTypeName.TIMESTAMP) + .returnTypeNonNull(SqlTypeName.TIMESTAMP) .functionCategory(SqlFunctionCategory.TIMEDATE) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeFormatOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeFormatOperatorConversion.java index fa988a38a976..1f7b6f95d32e 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeFormatOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeFormatOperatorConversion.java @@ -47,7 +47,7 @@ public class TimeFormatOperatorConversion implements SqlOperatorConversion .operatorBuilder("TIME_FORMAT") .operandTypes(SqlTypeFamily.TIMESTAMP, SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER) .requiredOperands(1) - .returnType(SqlTypeName.VARCHAR) + .returnTypeNonNull(SqlTypeName.VARCHAR) .functionCategory(SqlFunctionCategory.TIMEDATE) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeParseOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeParseOperatorConversion.java index 8bba7321ac46..c79d981af2df 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeParseOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeParseOperatorConversion.java @@ -45,7 +45,7 @@ public class TimeParseOperatorConversion implements SqlOperatorConversion .operatorBuilder("TIME_PARSE") .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER) .requiredOperands(1) - .nullableReturnType(SqlTypeName.TIMESTAMP) + .returnTypeNullable(SqlTypeName.TIMESTAMP) .functionCategory(SqlFunctionCategory.TIMEDATE) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeShiftOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeShiftOperatorConversion.java index 4ca033a29141..25b05c40f1d1 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeShiftOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimeShiftOperatorConversion.java @@ -45,7 +45,7 @@ public class TimeShiftOperatorConversion implements SqlOperatorConversion .operatorBuilder("TIME_SHIFT") .operandTypes(SqlTypeFamily.TIMESTAMP, SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER, SqlTypeFamily.CHARACTER) .requiredOperands(3) - .returnType(SqlTypeName.TIMESTAMP) + .returnTypeNonNull(SqlTypeName.TIMESTAMP) .functionCategory(SqlFunctionCategory.TIMEDATE) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimestampToMillisOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimestampToMillisOperatorConversion.java index ec1742d03c8d..ae4565579fb7 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimestampToMillisOperatorConversion.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/TimestampToMillisOperatorConversion.java @@ -39,7 +39,7 @@ public class TimestampToMillisOperatorConversion implements SqlOperatorConversio private static final SqlFunction SQL_FUNCTION = OperatorConversions .operatorBuilder("TIMESTAMP_TO_MILLIS") .operandTypes(SqlTypeFamily.TIMESTAMP) - .returnType(SqlTypeName.BIGINT) + .returnTypeNonNull(SqlTypeName.BIGINT) .functionCategory(SqlFunctionCategory.TIMEDATE) .build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java index 0fd811a0d28b..3f7699fa9f18 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java @@ -83,6 +83,7 @@ import org.apache.druid.sql.calcite.expression.builtin.RPadOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.RTrimOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.RegexpExtractOperatorConversion; +import org.apache.druid.sql.calcite.expression.builtin.RegexpLikeOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.ReinterpretOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.RepeatOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.ReverseOperatorConversion; @@ -162,6 +163,7 @@ public class DruidOperatorTable implements SqlOperatorTable .add(new LTrimOperatorConversion()) .add(new PositionOperatorConversion()) .add(new RegexpExtractOperatorConversion()) + .add(new RegexpLikeOperatorConversion()) .add(new RTrimOperatorConversion()) .add(new ParseLongOperatorConversion()) .add(new StringFormatOperatorConversion()) diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index a26d790ef160..213952bc27f6 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -82,6 +82,7 @@ import org.apache.druid.query.filter.InDimFilter; import org.apache.druid.query.filter.LikeDimFilter; import org.apache.druid.query.filter.NotDimFilter; +import org.apache.druid.query.filter.RegexDimFilter; import org.apache.druid.query.filter.SelectorDimFilter; import org.apache.druid.query.groupby.GroupByQuery; import org.apache.druid.query.groupby.orderby.DefaultLimitSpec; @@ -7331,6 +7332,74 @@ public void testRegexpExtract() throws Exception ); } + @Test + public void testRegexpExtractFilterViaNotNullCheck() throws Exception + { + // Cannot vectorize due to extractionFn in dimension spec. + cannotVectorize(); + + testQuery( + "SELECT COUNT(*)\n" + + "FROM foo\n" + + "WHERE REGEXP_EXTRACT(dim1, '^1') IS NOT NULL OR REGEXP_EXTRACT('Z' || dim1, '^Z2') IS NOT NULL", + ImmutableList.of( + Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .granularity(Granularities.ALL) + .virtualColumns( + expressionVirtualColumn("v0", "regexp_extract(concat('Z',\"dim1\"),'^Z2')", ValueType.STRING) + ) + .filters( + or( + not(selector("dim1", null, new RegexDimExtractionFn("^1", 0, true, null))), + not(selector("v0", null, null)) + ) + ) + .aggregators(new CountAggregatorFactory("a0")) + .context(TIMESERIES_CONTEXT_DEFAULT) + .build() + ), + ImmutableList.of( + new Object[]{3L} + ) + ); + } + + @Test + public void testRegexpLikeFilter() throws Exception + { + // Cannot vectorize due to usage of regex filter. + cannotVectorize(); + + testQuery( + "SELECT COUNT(*)\n" + + "FROM foo\n" + + "WHERE REGEXP_LIKE(dim1, '^1') OR REGEXP_LIKE('Z' || dim1, '^Z2')", + ImmutableList.of( + Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .granularity(Granularities.ALL) + .virtualColumns( + expressionVirtualColumn("v0", "concat('Z',\"dim1\")", ValueType.STRING) + ) + .filters( + or( + new RegexDimFilter("dim1", "^1", null), + new RegexDimFilter("v0", "^Z2", null) + ) + ) + .aggregators(new CountAggregatorFactory("a0")) + .context(TIMESERIES_CONTEXT_DEFAULT) + .build() + ), + ImmutableList.of( + new Object[]{3L} + ) + ); + } + @Test public void testGroupBySortPushDown() throws Exception { @@ -14474,6 +14543,42 @@ public void testRepeatedIdenticalVirtualExpressionGrouping() throws Exception ); } + @Test + public void testValidationErrorNullLiteralIllegal() throws Exception + { + expectedException.expectMessage("Illegal use of 'NULL'"); + + testQuery( + "SELECT REGEXP_LIKE('x', NULL)", + ImmutableList.of(), + ImmutableList.of() + ); + } + + @Test + public void testValidationErrorNonLiteralIllegal() throws Exception + { + expectedException.expectMessage("Argument to function 'REGEXP_LIKE' must be a literal"); + + testQuery( + "SELECT REGEXP_LIKE('x', dim1) FROM foo", + ImmutableList.of(), + ImmutableList.of() + ); + } + + @Test + public void testValidationErrorWrongTypeLiteral() throws Exception + { + expectedException.expectMessage("Cannot apply 'REGEXP_LIKE' to arguments"); + + testQuery( + "SELECT REGEXP_LIKE('x', 1) FROM foo", + ImmutableList.of(), + ImmutableList.of() + ); + } + /** * This is a provider of query contexts that should be used by join tests. * It tests various configs that can be passed to join queries. All the configs provided by this provider should diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionTestHelper.java b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionTestHelper.java index 3136508ee9b7..1d842175ef46 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionTestHelper.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionTestHelper.java @@ -30,12 +30,19 @@ import org.apache.calcite.sql.SqlIntervalQualifier; import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.druid.data.input.MapBasedRow; import org.apache.druid.math.expr.ExprEval; import org.apache.druid.math.expr.Parser; +import org.apache.druid.query.filter.DimFilter; +import org.apache.druid.query.filter.ValueMatcher; +import org.apache.druid.segment.RowAdapters; +import org.apache.druid.segment.RowBasedColumnSelectorFactory; +import org.apache.druid.segment.VirtualColumn; import org.apache.druid.segment.column.RowSignature; import org.apache.druid.sql.calcite.planner.Calcites; import org.apache.druid.sql.calcite.planner.PlannerConfig; import org.apache.druid.sql.calcite.planner.PlannerContext; +import org.apache.druid.sql.calcite.rel.VirtualColumnRegistry; import org.apache.druid.sql.calcite.table.RowSignatures; import org.apache.druid.sql.calcite.util.CalciteTests; import org.joda.time.DateTime; @@ -46,8 +53,10 @@ import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; class ExpressionTestHelper @@ -197,11 +206,11 @@ private static String quoteIfNeeded(@Nullable Object arg) } void testExpression( - SqlTypeName sqlTypeName, - SqlOperator op, - List exprs, - DruidExpression expectedExpression, - Object expectedResult + final SqlTypeName sqlTypeName, + final SqlOperator op, + final List exprs, + final DruidExpression expectedExpression, + final Object expectedResult ) { RelDataType returnType = createSqlType(sqlTypeName); @@ -209,36 +218,79 @@ void testExpression( } void testExpression( - SqlOperator op, - RexNode expr, - DruidExpression expectedExpression, - Object expectedResult + final SqlOperator op, + final RexNode expr, + final DruidExpression expectedExpression, + final Object expectedResult ) { testExpression(op, Collections.singletonList(expr), expectedExpression, expectedResult); } void testExpression( - SqlOperator op, - List exprs, - DruidExpression expectedExpression, - Object expectedResult + final SqlOperator op, + final List exprs, + final DruidExpression expectedExpression, + final Object expectedResult ) { testExpression(rexBuilder.makeCall(op, exprs), expectedExpression, expectedResult); } void testExpression( - RexNode rexNode, - DruidExpression expectedExpression, - Object expectedResult + final RexNode rexNode, + final DruidExpression expectedExpression, + final Object expectedResult ) { DruidExpression expression = Expressions.toDruidExpression(PLANNER_CONTEXT, rowSignature, rexNode); Assert.assertEquals("Expression for: " + rexNode, expectedExpression, expression); - ExprEval result = Parser.parse(expression.getExpression(), PLANNER_CONTEXT.getExprMacroTable()) - .eval(Parser.withMap(bindings)); + ExprEval result = Parser.parse(expression.getExpression(), PLANNER_CONTEXT.getExprMacroTable()) + .eval(Parser.withMap(bindings)); + Assert.assertEquals("Result for: " + rexNode, expectedResult, result.value()); } + + void testFilter( + final SqlOperator op, + final List exprs, + final List expectedVirtualColumns, + final DimFilter expectedFilter, + final boolean expectedResult + ) + { + final RexNode rexNode = rexBuilder.makeCall(op, exprs); + final VirtualColumnRegistry virtualColumnRegistry = VirtualColumnRegistry.create(rowSignature); + + final DimFilter filter = Expressions.toFilter(PLANNER_CONTEXT, rowSignature, virtualColumnRegistry, rexNode); + Assert.assertEquals("Filter for: " + rexNode, expectedFilter, filter); + + final List virtualColumns = + filter.getRequiredColumns() + .stream() + .map(virtualColumnRegistry::getVirtualColumn) + .filter(Objects::nonNull) + .sorted(Comparator.comparing(VirtualColumn::getOutputName)) + .collect(Collectors.toList()); + + Assert.assertEquals( + "Virtual columns for: " + rexNode, + expectedVirtualColumns.stream() + .sorted(Comparator.comparing(VirtualColumn::getOutputName)) + .collect(Collectors.toList()), + virtualColumns + ); + + final ValueMatcher matcher = expectedFilter.toFilter().makeMatcher( + RowBasedColumnSelectorFactory.create( + RowAdapters.standardRow(), + () -> new MapBasedRow(0L, bindings), + rowSignature, + false + ) + ); + + Assert.assertEquals("Result for: " + rexNode, expectedResult, matcher.matches()); + } } diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java index d2370d793e9e..25034521f99d 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java @@ -32,15 +32,19 @@ import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.IAE; +import org.apache.druid.query.expression.TestExprMacroTable; import org.apache.druid.query.extraction.RegexDimExtractionFn; +import org.apache.druid.query.filter.RegexDimFilter; import org.apache.druid.segment.column.RowSignature; import org.apache.druid.segment.column.ValueType; +import org.apache.druid.segment.virtual.ExpressionVirtualColumn; import org.apache.druid.sql.calcite.expression.builtin.DateTruncOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.LPadOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.LeftOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.ParseLongOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.RPadOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.RegexpExtractOperatorConversion; +import org.apache.druid.sql.calcite.expression.builtin.RegexpLikeOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.RepeatOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.ReverseOperatorConversion; import org.apache.druid.sql.calcite.expression.builtin.RightOperatorConversion; @@ -59,6 +63,7 @@ import org.junit.Test; import java.math.BigDecimal; +import java.util.Collections; import java.util.Map; public class ExpressionsTest extends ExpressionTestBase @@ -75,6 +80,7 @@ public class ExpressionsTest extends ExpressionTestBase .add("hexstr", ValueType.STRING) .add("intstr", ValueType.STRING) .add("spacey", ValueType.STRING) + .add("newliney", ValueType.STRING) .add("tstr", ValueType.STRING) .add("dstr", ValueType.STRING) .build(); @@ -90,6 +96,7 @@ public class ExpressionsTest extends ExpressionTestBase .put("hexstr", "EF") .put("intstr", "-100") .put("spacey", " hey there ") + .put("newliney", "beep\nboop") .put("tstr", "2000-02-03 04:05:06") .put("dstr", "2000-02-03") .build(); @@ -131,6 +138,50 @@ public void testCharacterLength() @Test public void testRegexpExtract() { + testHelper.testExpression( + new RegexpExtractOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("x(.)"), + testHelper.makeLiteral(1) + ), + DruidExpression.of( + SimpleExtraction.of("s", new RegexDimExtractionFn("x(.)", 1, true, null)), + "regexp_extract(\"s\",'x(.)',1)" + ), + null + ); + + testHelper.testExpression( + new RegexpExtractOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("(o)"), + testHelper.makeLiteral(1) + ), + DruidExpression.of( + SimpleExtraction.of("s", new RegexDimExtractionFn("(o)", 1, true, null)), + "regexp_extract(\"s\",'(o)',1)" + ), + + // Column "s" contains an 'o', but not at the beginning; we do match this. + "o" + ); + + testHelper.testExpression( + new RegexpExtractOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeCall( + SqlStdOperatorTable.CONCAT, + testHelper.makeLiteral("Z"), + testHelper.makeInputRef("s") + ), + testHelper.makeLiteral("Zf(.)") + ), + DruidExpression.fromExpression("regexp_extract(concat('Z',\"s\"),'Zf(.)')"), + "Zfo" + ); + testHelper.testExpression( new RegexpExtractOperatorConversion().calciteOperator(), ImmutableList.of( @@ -157,6 +208,307 @@ public void testRegexpExtract() ), "fo" ); + + testHelper.testExpression( + new RegexpExtractOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("") + ), + DruidExpression.of( + SimpleExtraction.of("s", new RegexDimExtractionFn("", 0, true, null)), + "regexp_extract(\"s\",'')" + ), + NullHandling.emptyToNullIfNeeded("") + ); + + testHelper.testExpression( + new RegexpExtractOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("") + ), + DruidExpression.of( + SimpleExtraction.of("s", new RegexDimExtractionFn("", 0, true, null)), + "regexp_extract(\"s\",'')" + ), + NullHandling.emptyToNullIfNeeded("") + ); + + testHelper.testExpression( + new RegexpExtractOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeNullLiteral(SqlTypeName.VARCHAR), + testHelper.makeLiteral("(.)") + ), + DruidExpression.fromExpression("regexp_extract(null,'(.)')"), + null + ); + + testHelper.testExpression( + new RegexpExtractOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeNullLiteral(SqlTypeName.VARCHAR), + testHelper.makeLiteral("") + ), + DruidExpression.fromExpression("regexp_extract(null,'')"), + null + ); + + testHelper.testExpression( + new RegexpExtractOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeNullLiteral(SqlTypeName.VARCHAR), + testHelper.makeLiteral("null") + ), + DruidExpression.fromExpression("regexp_extract(null,'null')"), + null + ); + } + + @Test + public void testRegexpLike() + { + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("f.") + ), + DruidExpression.fromExpression("regexp_like(\"s\",'f.')"), + 1L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("o") + ), + DruidExpression.fromExpression("regexp_like(\"s\",'o')"), + + // Column "s" contains an 'o', but not at the beginning; we do match this. + 1L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("x.") + ), + DruidExpression.fromExpression("regexp_like(\"s\",'x.')"), + 0L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("") + ), + DruidExpression.fromExpression("regexp_like(\"s\",'')"), + 1L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeLiteral("beep\nboop"), + testHelper.makeLiteral("^beep$") + ), + DruidExpression.fromExpression("regexp_like('beep\\u000Aboop','^beep$')"), + 0L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeLiteral("beep\nboop"), + testHelper.makeLiteral("^beep\\nboop$") + ), + DruidExpression.fromExpression("regexp_like('beep\\u000Aboop','^beep\\u005Cnboop$')"), + 1L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("newliney"), + testHelper.makeLiteral("^beep$") + ), + DruidExpression.fromExpression("regexp_like(\"newliney\",'^beep$')"), + 0L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("newliney"), + testHelper.makeLiteral("^beep\\nboop$") + ), + DruidExpression.fromExpression("regexp_like(\"newliney\",'^beep\\u005Cnboop$')"), + 1L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("newliney"), + testHelper.makeLiteral("boo") + ), + DruidExpression.fromExpression("regexp_like(\"newliney\",'boo')"), + 1L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("newliney"), + testHelper.makeLiteral("^boo") + ), + DruidExpression.fromExpression("regexp_like(\"newliney\",'^boo')"), + 0L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeCall( + SqlStdOperatorTable.CONCAT, + testHelper.makeLiteral("Z"), + testHelper.makeInputRef("s") + ), + testHelper.makeLiteral("x(.)") + ), + DruidExpression.fromExpression("regexp_like(concat('Z',\"s\"),'x(.)')"), + 0L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeNullLiteral(SqlTypeName.VARCHAR), + testHelper.makeLiteral("(.)") + ), + DruidExpression.fromExpression("regexp_like(null,'(.)')"), + 0L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeNullLiteral(SqlTypeName.VARCHAR), + testHelper.makeLiteral("") + ), + DruidExpression.fromExpression("regexp_like(null,'')"), + + // In SQL-compatible mode, nulls don't match anything. Otherwise, they match like empty strings. + NullHandling.sqlCompatible() ? 0L : 1L + ); + + testHelper.testExpression( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeNullLiteral(SqlTypeName.VARCHAR), + testHelper.makeLiteral("null") + ), + DruidExpression.fromExpression("regexp_like(null,'null')"), + 0L + ); + } + + @Test + public void testRegexpLikeAsFilter() + { + testHelper.testFilter( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("f.") + ), + Collections.emptyList(), + new RegexDimFilter("s", "f.", null), + true + ); + + testHelper.testFilter( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("o") + ), + Collections.emptyList(), + // Column "s" contains an 'o', but not at the beginning, so we don't match + new RegexDimFilter("s", "o", null), + true + ); + + testHelper.testFilter( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("x.") + ), + Collections.emptyList(), + new RegexDimFilter("s", "x.", null), + false + ); + + testHelper.testFilter( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("s"), + testHelper.makeLiteral("") + ), + Collections.emptyList(), + new RegexDimFilter("s", "", null), + true + ); + + testHelper.testFilter( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("newliney"), + testHelper.makeLiteral("^beep$") + ), + Collections.emptyList(), + new RegexDimFilter("newliney", "^beep$", null), + false + ); + + testHelper.testFilter( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeInputRef("newliney"), + testHelper.makeLiteral("^beep\\nboop$") + ), + Collections.emptyList(), + new RegexDimFilter("newliney", "^beep\\nboop$", null), + true + ); + + testHelper.testFilter( + new RegexpLikeOperatorConversion().calciteOperator(), + ImmutableList.of( + testHelper.makeCall( + SqlStdOperatorTable.CONCAT, + testHelper.makeLiteral("Z"), + testHelper.makeInputRef("s") + ), + testHelper.makeLiteral("x(.)") + ), + ImmutableList.of( + new ExpressionVirtualColumn( + "v0", + "concat('Z',\"s\")", + ValueType.STRING, + TestExprMacroTable.INSTANCE + ) + ), + new RegexDimFilter("v0", "x(.)", null), + false + ); } @Test diff --git a/website/.spelling b/website/.spelling index 91c962f16606..8d41dc98a478 100644 --- a/website/.spelling +++ b/website/.spelling @@ -1073,6 +1073,7 @@ nextafter nvl parse_long regexp_extract +regexp_like result1 result2 rint From cdf593965e6afdda44dcfc01437d9ad467664677 Mon Sep 17 00:00:00 2001 From: Jihoon Son Date: Wed, 3 Jun 2020 15:40:28 -0700 Subject: [PATCH 056/107] Fix shutdown reason for unknown tasks in taskQueue (#9954) * Fix shutdown reason for unknown tasks in taskQueue * unused imports --- .../druid/indexing/overlord/TaskQueue.java | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/TaskQueue.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/TaskQueue.java index 16ad0864106f..6c7dd4e3bd5c 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/TaskQueue.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/TaskQueue.java @@ -19,12 +19,9 @@ package org.apache.druid.indexing.overlord; -import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; @@ -284,30 +281,19 @@ private void manage() throws InterruptedException } } // Kill tasks that shouldn't be running - final Set tasksToKill = Sets.difference( - runnerTaskFutures.keySet(), - ImmutableSet.copyOf( - Lists.transform( - tasks, - new Function() - { - @Override - public String apply(Task task) - { - return task.getId(); - } - } - ) - ) - ); + final Set knownTaskIds = tasks + .stream() + .map(Task::getId) + .collect(Collectors.toSet()); + final Set tasksToKill = Sets.difference(runnerTaskFutures.keySet(), knownTaskIds); if (!tasksToKill.isEmpty()) { log.info("Asking taskRunner to clean up %,d tasks.", tasksToKill.size()); for (final String taskId : tasksToKill) { try { taskRunner.shutdown( taskId, - "task is not in runnerTaskFutures[%s]", - runnerTaskFutures.keySet() + "task is not in knownTaskIds[%s]", + knownTaskIds ); } catch (Exception e) { From fd6e037831789d9d52c687c13afd9e0706e986a3 Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn <52679095+maytasm@users.noreply.github.com> Date: Wed, 3 Jun 2020 13:46:28 -1000 Subject: [PATCH 057/107] Fix Subquery could not be converted to groupBy query (#9959) * Fix join * Fix Subquery could not be converted to groupBy query * Fix Subquery could not be converted to groupBy query * Fix Subquery could not be converted to groupBy query * Fix Subquery could not be converted to groupBy query * Fix Subquery could not be converted to groupBy query * Fix Subquery could not be converted to groupBy query * Fix Subquery could not be converted to groupBy query * Fix Subquery could not be converted to groupBy query * add tests * address comments * fix failing tests --- extensions-core/datasketches/pom.xml | 5 + .../hll/sql/HllSketchSqlAggregatorTest.java | 71 +-- .../sql/ThetaSketchSqlAggregatorTest.java | 74 ++- .../query/timeseries/TimeseriesQuery.java | 10 + .../TimeseriesQueryQueryToolChest.java | 38 +- .../GroupByTimeseriesQueryRunnerTest.java | 14 + .../TimeseriesQueryQueryToolChestTest.java | 30 +- .../timeseries/TimeseriesQueryRunnerTest.java | 243 ++++++++ .../sql/calcite/rel/DruidOuterQueryRel.java | 10 +- .../druid/sql/calcite/rel/DruidQuery.java | 11 +- .../druid/sql/calcite/rel/QueryMaker.java | 5 +- .../sql/calcite/BaseCalciteQueryTest.java | 11 + .../druid/sql/calcite/CalciteQueryTest.java | 526 ++++++++++++++---- 13 files changed, 839 insertions(+), 209 deletions(-) diff --git a/extensions-core/datasketches/pom.xml b/extensions-core/datasketches/pom.xml index 48aae4cef47f..4b969818c290 100644 --- a/extensions-core/datasketches/pom.xml +++ b/extensions-core/datasketches/pom.xml @@ -162,6 +162,11 @@ junit test + + joda-time + joda-time + test + org.easymock easymock diff --git a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregatorTest.java b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregatorTest.java index 5876726218dc..6f4c8b6a9aaa 100644 --- a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregatorTest.java +++ b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/hll/sql/HllSketchSqlAggregatorTest.java @@ -28,6 +28,7 @@ import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.java.util.common.granularity.Granularities; +import org.apache.druid.java.util.common.granularity.PeriodGranularity; import org.apache.druid.java.util.common.io.Closer; import org.apache.druid.query.Druids; import org.apache.druid.query.Query; @@ -47,7 +48,6 @@ import org.apache.druid.query.aggregation.post.ExpressionPostAggregator; import org.apache.druid.query.aggregation.post.FieldAccessPostAggregator; import org.apache.druid.query.aggregation.post.FinalizingFieldAccessPostAggregator; -import org.apache.druid.query.dimension.DefaultDimensionSpec; import org.apache.druid.query.expression.TestExprMacroTable; import org.apache.druid.query.groupby.GroupByQuery; import org.apache.druid.query.spec.MultipleIntervalSegmentSpec; @@ -75,6 +75,8 @@ import org.apache.druid.sql.calcite.util.SpecificSegmentsQuerySegmentWalker; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.partition.LinearShardSpec; +import org.joda.time.DateTimeZone; +import org.joda.time.Period; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; @@ -355,46 +357,33 @@ public void testAvgDailyCountDistinctHllSketch() throws Exception Query expected = GroupByQuery.builder() .setDataSource( new QueryDataSource( - GroupByQuery.builder() - .setDataSource(CalciteTests.DATASOURCE1) - .setInterval(new MultipleIntervalSegmentSpec(ImmutableList.of( - Filtration.eternity()))) - .setGranularity(Granularities.ALL) - .setVirtualColumns( - new ExpressionVirtualColumn( - "v0", - "timestamp_floor(\"__time\",'P1D',null,'UTC')", - ValueType.LONG, - TestExprMacroTable.INSTANCE - ) - ) - .setDimensions( - Collections.singletonList( - new DefaultDimensionSpec( - "v0", - "d0", - ValueType.LONG - ) - ) - ) - .setAggregatorSpecs( - Collections.singletonList( - new HllSketchBuildAggregatorFactory( - "a0:a", - "cnt", - null, - null, - ROUND - ) - ) - ) - .setPostAggregatorSpecs( - ImmutableList.of( - new FinalizingFieldAccessPostAggregator("a0", "a0:a") - ) - ) - .setContext(QUERY_CONTEXT_DEFAULT) - .build() + Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(new MultipleIntervalSegmentSpec(ImmutableList.of( + Filtration.eternity() + ))) + .granularity(new PeriodGranularity(Period.days(1), null, DateTimeZone.UTC)) + .aggregators( + Collections.singletonList( + new HllSketchBuildAggregatorFactory( + "a0:a", + "cnt", + null, + null, + ROUND + ) + ) + ) + .postAggregators( + ImmutableList.of( + new FinalizingFieldAccessPostAggregator("a0", "a0:a") + ) + ) + .context(BaseCalciteQueryTest.getTimeseriesContextWithFloorTime( + ImmutableMap.of("skipEmptyBuckets", true, "sqlQueryId", "dummy"), + "d0" + )) + .build() ) ) .setInterval(new MultipleIntervalSegmentSpec(ImmutableList.of(Filtration.eternity()))) diff --git a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregatorTest.java b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregatorTest.java index b41ab60c3409..201380a1b206 100644 --- a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregatorTest.java +++ b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/sql/ThetaSketchSqlAggregatorTest.java @@ -28,6 +28,7 @@ import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.java.util.common.granularity.Granularities; +import org.apache.druid.java.util.common.granularity.PeriodGranularity; import org.apache.druid.java.util.common.io.Closer; import org.apache.druid.query.Druids; import org.apache.druid.query.Query; @@ -44,7 +45,6 @@ import org.apache.druid.query.aggregation.post.ArithmeticPostAggregator; import org.apache.druid.query.aggregation.post.FieldAccessPostAggregator; import org.apache.druid.query.aggregation.post.FinalizingFieldAccessPostAggregator; -import org.apache.druid.query.dimension.DefaultDimensionSpec; import org.apache.druid.query.expression.TestExprMacroTable; import org.apache.druid.query.groupby.GroupByQuery; import org.apache.druid.query.spec.MultipleIntervalSegmentSpec; @@ -72,6 +72,8 @@ import org.apache.druid.sql.calcite.util.SpecificSegmentsQuerySegmentWalker; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.partition.LinearShardSpec; +import org.joda.time.DateTimeZone; +import org.joda.time.Period; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; @@ -351,48 +353,34 @@ public void testAvgDailyCountDistinctThetaSketch() throws Exception Query expected = GroupByQuery.builder() .setDataSource( - new QueryDataSource( - GroupByQuery.builder() - .setDataSource(CalciteTests.DATASOURCE1) - .setInterval(new MultipleIntervalSegmentSpec(ImmutableList.of( - Filtration.eternity()))) - .setGranularity(Granularities.ALL) - .setVirtualColumns( - new ExpressionVirtualColumn( - "v0", - "timestamp_floor(\"__time\",'P1D',null,'UTC')", - ValueType.LONG, - TestExprMacroTable.INSTANCE - ) - ) - .setDimensions( - Collections.singletonList( - new DefaultDimensionSpec( - "v0", - "d0", - ValueType.LONG - ) - ) - ) - .setAggregatorSpecs( - Collections.singletonList( - new SketchMergeAggregatorFactory( - "a0:a", - "cnt", - null, - null, - null, - null - ) - ) - ) - .setPostAggregatorSpecs( - ImmutableList.of( - new FinalizingFieldAccessPostAggregator("a0", "a0:a") - ) - ) - .setContext(QUERY_CONTEXT_DEFAULT) - .build() + new QueryDataSource(Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(new MultipleIntervalSegmentSpec(ImmutableList.of( + Filtration.eternity() + ))) + .granularity(new PeriodGranularity(Period.days(1), null, DateTimeZone.UTC)) + .aggregators( + Collections.singletonList( + new SketchMergeAggregatorFactory( + "a0:a", + "cnt", + null, + null, + null, + null + ) + ) + ) + .postAggregators( + ImmutableList.of( + new FinalizingFieldAccessPostAggregator("a0", "a0:a") + ) + ) + .context(BaseCalciteQueryTest.getTimeseriesContextWithFloorTime( + ImmutableMap.of("skipEmptyBuckets", true, "sqlQueryId", "dummy"), + "d0" + )) + .build() ) ) .setInterval(new MultipleIntervalSegmentSpec(ImmutableList.of(Filtration.eternity()))) diff --git a/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQuery.java b/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQuery.java index 729d839d0298..47ab8a8c670a 100644 --- a/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQuery.java +++ b/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQuery.java @@ -50,6 +50,11 @@ public class TimeseriesQuery extends BaseQuery> { public static final String CTX_GRAND_TOTAL = "grandTotal"; public static final String SKIP_EMPTY_BUCKETS = "skipEmptyBuckets"; + // "timestampResultField" is an undocumented parameter used internally by the SQL layer. + // It is necessary because when the SQL layer generates a Timeseries query for a group-by-time-floor SQL query, + // it expects the result of the time-floor to have a specific name. That name is provided using this parameter. + // TODO: We can remove this once https://github.com/apache/druid/issues/9974 is done. + public static final String CTX_TIMESTAMP_RESULT_FIELD = "timestampResultField"; private final VirtualColumns virtualColumns; private final DimFilter dimFilter; @@ -139,6 +144,11 @@ public boolean isGrandTotal() return getContextBoolean(CTX_GRAND_TOTAL, false); } + public String getTimestampResultField() + { + return getContextValue(CTX_TIMESTAMP_RESULT_FIELD); + } + public boolean isSkipEmptyBuckets() { return getContextBoolean(SKIP_EMPTY_BUCKETS, false); diff --git a/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQueryQueryToolChest.java b/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQueryQueryToolChest.java index e381ffe86814..2ce7e3b34632 100644 --- a/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQueryQueryToolChest.java +++ b/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQueryQueryToolChest.java @@ -28,6 +28,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.inject.Inject; +import org.apache.commons.lang.StringUtils; import org.apache.druid.data.input.MapBasedRow; import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.granularity.Granularities; @@ -51,6 +52,7 @@ import org.apache.druid.segment.RowAdapters; import org.apache.druid.segment.RowBasedColumnSelectorFactory; import org.apache.druid.segment.column.RowSignature; +import org.apache.druid.segment.column.ValueType; import org.joda.time.DateTime; import java.util.Collections; @@ -286,6 +288,7 @@ public byte[] computeResultLevelCacheKey(TimeseriesQuery query) .appendCacheable(query.getVirtualColumns()) .appendCacheables(query.getPostAggregatorSpecs()) .appendInt(query.getLimit()) + .appendString(query.getTimestampResultField()) .appendBoolean(query.isGrandTotal()); return builder.build(); } @@ -404,11 +407,15 @@ public Function, Result> ma @Override public RowSignature resultArraySignature(TimeseriesQuery query) { - return RowSignature.builder() - .addTimeColumn() - .addAggregators(query.getAggregatorSpecs()) - .addPostAggregators(query.getPostAggregatorSpecs()) - .build(); + + RowSignature.Builder rowSignatureBuilder = RowSignature.builder(); + rowSignatureBuilder.addTimeColumn(); + if (StringUtils.isNotEmpty(query.getTimestampResultField())) { + rowSignatureBuilder.add(query.getTimestampResultField(), ValueType.LONG); + } + rowSignatureBuilder.addAggregators(query.getAggregatorSpecs()); + rowSignatureBuilder.addPostAggregators(query.getPostAggregatorSpecs()); + return rowSignatureBuilder.build(); } @Override @@ -447,13 +454,22 @@ private Function, Result> m return result -> { final TimeseriesResultValue holder = result.getValue(); final Map values = new HashMap<>(holder.getBaseObject()); - if (calculatePostAggs && !query.getPostAggregatorSpecs().isEmpty()) { - // put non finalized aggregators for calculating dependent post Aggregators - for (AggregatorFactory agg : query.getAggregatorSpecs()) { - values.put(agg.getName(), holder.getMetric(agg.getName())); + if (calculatePostAggs) { + if (!query.getPostAggregatorSpecs().isEmpty()) { + // put non finalized aggregators for calculating dependent post Aggregators + for (AggregatorFactory agg : query.getAggregatorSpecs()) { + values.put(agg.getName(), holder.getMetric(agg.getName())); + } + for (PostAggregator postAgg : query.getPostAggregatorSpecs()) { + values.put(postAgg.getName(), postAgg.compute(values)); + } } - for (PostAggregator postAgg : query.getPostAggregatorSpecs()) { - values.put(postAgg.getName(), postAgg.compute(values)); + // If "timestampResultField" is set, we must include a copy of the timestamp in the result. + // This is used by the SQL layer when it generates a Timeseries query for a group-by-time-floor SQL query. + // The SQL layer expects the result of the time-floor to have a specific name that is not going to be "__time". + if (StringUtils.isNotEmpty(query.getTimestampResultField()) && result.getTimestamp() != null) { + final DateTime timestamp = result.getTimestamp(); + values.put(query.getTimestampResultField(), timestamp.getMillis()); } } for (AggregatorFactory agg : query.getAggregatorSpecs()) { diff --git a/processing/src/test/java/org/apache/druid/query/groupby/GroupByTimeseriesQueryRunnerTest.java b/processing/src/test/java/org/apache/druid/query/groupby/GroupByTimeseriesQueryRunnerTest.java index 8ddeb57f6237..9e7b45a58bac 100644 --- a/processing/src/test/java/org/apache/druid/query/groupby/GroupByTimeseriesQueryRunnerTest.java +++ b/processing/src/test/java/org/apache/druid/query/groupby/GroupByTimeseriesQueryRunnerTest.java @@ -235,4 +235,18 @@ public void testTimeseriesWithFilterOnNonExistentDimension() // Skip this test because the timeseries test expects a day that doesn't have a filter match to be filled in, // but group by just doesn't return a value if the filter doesn't match. } + + @Override + public void testTimeseriesWithTimestampResultFieldContextForArrayResponse() + { + // Skip this test because the timeseries test expects an extra column to be created (map from the timestamp_floor + // of the timestamp dimension) but group by doesn't do this. + } + + @Override + public void testTimeseriesWithTimestampResultFieldContextForMapResponse() + { + // Skip this test because the timeseries test expects an extra column to be created (map from the timestamp_floor + // of the timestamp dimension) but group by doesn't do this. + } } diff --git a/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryQueryToolChestTest.java b/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryQueryToolChestTest.java index 3f6e59b7f493..b7bc426f8f9b 100644 --- a/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryQueryToolChestTest.java +++ b/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryQueryToolChestTest.java @@ -56,6 +56,7 @@ @RunWith(Parameterized.class) public class TimeseriesQueryQueryToolChestTest { + private static final String TIMESTAMP_RESULT_FIELD_NAME = "d0"; private static final TimeseriesQueryQueryToolChest TOOL_CHEST = new TimeseriesQueryQueryToolChest(null); @BeforeClass @@ -364,7 +365,7 @@ public void testResultLevelCacheKeyWithGrandTotal() } @Test - public void testResultArraySignature() + public void testResultArraySignatureWithoutTimestampResultField() { final TimeseriesQuery query = Druids.newTimeseriesQueryBuilder() @@ -388,6 +389,33 @@ public void testResultArraySignature() ); } + @Test + public void testResultArraySignatureWithTimestampResultField() + { + final TimeseriesQuery query = + Druids.newTimeseriesQueryBuilder() + .dataSource("dummy") + .intervals("2000/3000") + .descending(descending) + .granularity(Granularities.HOUR) + .aggregators(QueryRunnerTestHelper.COMMON_DOUBLE_AGGREGATORS) + .postAggregators(QueryRunnerTestHelper.CONSTANT) + .context(ImmutableMap.of(TimeseriesQuery.CTX_TIMESTAMP_RESULT_FIELD, TIMESTAMP_RESULT_FIELD_NAME)) + .build(); + + Assert.assertEquals( + RowSignature.builder() + .addTimeColumn() + .add(TIMESTAMP_RESULT_FIELD_NAME, ValueType.LONG) + .add("rows", ValueType.LONG) + .add("index", ValueType.DOUBLE) + .add("uniques", null) + .add("const", null) + .build(), + TOOL_CHEST.resultArraySignature(query) + ); + } + @Test public void testResultsAsArrays() { diff --git a/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java b/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java index 4c0154b120c3..5d5af015b5c8 100644 --- a/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java +++ b/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java @@ -31,6 +31,7 @@ import org.apache.druid.java.util.common.granularity.Granularities; import org.apache.druid.java.util.common.granularity.Granularity; import org.apache.druid.java.util.common.granularity.PeriodGranularity; +import org.apache.druid.java.util.common.guava.Sequence; import org.apache.druid.query.Druids; import org.apache.druid.query.FinalizeResultsQueryRunner; import org.apache.druid.query.QueryPlus; @@ -59,6 +60,7 @@ import org.apache.druid.query.ordering.StringComparators; import org.apache.druid.query.spec.MultipleIntervalSegmentSpec; import org.apache.druid.segment.TestHelper; +import org.apache.druid.segment.column.RowSignature; import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; import org.apache.druid.testing.InitializedNullHandlingTest; @@ -87,6 +89,7 @@ @RunWith(Parameterized.class) public class TimeseriesQueryRunnerTest extends InitializedNullHandlingTest { + private static final String TIMESTAMP_RESULT_FIELD_NAME = "d0"; @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -2471,6 +2474,246 @@ public void testTimeseriesWithBoundFilter1() TestHelper.assertExpectedResults(expectedResults, results); } + @Test + public void testTimeseriesWithTimestampResultFieldContextForArrayResponse() + { + Granularity gran = Granularities.DAY; + TimeseriesQuery query = Druids.newTimeseriesQueryBuilder() + .dataSource(QueryRunnerTestHelper.DATA_SOURCE) + .granularity(gran) + .intervals(QueryRunnerTestHelper.FULL_ON_INTERVAL_SPEC) + .aggregators( + QueryRunnerTestHelper.ROWS_COUNT, + QueryRunnerTestHelper.INDEX_DOUBLE_SUM, + QueryRunnerTestHelper.QUALITY_UNIQUES + ) + .postAggregators(QueryRunnerTestHelper.ADD_ROWS_INDEX_CONSTANT) + .descending(descending) + .context(ImmutableMap.of( + TimeseriesQuery.CTX_TIMESTAMP_RESULT_FIELD, TIMESTAMP_RESULT_FIELD_NAME + )) + .build(); + + Assert.assertEquals(TIMESTAMP_RESULT_FIELD_NAME, query.getTimestampResultField()); + + QueryToolChest, TimeseriesQuery> toolChest = new TimeseriesQueryQueryToolChest(); + + RowSignature rowSignature = toolChest.resultArraySignature(query); + Assert.assertNotNull(rowSignature); + List columnNames = rowSignature.getColumnNames(); + Assert.assertNotNull(columnNames); + Assert.assertEquals(6, columnNames.size()); + Assert.assertEquals("__time", columnNames.get(0)); + Assert.assertEquals(TIMESTAMP_RESULT_FIELD_NAME, columnNames.get(1)); + Assert.assertEquals("rows", columnNames.get(2)); + Assert.assertEquals("index", columnNames.get(3)); + Assert.assertEquals("uniques", columnNames.get(4)); + Assert.assertEquals("addRowsIndexConstant", columnNames.get(5)); + + Sequence> results = runner.run(QueryPlus.wrap(query)); + Sequence resultsAsArrays = toolChest.resultsAsArrays(query, results); + + Assert.assertNotNull(resultsAsArrays); + + final String[] expectedIndex = descending ? + QueryRunnerTestHelper.EXPECTED_FULL_ON_INDEX_VALUES_DESC : + QueryRunnerTestHelper.EXPECTED_FULL_ON_INDEX_VALUES; + + final Long expectedLast = descending ? + QueryRunnerTestHelper.EARLIEST.getMillis() : + QueryRunnerTestHelper.LAST.getMillis(); + + int count = 0; + Object[] lastResult = null; + for (Object[] result : resultsAsArrays.toList()) { + Long current = (Long) result[0]; + Assert.assertFalse( + StringUtils.format("Timestamp[%s] > expectedLast[%s]", current, expectedLast), + descending ? current < expectedLast : current > expectedLast + ); + + Assert.assertEquals( + (Long) result[1], + current, + 0 + ); + + Assert.assertEquals( + QueryRunnerTestHelper.SKIPPED_DAY.getMillis() == current ? (Long) 0L : (Long) 13L, + result[2] + ); + + if (QueryRunnerTestHelper.SKIPPED_DAY.getMillis() != current) { + Assert.assertEquals( + Doubles.tryParse(expectedIndex[count]).doubleValue(), + (Double) result[3], + (Double) result[3] * 1e-6 + ); + Assert.assertEquals( + (Double) result[4], + 9.0d, + 0.02 + ); + Assert.assertEquals( + new Double(expectedIndex[count]) + 13L + 1L, + (Double) result[5], + (Double) result[5] * 1e-6 + ); + } else { + if (NullHandling.replaceWithDefault()) { + Assert.assertEquals( + 0.0D, + (Double) result[3], + (Double) result[3] * 1e-6 + ); + Assert.assertEquals( + 0.0D, + NullHandling.sqlCompatible() ? (Double) result[4] : (Integer) result[4], + 0.02 + ); + Assert.assertEquals( + new Double(expectedIndex[count]) + 1L, + (Double) result[5], + (Double) result[5] * 1e-6 + ); + } else { + Assert.assertNull( + result[3] + ); + Assert.assertEquals( + (Integer) result[4], + 0.0, + 0.02 + ); + Assert.assertNull( + result[5] + ); + } + } + + lastResult = result; + ++count; + } + Assert.assertEquals(expectedLast, lastResult[0]); + } + + @Test + public void testTimeseriesWithTimestampResultFieldContextForMapResponse() + { + Granularity gran = Granularities.DAY; + TimeseriesQuery query = Druids.newTimeseriesQueryBuilder() + .dataSource(QueryRunnerTestHelper.DATA_SOURCE) + .granularity(gran) + .intervals(QueryRunnerTestHelper.FULL_ON_INTERVAL_SPEC) + .aggregators( + QueryRunnerTestHelper.ROWS_COUNT, + QueryRunnerTestHelper.INDEX_DOUBLE_SUM, + QueryRunnerTestHelper.QUALITY_UNIQUES + ) + .postAggregators(QueryRunnerTestHelper.ADD_ROWS_INDEX_CONSTANT) + .descending(descending) + .context(ImmutableMap.of( + TimeseriesQuery.CTX_TIMESTAMP_RESULT_FIELD, TIMESTAMP_RESULT_FIELD_NAME + )) + .build(); + + Assert.assertEquals(TIMESTAMP_RESULT_FIELD_NAME, query.getTimestampResultField()); + + Iterable> results = runner.run(QueryPlus.wrap(query)).toList(); + + final String[] expectedIndex = descending ? + QueryRunnerTestHelper.EXPECTED_FULL_ON_INDEX_VALUES_DESC : + QueryRunnerTestHelper.EXPECTED_FULL_ON_INDEX_VALUES; + + final DateTime expectedLast = descending ? + QueryRunnerTestHelper.EARLIEST : + QueryRunnerTestHelper.LAST; + + int count = 0; + Result lastResult = null; + for (Result result : results) { + DateTime current = result.getTimestamp(); + Assert.assertFalse( + StringUtils.format("Timestamp[%s] > expectedLast[%s]", current, expectedLast), + descending ? current.isBefore(expectedLast) : current.isAfter(expectedLast) + ); + + final TimeseriesResultValue value = result.getValue(); + + Assert.assertEquals( + value.getLongMetric(TIMESTAMP_RESULT_FIELD_NAME), + current.getMillis(), + 0 + ); + + Assert.assertEquals( + result.toString(), + QueryRunnerTestHelper.SKIPPED_DAY.equals(current) ? 0L : 13L, + value.getLongMetric("rows").longValue() + ); + + if (!QueryRunnerTestHelper.SKIPPED_DAY.equals(current)) { + Assert.assertEquals( + result.toString(), + Doubles.tryParse(expectedIndex[count]).doubleValue(), + value.getDoubleMetric("index").doubleValue(), + value.getDoubleMetric("index").doubleValue() * 1e-6 + ); + Assert.assertEquals( + result.toString(), + new Double(expectedIndex[count]) + + 13L + 1L, + value.getDoubleMetric("addRowsIndexConstant"), + value.getDoubleMetric("addRowsIndexConstant") * 1e-6 + ); + Assert.assertEquals( + value.getDoubleMetric("uniques"), + 9.0d, + 0.02 + ); + } else { + if (NullHandling.replaceWithDefault()) { + Assert.assertEquals( + result.toString(), + 0.0D, + value.getDoubleMetric("index").doubleValue(), + value.getDoubleMetric("index").doubleValue() * 1e-6 + ); + Assert.assertEquals( + result.toString(), + new Double(expectedIndex[count]) + 1L, + value.getDoubleMetric("addRowsIndexConstant"), + value.getDoubleMetric("addRowsIndexConstant") * 1e-6 + ); + Assert.assertEquals( + 0.0D, + value.getDoubleMetric("uniques"), + 0.02 + ); + } else { + Assert.assertNull( + result.toString(), + value.getDoubleMetric("index") + ); + Assert.assertNull( + result.toString(), + value.getDoubleMetric("addRowsIndexConstant") + ); + Assert.assertEquals( + value.getDoubleMetric("uniques"), + 0.0d, + 0.02 + ); + } + } + + lastResult = result; + ++count; + } + + Assert.assertEquals(lastResult.toString(), expectedLast, lastResult.getTimestamp()); + } + @Test public void testTimeSeriesWithSelectionFilterLookupExtractionFn() { diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidOuterQueryRel.java b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidOuterQueryRel.java index f63c618253bc..32e4b746d7ee 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidOuterQueryRel.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidOuterQueryRel.java @@ -36,7 +36,6 @@ import org.apache.druid.java.util.common.guava.Sequences; import org.apache.druid.query.QueryDataSource; import org.apache.druid.query.TableDataSource; -import org.apache.druid.query.groupby.GroupByQuery; import org.apache.druid.segment.column.RowSignature; import org.apache.druid.sql.calcite.table.RowSignatures; @@ -117,17 +116,10 @@ public DruidOuterQueryRel withPartialQuery(final PartialDruidQuery newQueryBuild public DruidQuery toDruidQuery(final boolean finalizeAggregations) { // Must finalize aggregations on subqueries. - final DruidQuery subQuery = ((DruidRel) sourceRel).toDruidQuery(true); - - final GroupByQuery groupByQuery = subQuery.toGroupByQuery(); - if (groupByQuery == null) { - throw new CannotBuildQueryException("Subquery could not be converted to GroupBy query"); - } - final RowSignature sourceRowSignature = subQuery.getOutputRowSignature(); return partialQuery.build( - new QueryDataSource(groupByQuery), + new QueryDataSource(subQuery.getQuery()), sourceRowSignature, getPlannerContext(), getCluster().getRexBuilder(), diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java index 262ba664def1..b25bfb823307 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java @@ -734,6 +734,9 @@ public TimeseriesQuery toTimeseriesQuery() final Granularity queryGranularity; final boolean descending; int timeseriesLimit = 0; + final Map theContext = new HashMap<>(); + theContext.put("skipEmptyBuckets", true); + theContext.putAll(plannerContext.getQueryContext()); if (grouping.getDimensions().isEmpty()) { queryGranularity = Granularities.ALL; descending = false; @@ -748,7 +751,10 @@ public TimeseriesQuery toTimeseriesQuery() // Timeseries only applies if the single dimension is granular __time. return null; } - + theContext.put( + TimeseriesQuery.CTX_TIMESTAMP_RESULT_FIELD, + Iterables.getOnlyElement(grouping.getDimensions()).toDimensionSpec().getOutputName() + ); if (sorting != null) { // If there is sorting, set timeseriesLimit to given value if less than Integer.Max_VALUE if (sorting.isLimited()) { @@ -782,9 +788,6 @@ public TimeseriesQuery toTimeseriesQuery() if (sorting != null && sorting.getProjection() != null) { postAggregators.addAll(sorting.getProjection().getPostAggregators()); } - final Map theContext = new HashMap<>(); - theContext.put("skipEmptyBuckets", true); - theContext.putAll(plannerContext.getQueryContext()); return new TimeseriesQuery( dataSource, diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rel/QueryMaker.java b/sql/src/main/java/org/apache/druid/sql/calcite/rel/QueryMaker.java index d73312e98ac8..c0e73163c021 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/rel/QueryMaker.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/rel/QueryMaker.java @@ -100,9 +100,8 @@ public Sequence runQuery(final DruidQuery druidQuery) if (query instanceof TimeseriesQuery && !druidQuery.getGrouping().getDimensions().isEmpty()) { // Hack for timeseries queries: when generating them, DruidQuery.toTimeseriesQuery translates a dimension // based on a timestamp_floor expression into a 'granularity'. This is not reflected in the druidQuery's - // output row signature, so we have to account for it here. When groupBy on timestamp_floor expressions is - // just as fast as a timeseries query (a noble goal) we can remove timeseries queries from the SQL layer and - // also remove this hack. + // output row signature, so we have to account for it here. + // TODO: We can remove this once https://github.com/apache/druid/issues/9974 is done. final String timeDimension = Iterables.getOnlyElement(druidQuery.getGrouping().getDimensions()).getOutputName(); rowOrder = druidQuery.getOutputRowSignature().getColumnNames().stream() .map(f -> timeDimension.equals(f) ? ColumnHolder.TIME_COLUMN_NAME : f) diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java index a12775078bfd..952b3cfe2f71 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java @@ -58,6 +58,7 @@ import org.apache.druid.query.scan.ScanQuery; import org.apache.druid.query.spec.MultipleIntervalSegmentSpec; import org.apache.druid.query.spec.QuerySegmentSpec; +import org.apache.druid.query.timeseries.TimeseriesQuery; import org.apache.druid.segment.column.ColumnHolder; import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.join.JoinType; @@ -203,6 +204,16 @@ public DateTimeZone getSqlTimeZone() QueryContexts.MAX_SCATTER_GATHER_BYTES_KEY, Long.MAX_VALUE ); + // Add additional context to the given context map for when the + // timeseries query has timestamp_floor expression on the timestamp dimension + public static Map getTimeseriesContextWithFloorTime(Map context, + String timestampResultField) + { + return ImmutableMap.builder().putAll(context) + .put(TimeseriesQuery.CTX_TIMESTAMP_RESULT_FIELD, timestampResultField) + .build(); + } + // Matches QUERY_CONTEXT_LOS_ANGELES public static final Map TIMESERIES_CONTEXT_LOS_ANGELES = new HashMap<>(); diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index 213952bc27f6..8068a0d9e38b 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -161,6 +161,344 @@ public void testSelectConstantExpressionFromTable() throws Exception ); } + @Test + public void testJoinOuterGroupByAndSubqueryHasLimit() throws Exception + { + // Cannot vectorize JOIN operator. + cannotVectorize(); + + testQuery( + "SELECT dim2, AVG(m2) FROM (SELECT * FROM foo AS t1 INNER JOIN foo AS t2 ON t1.m1 = t2.m1 LIMIT 10) AS t3 GROUP BY dim2", + ImmutableList.of( + GroupByQuery.builder() + .setDataSource( + newScanQueryBuilder() + .dataSource( + join( + new TableDataSource(CalciteTests.DATASOURCE1), + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .columns(ImmutableList.of("m1")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(QUERY_CONTEXT_DEFAULT) + .build() + ), + "j0.", + equalsCondition( + DruidExpression.fromColumn("m1"), + DruidExpression.fromColumn("j0.m1") + ), + JoinType.INNER + ) + ) + .intervals(querySegmentSpec(Filtration.eternity())) + .limit(10) + .columns("__time", "cnt", "dim1", "dim2", "dim3", "j0.m1", "m1", "m2", "unique_dim1") + .context(QUERY_CONTEXT_DEFAULT) + .build() + ) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setDimensions(new DefaultDimensionSpec("dim2", "d0", ValueType.STRING)) + .setGranularity(Granularities.ALL) + .setAggregatorSpecs(aggregators( + new DoubleSumAggregatorFactory("a0:sum", "m2"), + new CountAggregatorFactory("a0:count") + ) + ) + .setPostAggregatorSpecs( + ImmutableList.of( + new ArithmeticPostAggregator( + "a0", + "quotient", + ImmutableList.of( + new FieldAccessPostAggregator(null, "a0:sum"), + new FieldAccessPostAggregator(null, "a0:count") + ) + ) + + ) + ) + .setContext(QUERY_CONTEXT_DEFAULT) + .build() + ), + NullHandling.sqlCompatible() + ? ImmutableList.of( + new Object[]{null, 4.0}, + new Object[]{"", 3.0}, + new Object[]{"a", 2.5}, + new Object[]{"abc", 5.0} + ) + : ImmutableList.of( + new Object[]{"", 3.6666666666666665}, + new Object[]{"a", 2.5}, + new Object[]{"abc", 5.0} + ) + ); + } + + @Test + public void testJoinOuterGroupByAndSubqueryNoLimit() throws Exception + { + // Cannot vectorize JOIN operator. + cannotVectorize(); + + testQuery( + "SELECT dim2, AVG(m2) FROM (SELECT * FROM foo AS t1 INNER JOIN foo AS t2 ON t1.m1 = t2.m1) AS t3 GROUP BY dim2", + ImmutableList.of( + GroupByQuery.builder() + .setDataSource( + join( + new TableDataSource(CalciteTests.DATASOURCE1), + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .columns(ImmutableList.of("m1")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(QUERY_CONTEXT_DEFAULT) + .build() + ), + "j0.", + equalsCondition( + DruidExpression.fromColumn("m1"), + DruidExpression.fromColumn("j0.m1") + ), + JoinType.INNER + ) + ) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setDimensions(new DefaultDimensionSpec("dim2", "d0", ValueType.STRING)) + .setGranularity(Granularities.ALL) + .setAggregatorSpecs(aggregators( + new DoubleSumAggregatorFactory("a0:sum", "m2"), + new CountAggregatorFactory("a0:count") + ) + ) + .setPostAggregatorSpecs( + ImmutableList.of( + new ArithmeticPostAggregator( + "a0", + "quotient", + ImmutableList.of( + new FieldAccessPostAggregator(null, "a0:sum"), + new FieldAccessPostAggregator(null, "a0:count") + ) + ) + + ) + ) + .setContext(QUERY_CONTEXT_DEFAULT) + .build() + ), + NullHandling.sqlCompatible() + ? ImmutableList.of( + new Object[]{null, 4.0}, + new Object[]{"", 3.0}, + new Object[]{"a", 2.5}, + new Object[]{"abc", 5.0} + ) + : ImmutableList.of( + new Object[]{"", 3.6666666666666665}, + new Object[]{"a", 2.5}, + new Object[]{"abc", 5.0} + ) + ); + } + + @Test + public void testJoinWithLimitBeforeJoining() throws Exception + { + // Cannot vectorize JOIN operator. + cannotVectorize(); + + testQuery( + "SELECT t1.dim2, AVG(t1.m2) FROM (SELECT * FROM foo LIMIT 10) AS t1 INNER JOIN foo AS t2 ON t1.m1 = t2.m1 GROUP BY t1.dim2", + ImmutableList.of( + GroupByQuery.builder() + .setDataSource( + join( + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .columns("dim2", "m1", "m2") + .context(QUERY_CONTEXT_DEFAULT) + .limit(10) + .build() + ), + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .columns(ImmutableList.of("m1")) + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(QUERY_CONTEXT_DEFAULT) + .build() + ), + "j0.", + equalsCondition( + DruidExpression.fromColumn("m1"), + DruidExpression.fromColumn("j0.m1") + ), + JoinType.INNER + ) + ) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setDimensions(new DefaultDimensionSpec("dim2", "d0", ValueType.STRING)) + .setGranularity(Granularities.ALL) + .setAggregatorSpecs(aggregators( + new DoubleSumAggregatorFactory("a0:sum", "m2"), + new CountAggregatorFactory("a0:count") + ) + ) + .setPostAggregatorSpecs( + ImmutableList.of( + new ArithmeticPostAggregator( + "a0", + "quotient", + ImmutableList.of( + new FieldAccessPostAggregator(null, "a0:sum"), + new FieldAccessPostAggregator(null, "a0:count") + ) + ) + + ) + ) + .setContext(QUERY_CONTEXT_DEFAULT) + .build() + ), + NullHandling.sqlCompatible() + ? ImmutableList.of( + new Object[]{null, 4.0}, + new Object[]{"", 3.0}, + new Object[]{"a", 2.5}, + new Object[]{"abc", 5.0} + ) + : ImmutableList.of( + new Object[]{"", 3.6666666666666665}, + new Object[]{"a", 2.5}, + new Object[]{"abc", 5.0} + ) + ); + } + + @Test + public void testJoinOnTimeseriesWithFloorOnTime() throws Exception + { + // Cannot vectorize JOIN operator. + cannotVectorize(); + + testQuery( + "SELECT CAST(__time AS BIGINT), m1, ANY_VALUE(dim3, 100) FROM foo WHERE (TIME_FLOOR(__time, 'PT1H'), m1) IN\n" + + " (\n" + + " SELECT TIME_FLOOR(__time, 'PT1H') AS t1, MIN(m1) AS t2 FROM foo WHERE dim3 = 'b'\n" + + " AND __time BETWEEN '1994-04-29 00:00:00' AND '2020-01-11 00:00:00' GROUP BY 1\n" + + " )\n" + + "GROUP BY 1, 2\n", + ImmutableList.of( + GroupByQuery.builder() + .setDataSource( + join( + new TableDataSource(CalciteTests.DATASOURCE1), + new QueryDataSource( + Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Intervals.of("1994-04-29/2020-01-11T00:00:00.001Z"))) + .filters(selector("dim3", "b", null)) + .granularity(new PeriodGranularity(Period.hours(1), null, DateTimeZone.UTC)) + .aggregators(aggregators( + new FloatMinAggregatorFactory("a0", "m1") + )) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) + .build()), + "j0.", + "((timestamp_floor(\"__time\",'PT1H',null,'UTC') == \"j0.d0\") && (\"m1\" == \"j0.a0\"))", + JoinType.INNER + ) + ) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setDimensions( + new DefaultDimensionSpec("__time", "d0", ValueType.LONG), + new DefaultDimensionSpec("m1", "d1", ValueType.FLOAT) + + ) + .setGranularity(Granularities.ALL) + .setAggregatorSpecs(aggregators( + new StringAnyAggregatorFactory("a0", "dim3", 100) + )) + .setContext(QUERY_CONTEXT_DEFAULT) + .build() + ), + ImmutableList.of( + new Object[]{946684800000L, 1.0f, "[a, b]"}, + new Object[]{946771200000L, 2.0f, "[b, c]"} + ) + ); + } + + @Test + public void testJoinOnGroupByInsteadOfTimeseriesWithFloorOnTime() throws Exception + { + // Cannot vectorize JOIN operator. + cannotVectorize(); + + testQuery( + "SELECT CAST(__time AS BIGINT), m1, ANY_VALUE(dim3, 100) FROM foo WHERE (CAST(TIME_FLOOR(__time, 'PT1H') AS BIGINT), m1) IN\n" + + " (\n" + + " SELECT CAST(TIME_FLOOR(__time, 'PT1H') AS BIGINT) + 0 AS t1, MIN(m1) AS t2 FROM foo WHERE dim3 = 'b'\n" + + " AND __time BETWEEN '1994-04-29 00:00:00' AND '2020-01-11 00:00:00' GROUP BY 1\n" + + " )\n" + + "GROUP BY 1, 2\n", + ImmutableList.of( + GroupByQuery.builder() + .setDataSource( + join( + new TableDataSource(CalciteTests.DATASOURCE1), + new QueryDataSource( + GroupByQuery.builder() + .setDataSource(CalciteTests.DATASOURCE1) + .setInterval(querySegmentSpec(Intervals.of("1994-04-29/2020-01-11T00:00:00.001Z"))) + .setVirtualColumns( + expressionVirtualColumn( + "v0", + "(timestamp_floor(\"__time\",'PT1H',null,'UTC') + 0)", + ValueType.LONG + ) + ) + .setDimFilter(selector("dim3", "b", null)) + .setGranularity(Granularities.ALL) + .setDimensions(dimensions(new DefaultDimensionSpec("v0", "d0", ValueType.LONG))) + .setAggregatorSpecs(aggregators( + new FloatMinAggregatorFactory("a0", "m1") + )) + .setContext(QUERY_CONTEXT_DEFAULT) + .build()), + "j0.", + "((timestamp_floor(\"__time\",'PT1H',null,'UTC') == \"j0.d0\") && (\"m1\" == \"j0.a0\"))", + JoinType.INNER + ) + ) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setDimensions( + new DefaultDimensionSpec("__time", "d0", ValueType.LONG), + new DefaultDimensionSpec("m1", "d1", ValueType.FLOAT) + + ) + .setGranularity(Granularities.ALL) + .setAggregatorSpecs(aggregators( + new StringAnyAggregatorFactory("a0", "dim3", 100) + )) + .setContext(QUERY_CONTEXT_DEFAULT) + .build() + ), + ImmutableList.of( + new Object[]{946684800000L, 1.0f, "[a, b]"}, + new Object[]{946771200000L, 2.0f, "[b, c]"} + ) + ); + } @Test public void testSelectCountStart() throws Exception @@ -224,7 +562,7 @@ public void testSelectCountStart() throws Exception .aggregators(aggregators( new CountAggregatorFactory("a0") )) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build()), ImmutableList.of() ); @@ -2407,7 +2745,7 @@ public void testGroupByExpressionAliasedAsOriginalColumnName() throws Exception .intervals(querySegmentSpec(Filtration.eternity())) .granularity(Granularities.MONTH) .aggregators(aggregators(new CountAggregatorFactory("a0"))) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -6404,25 +6742,13 @@ public void testMinMaxAvgDailyCountWithLimit() throws Exception GroupByQuery.builder() .setDataSource( new QueryDataSource( - GroupByQuery.builder() - .setDataSource(CalciteTests.DATASOURCE1) - .setInterval(querySegmentSpec(Filtration.eternity())) - .setGranularity(Granularities.ALL) - .setVirtualColumns( - expressionVirtualColumn( - "v0", - "timestamp_floor(\"__time\",'P1D',null,'UTC')", - ValueType.LONG - ) - ) - .setDimensions(dimensions(new DefaultDimensionSpec( - "v0", - "d0", - ValueType.LONG - ))) - .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) - .setContext(QUERY_CONTEXT_DEFAULT) - .build() + Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .granularity(new PeriodGranularity(Period.days(1), null, DateTimeZone.UTC)) + .intervals(querySegmentSpec(Filtration.eternity())) + .aggregators(new CountAggregatorFactory("a0")) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) + .build() ) ) .setInterval(querySegmentSpec(Filtration.eternity())) @@ -6470,44 +6796,30 @@ public void testAvgDailyCountDistinct() throws Exception GroupByQuery.builder() .setDataSource( new QueryDataSource( - GroupByQuery.builder() - .setDataSource(CalciteTests.DATASOURCE1) - .setInterval(querySegmentSpec(Filtration.eternity())) - .setGranularity(Granularities.ALL) - .setVirtualColumns( - expressionVirtualColumn( - "v0", - "timestamp_floor(\"__time\",'P1D',null,'UTC')", - ValueType.LONG - ) - ) - .setDimensions(dimensions(new DefaultDimensionSpec( - "v0", - "d0", - ValueType.LONG - ))) - .setAggregatorSpecs( - aggregators( - new CardinalityAggregatorFactory( - "a0:a", - null, - dimensions(new DefaultDimensionSpec( - "cnt", - "cnt", - ValueType.LONG - )), - false, - true - ) - ) - ) - .setPostAggregatorSpecs( - ImmutableList.of( - new HyperUniqueFinalizingPostAggregator("a0", "a0:a") - ) - ) - .setContext(QUERY_CONTEXT_DEFAULT) - .build() + Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .granularity(new PeriodGranularity(Period.days(1), null, DateTimeZone.UTC)) + .aggregators( + new CardinalityAggregatorFactory( + "a0:a", + null, + dimensions(new DefaultDimensionSpec( + "cnt", + "cnt", + ValueType.LONG + )), + false, + true + ) + ) + .postAggregators( + ImmutableList.of( + new HyperUniqueFinalizingPostAggregator("a0", "a0:a") + ) + ) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) + .build() ) ) .setInterval(querySegmentSpec(Filtration.eternity())) @@ -6935,6 +7247,9 @@ public void testExactCountDistinctUsingSubqueryWithWherePushDown() throws Except @Test public void testExactCountDistinctUsingSubqueryWithWhereToOuterFilter() throws Exception { + // Cannot vectorize topN operator. + cannotVectorize(); + testQuery( "SELECT\n" + " SUM(cnt),\n" @@ -6945,15 +7260,16 @@ public void testExactCountDistinctUsingSubqueryWithWhereToOuterFilter() throws E GroupByQuery.builder() .setDataSource( new QueryDataSource( - GroupByQuery.builder() - .setDataSource(CalciteTests.DATASOURCE1) - .setInterval(querySegmentSpec(Filtration.eternity())) - .setGranularity(Granularities.ALL) - .setDimensions(dimensions(new DefaultDimensionSpec("dim2", "d0"))) - .setAggregatorSpecs(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .setLimit(1) - .setContext(QUERY_CONTEXT_DEFAULT) - .build() + new TopNQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .granularity(Granularities.ALL) + .dimension(new DefaultDimensionSpec("dim2", "d0")) + .aggregators(new LongSumAggregatorFactory("a0", "cnt")) + .metric(new DimensionTopNMetricSpec(null, StringComparators.LEXICOGRAPHIC)) + .threshold(1) + .context(QUERY_CONTEXT_DEFAULT) + .build() ) ) .setDimFilter(bound("a0", "0", null, true, false, null, StringComparators.NUMERIC)) @@ -7577,7 +7893,7 @@ public void testGroupAndFilterOnTimeFloorWithTimeZone() throws Exception .intervals(querySegmentSpec(Intervals.of("2000-01-01T00-08:00/2000-03-01T00-08:00"))) .granularity(new PeriodGranularity(Period.months(1), null, DateTimes.inferTzFromString(LOS_ANGELES))) .aggregators(aggregators(new CountAggregatorFactory("a0"))) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -9988,7 +10304,7 @@ public void testTimeseries() throws Exception .intervals(querySegmentSpec(Filtration.eternity())) .granularity(Granularities.MONTH) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -10084,7 +10400,7 @@ public void testTimeseriesLosAngelesViaQueryContext() throws Exception .intervals(querySegmentSpec(Filtration.eternity())) .granularity(new PeriodGranularity(Period.months(1), null, DateTimes.inferTzFromString(LOS_ANGELES))) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .context(TIMESERIES_CONTEXT_LOS_ANGELES) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_LOS_ANGELES, "d0")) .build() ), ImmutableList.of( @@ -10118,7 +10434,7 @@ public void testTimeseriesLosAngelesViaPlannerConfig() throws Exception .intervals(querySegmentSpec(Intervals.of("1999-12-01T00-08:00/2002-01-01T00-08:00"))) .granularity(new PeriodGranularity(Period.months(1), null, DateTimes.inferTzFromString(LOS_ANGELES))) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -10146,7 +10462,7 @@ public void testTimeseriesUsingTimeFloor() throws Exception .intervals(querySegmentSpec(Filtration.eternity())) .granularity(Granularities.MONTH) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -10280,7 +10596,7 @@ public void testTimeseriesUsingTimeFloorWithOrigin() throws Exception ) ) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -10308,7 +10624,7 @@ public void testTimeseriesLosAngelesUsingTimeFloorConnectionUtc() throws Excepti .intervals(querySegmentSpec(Filtration.eternity())) .granularity(new PeriodGranularity(Period.months(1), null, DateTimes.inferTzFromString(LOS_ANGELES))) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -10339,7 +10655,7 @@ public void testTimeseriesLosAngelesUsingTimeFloorConnectionLosAngeles() throws .intervals(querySegmentSpec(Filtration.eternity())) .granularity(new PeriodGranularity(Period.months(1), null, DateTimes.inferTzFromString(LOS_ANGELES))) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .context(TIMESERIES_CONTEXT_LOS_ANGELES) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_LOS_ANGELES, "d0")) .build() ), ImmutableList.of( @@ -10372,7 +10688,7 @@ public void testTimeseriesDontSkipEmptyBuckets() throws Exception .intervals(querySegmentSpec(Intervals.of("2000/2000-01-02"))) .granularity(new PeriodGranularity(Period.hours(1), null, DateTimeZone.UTC)) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .context(QUERY_CONTEXT_DONT_SKIP_EMPTY_BUCKETS) + .context(getTimeseriesContextWithFloorTime(QUERY_CONTEXT_DONT_SKIP_EMPTY_BUCKETS, "d0")) .build() ), ImmutableList.builder() @@ -10420,7 +10736,7 @@ public void testTimeseriesUsingCastAsDate() throws Exception .intervals(querySegmentSpec(Filtration.eternity())) .granularity(new PeriodGranularity(Period.days(1), null, DateTimeZone.UTC)) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -10450,7 +10766,7 @@ public void testTimeseriesUsingFloorPlusCastAsDate() throws Exception .intervals(querySegmentSpec(Filtration.eternity())) .granularity(new PeriodGranularity(Period.months(3), null, DateTimeZone.UTC)) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -10480,7 +10796,7 @@ public void testTimeseriesDescending() throws Exception .granularity(Granularities.MONTH) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) .descending(true) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -10683,7 +10999,7 @@ public void testTimeseriesWithLimitNoTopN() throws Exception .granularity(Granularities.MONTH) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) .limit(1) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -10710,7 +11026,7 @@ public void testTimeseriesWithLimit() throws Exception .granularity(Granularities.MONTH) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) .limit(1) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -10738,7 +11054,7 @@ public void testTimeseriesWithOrderByAndLimit() throws Exception .granularity(Granularities.MONTH) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) .limit(1) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -11946,14 +12262,13 @@ public void testNotInAggregationSubquery(Map queryContext) throw GroupByQuery .builder() .setDataSource( - GroupByQuery - .builder() - .setDataSource(CalciteTests.DATASOURCE1) - .setInterval(querySegmentSpec(Filtration.eternity())) - .setGranularity(Granularities.ALL) - .setAggregatorSpecs(new LongMaxAggregatorFactory("a0", "__time")) - .setContext(QUERY_CONTEXT_DEFAULT) - .build() + Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .granularity(Granularities.ALL) + .aggregators(new LongMaxAggregatorFactory("a0", "__time")) + .context(TIMESERIES_CONTEXT_DEFAULT) + .build() ) .setInterval(querySegmentSpec(Filtration.eternity())) .setGranularity(Granularities.ALL) @@ -12144,13 +12459,30 @@ public void testInnerJoinOnMultiValueColumn(Map queryContext) th @Test public void testUsingSubqueryWithLimit() throws Exception { - expectedException.expect(CannotBuildQueryException.class); - expectedException.expectMessage("Subquery could not be converted to GroupBy query"); + // Cannot vectorize scan query. + cannotVectorize(); testQuery( "SELECT COUNT(*) AS cnt FROM ( SELECT * FROM druid.foo LIMIT 10 ) tmpA", - ImmutableList.of(), - ImmutableList.of() + ImmutableList.of( + GroupByQuery.builder() + .setDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .columns("__time", "cnt", "dim1", "dim2", "dim3", "m1", "m2", "unique_dim1") + .limit(10) + .context(QUERY_CONTEXT_DEFAULT) + .build() + ) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setGranularity(Granularities.ALL) + .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) + .build() + ), + ImmutableList.of( + new Object[]{6L} + ) ); } @@ -12450,7 +12782,7 @@ public void testPostAggWithTimeseries() throws Exception expressionPostAgg("p0", "(\"a0\" + \"a1\")") ) .descending(true) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( @@ -12638,7 +12970,7 @@ public void testRequireTimeConditionPositive() throws Exception .intervals(querySegmentSpec(Intervals.of("2000-01-01/2002-01-01"))) .granularity(Granularities.MONTH) .aggregators(aggregators(new LongSumAggregatorFactory("a0", "cnt"))) - .context(TIMESERIES_CONTEXT_DEFAULT) + .context(getTimeseriesContextWithFloorTime(TIMESERIES_CONTEXT_DEFAULT, "d0")) .build() ), ImmutableList.of( From b2f60e28840cb36b0795c54d24232338592f79d1 Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn <52679095+maytasm@users.noreply.github.com> Date: Thu, 4 Jun 2020 13:28:05 -1000 Subject: [PATCH 058/107] Fix groupBy with literal in subquery grouping (#9986) * fix groupBy with literal in subquery grouping * fix groupBy with literal in subquery grouping * fix groupBy with literal in subquery grouping * address comments * update javadocs --- .../apache/druid/query/QueryToolChest.java | 9 ++- .../druid/sql/calcite/rel/DruidQuery.java | 10 ++- .../druid/sql/calcite/CalciteQueryTest.java | 63 +++++++++++++++++++ 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/processing/src/main/java/org/apache/druid/query/QueryToolChest.java b/processing/src/main/java/org/apache/druid/query/QueryToolChest.java index 509738ee6803..ee400f814fb8 100644 --- a/processing/src/main/java/org/apache/druid/query/QueryToolChest.java +++ b/processing/src/main/java/org/apache/druid/query/QueryToolChest.java @@ -179,11 +179,10 @@ public abstract Function makePreComputeManipulatorFn( ); /** - * Generally speaking this is the exact same thing as makePreComputeManipulatorFn. It is leveraged in - * order to compute PostAggregators on results after they have been completely merged together, which - * should actually be done in the mergeResults() call instead of here. - *

- * This should never actually be overridden and it should be removed as quickly as possible. + * Generally speaking this is the exact same thing as makePreComputeManipulatorFn. It is leveraged in order to + * compute PostAggregators on results after they have been completely merged together. To minimize walks of segments, + * it is recommended to use mergeResults() call instead of this method if possible. However, this may not always be + * possible as we don’t always want to run PostAggregators and other stuff that happens there when you mergeResults. * * @param query The Query that is currently being processed * @param fn The function that should be applied to all metrics in the results diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java index b25bfb823307..137f9b70bd38 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidQuery.java @@ -191,7 +191,7 @@ public static DruidQuery fromPartialQuery( computeGrouping( partialQuery, plannerContext, - computeOutputRowSignature(sourceRowSignature, selectProjection, null, null), + computeOutputRowSignature(sourceRowSignature, null, null, null), virtualColumnRegistry, rexBuilder, finalizeAggregations @@ -433,7 +433,13 @@ private static Subtotals computeSubtotals( final Aggregate aggregate = partialQuery.getAggregate(); // dimBitMapping maps from input field position to group set position (dimension number). - final int[] dimBitMapping = new int[rowSignature.size()]; + final int[] dimBitMapping; + if (partialQuery.getSelectProject() != null) { + dimBitMapping = new int[partialQuery.getSelectProject().getRowType().getFieldCount()]; + } else { + dimBitMapping = new int[rowSignature.size()]; + } + int i = 0; for (int dimBit : aggregate.getGroupSet()) { dimBitMapping[dimBit] = i++; diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index 8068a0d9e38b..e3579aad6a94 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -13419,6 +13419,69 @@ public void testNvlColumns() throws Exception ); } + @Test + public void testGroupByWithLiteralInSubqueryGrouping() throws Exception + { + testQuery( + "SELECT \n" + + " t1, t2\n" + + " FROM\n" + + " ( SELECT\n" + + " 'dummy' as t1,\n" + + " CASE\n" + + " WHEN \n" + + " dim4 = 'b'\n" + + " THEN dim4\n" + + " ELSE NULL\n" + + " END AS t2\n" + + " FROM\n" + + " numfoo\n" + + " GROUP BY\n" + + " dim4\n" + + " )\n" + + " GROUP BY\n" + + " t1,t2\n", + ImmutableList.of( + GroupByQuery.builder() + .setDataSource( + GroupByQuery.builder() + .setDataSource(CalciteTests.DATASOURCE3) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setGranularity(Granularities.ALL) + .setDimensions(new DefaultDimensionSpec("dim4", "_d0", ValueType.STRING)) + .setContext(QUERY_CONTEXT_DEFAULT) + .build() + ) + .setVirtualColumns( + expressionVirtualColumn( + "v0", + "\'dummy\'", + ValueType.STRING + ), + expressionVirtualColumn( + "v1", + "case_searched((\"_d0\" == 'b'),\"_d0\",null)", + ValueType.STRING + ) + ) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setDimensions( + dimensions( + new DefaultDimensionSpec("v0", "d0", ValueType.STRING), + new DefaultDimensionSpec("v1", "d1", ValueType.STRING) + ) + ) + .setGranularity(Granularities.ALL) + .setContext(QUERY_CONTEXT_DEFAULT) + .build() + ), + ImmutableList.of( + new Object[]{"dummy", NULL_STRING}, + new Object[]{"dummy", "b"} + ) + ); + } + @Test public void testMultiValueStringWorksLikeStringGroupBy() throws Exception { From a96898de965728c33d73f451bdfdad039bc46a42 Mon Sep 17 00:00:00 2001 From: agricenko Date: Fri, 5 Jun 2020 03:10:56 +0300 Subject: [PATCH 059/107] Integration Tests. Small fixes for CI. (#9988) Co-authored-by: agritsenko --- integration-tests/build_run_cluster.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/integration-tests/build_run_cluster.sh b/integration-tests/build_run_cluster.sh index aba2d6af805d..ced683eef9f1 100755 --- a/integration-tests/build_run_cluster.sh +++ b/integration-tests/build_run_cluster.sh @@ -25,15 +25,15 @@ export SHARED_DIR=${HOME}/shared echo ${DOCKER_IP:=127.0.0.1} > $DOCKERDIR/docker_ip if !($DRUID_INTEGRATION_TEST_SKIP_BUILD_DOCKER); then - sh ./script/copy_resources.sh - sh ./script/docker_build_containers.sh + bash ./script/copy_resources.sh + bash ./script/docker_build_containers.sh fi if !($DRUID_INTEGRATION_TEST_SKIP_RUN_DOCKER); then - sh ./stop_cluster.sh - sh ./script/docker_run_cluster.sh + bash ./stop_cluster.sh + bash ./script/docker_run_cluster.sh fi if ($DRUID_INTEGRATION_TEST_START_HADOOP_DOCKER); then - sh ./script/copy_hadoop_resources.sh + bash ./script/copy_hadoop_resources.sh fi From 8c5ed47447c2bed7eb19d7aa0c4f91fd3ba95476 Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Thu, 4 Jun 2020 23:52:37 -0700 Subject: [PATCH 060/107] ColumnCapabilities.hasMultipleValues refactor (#9731) * transition ColumnCapabilities.hasMultipleValues to Capable enum, remove ColumnCapabilities.isComplete * remove artifical, always multi-value capabilities from IncrementalIndexStorageAdapter and fix up fallout from that, fix ColumnCapabilities merge in index merger * fix typo * remove unused method * review stuffs, revert IncrementalIndexStorageAdapater capabilities change, plumb lame workaround to SegmentAnalyzer * more comment * use volatile booleans * fix line length * correctly handle missing columns for vector processors * return ColumnCapabilities.Capable for BitmapIndexSelector.hasMultipleValues, fix vector processor selection for complex * false on non-existent --- .../druid/benchmark/BoundFilterBenchmark.java | 3 +- .../DimensionPredicateFilterBenchmark.java | 3 +- .../druid/benchmark/LikeFilterBenchmark.java | 3 +- .../query/filter/BitmapIndexSelector.java | 3 +- .../epinephelinae/GroupByQueryEngineV2.java | 8 +- .../druid/query/metadata/SegmentAnalyzer.java | 28 ++++-- .../druid/segment/ColumnProcessors.java | 2 +- .../ColumnSelectorBitmapIndexSelector.java | 9 +- .../druid/segment/DimensionHandlerUtils.java | 18 ++-- .../druid/segment/DimensionIndexer.java | 13 +++ .../druid/segment/DoubleDimensionIndexer.java | 6 ++ .../druid/segment/FloatDimensionIndexer.java | 6 ++ .../apache/druid/segment/IndexMergerV9.java | 16 ++-- .../druid/segment/LongDimensionIndexer.java | 6 ++ .../RowBasedColumnSelectorFactory.java | 9 +- .../druid/segment/StringDimensionIndexer.java | 13 ++- .../segment/StringDimensionMergerV9.java | 4 +- .../druid/segment/column/ColumnBuilder.java | 1 - .../segment/column/ColumnCapabilities.java | 63 +++++++++++--- .../column/ColumnCapabilitiesImpl.java | 87 +++++++++++++------ .../segment/filter/ExpressionFilter.java | 2 +- .../apache/druid/segment/filter/Filters.java | 2 +- .../segment/incremental/IncrementalIndex.java | 66 +++++++------- .../IncrementalIndexStorageAdapter.java | 26 +++--- .../IndexedTableColumnSelectorFactory.java | 3 +- ...yableIndexVectorColumnSelectorFactory.java | 4 +- .../segment/virtual/ExpressionSelectors.java | 11 ++- .../virtual/ExpressionVirtualColumn.java | 8 +- .../GroupByQueryEngineV2Test.java | 18 ++-- .../druid/query/lookup/LookupSegmentTest.java | 6 +- .../metadata/SegmentMetadataQueryTest.java | 24 ++--- .../SegmentMetadataUnionQueryTest.java | 2 +- .../IndexMergerV9WithSpatialIndexTest.java | 3 +- .../QueryableIndexColumnCapabilitiesTest.java | 20 ++--- .../RowBasedColumnSelectorFactoryTest.java | 18 ++-- .../segment/RowBasedStorageAdapterTest.java | 15 ++-- .../column/ColumnCapabilitiesImplTest.java | 4 +- .../filter/ExtractionDimFilterTest.java | 5 +- .../join/table/IndexedTableJoinableTest.java | 6 +- .../virtual/ExpressionVirtualColumnTest.java | 11 ++- .../segment/virtual/VirtualColumnsTest.java | 2 +- 41 files changed, 347 insertions(+), 210 deletions(-) diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/BoundFilterBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/BoundFilterBenchmark.java index 6c0b6d940ed4..f9a920a3d39f 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/BoundFilterBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/BoundFilterBenchmark.java @@ -33,6 +33,7 @@ import org.apache.druid.query.filter.BoundDimFilter; import org.apache.druid.query.ordering.StringComparators; import org.apache.druid.segment.column.BitmapIndex; +import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.data.BitmapSerdeFactory; import org.apache.druid.segment.data.CloseableIndexed; import org.apache.druid.segment.data.GenericIndexed; @@ -195,7 +196,7 @@ public CloseableIndexed getDimensionValues(String dimension) } @Override - public boolean hasMultipleValues(final String dimension) + public ColumnCapabilities.Capable hasMultipleValues(final String dimension) { throw new UnsupportedOperationException(); } diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/DimensionPredicateFilterBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/DimensionPredicateFilterBenchmark.java index 8dd177f1e831..a845bb1a2233 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/DimensionPredicateFilterBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/DimensionPredicateFilterBenchmark.java @@ -35,6 +35,7 @@ import org.apache.druid.query.filter.DruidLongPredicate; import org.apache.druid.query.filter.DruidPredicateFactory; import org.apache.druid.segment.column.BitmapIndex; +import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.data.BitmapSerdeFactory; import org.apache.druid.segment.data.CloseableIndexed; import org.apache.druid.segment.data.GenericIndexed; @@ -166,7 +167,7 @@ public CloseableIndexed getDimensionValues(String dimension) } @Override - public boolean hasMultipleValues(final String dimension) + public ColumnCapabilities.Capable hasMultipleValues(final String dimension) { throw new UnsupportedOperationException(); } diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/LikeFilterBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/LikeFilterBenchmark.java index 83551146eb24..5c4a0f35a60e 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/LikeFilterBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/LikeFilterBenchmark.java @@ -35,6 +35,7 @@ import org.apache.druid.query.filter.SelectorDimFilter; import org.apache.druid.query.ordering.StringComparators; import org.apache.druid.segment.column.BitmapIndex; +import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.data.BitmapSerdeFactory; import org.apache.druid.segment.data.CloseableIndexed; import org.apache.druid.segment.data.GenericIndexed; @@ -166,7 +167,7 @@ public CloseableIndexed getDimensionValues(String dimension) } @Override - public boolean hasMultipleValues(final String dimension) + public ColumnCapabilities.Capable hasMultipleValues(final String dimension) { throw new UnsupportedOperationException(); } diff --git a/processing/src/main/java/org/apache/druid/query/filter/BitmapIndexSelector.java b/processing/src/main/java/org/apache/druid/query/filter/BitmapIndexSelector.java index 90307eb6380a..fd90e7412b68 100644 --- a/processing/src/main/java/org/apache/druid/query/filter/BitmapIndexSelector.java +++ b/processing/src/main/java/org/apache/druid/query/filter/BitmapIndexSelector.java @@ -24,6 +24,7 @@ import org.apache.druid.collections.bitmap.ImmutableBitmap; import org.apache.druid.collections.spatial.ImmutableRTree; import org.apache.druid.segment.column.BitmapIndex; +import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.data.CloseableIndexed; import javax.annotation.Nullable; @@ -35,7 +36,7 @@ public interface BitmapIndexSelector @MustBeClosed @Nullable CloseableIndexed getDimensionValues(String dimension); - boolean hasMultipleValues(String dimension); + ColumnCapabilities.Capable hasMultipleValues(String dimension); int getNumRows(); BitmapFactory getBitmapFactory(); @Nullable diff --git a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java index 9a417c9d6290..f19ae25035ae 100644 --- a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java +++ b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2.java @@ -321,13 +321,13 @@ public static int getCardinalityForArrayAggregation( /** * Checks whether all "dimensions" are either single-valued, or if allowed, nonexistent. Since non-existent column * selectors will show up as full of nulls they are effectively single valued, however they can also be null during - * broker merge, for example with an 'inline' datasource subquery. 'missingMeansNonexistent' is sort of a hack to let + * broker merge, for example with an 'inline' datasource subquery. 'missingMeansNonExistent' is sort of a hack to let * the vectorized engine, which only operates on actual segments, to still work in this case for non-existent columns. */ public static boolean isAllSingleValueDims( final Function capabilitiesFunction, final List dimensions, - final boolean missingMeansNonexistent + final boolean missingMeansNonExistent ) { return dimensions @@ -342,8 +342,8 @@ public static boolean isAllSingleValueDims( // Now check column capabilities. final ColumnCapabilities columnCapabilities = capabilitiesFunction.apply(dimension.getDimension()); - return (columnCapabilities != null && !columnCapabilities.hasMultipleValues()) || - (missingMeansNonexistent && columnCapabilities == null); + return (columnCapabilities != null && !columnCapabilities.hasMultipleValues().isMaybeTrue()) || + (missingMeansNonExistent && columnCapabilities == null); }); } diff --git a/processing/src/main/java/org/apache/druid/query/metadata/SegmentAnalyzer.java b/processing/src/main/java/org/apache/druid/query/metadata/SegmentAnalyzer.java index c5f1800acd15..659b55b1680d 100644 --- a/processing/src/main/java/org/apache/druid/query/metadata/SegmentAnalyzer.java +++ b/processing/src/main/java/org/apache/druid/query/metadata/SegmentAnalyzer.java @@ -45,6 +45,7 @@ import org.apache.druid.segment.column.DictionaryEncodedColumn; import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.data.IndexedInts; +import org.apache.druid.segment.incremental.IncrementalIndexStorageAdapter; import org.apache.druid.segment.serde.ComplexMetricSerde; import org.apache.druid.segment.serde.ComplexMetrics; import org.joda.time.DateTime; @@ -101,9 +102,18 @@ public Map analyze(Segment segment) for (String columnName : columnNames) { final ColumnHolder columnHolder = index == null ? null : index.getColumnHolder(columnName); - final ColumnCapabilities capabilities = columnHolder != null - ? columnHolder.getCapabilities() - : storageAdapter.getColumnCapabilities(columnName); + final ColumnCapabilities capabilities; + if (columnHolder != null) { + capabilities = columnHolder.getCapabilities(); + } else { + // this can be removed if we get to the point where IncrementalIndexStorageAdapter.getColumnCapabilities + // accurately reports the capabilities + if (storageAdapter instanceof IncrementalIndexStorageAdapter) { + capabilities = ((IncrementalIndexStorageAdapter) storageAdapter).getSnapshotColumnCapabilities(columnName); + } else { + capabilities = storageAdapter.getColumnCapabilities(columnName); + } + } final ColumnAnalysis analysis; final ValueType type = capabilities.getType(); @@ -138,7 +148,7 @@ public Map analyze(Segment segment) // Add time column too ColumnCapabilities timeCapabilities = storageAdapter.getColumnCapabilities(ColumnHolder.TIME_COLUMN_NAME); if (timeCapabilities == null) { - timeCapabilities = new ColumnCapabilitiesImpl().setType(ValueType.LONG).setHasMultipleValues(false); + timeCapabilities = ColumnCapabilitiesImpl.createSimpleNumericColumnCapabilities(ValueType.LONG); } columns.put( ColumnHolder.TIME_COLUMN_NAME, @@ -172,7 +182,7 @@ private ColumnAnalysis analyzeNumericColumn( long size = 0; if (analyzingSize()) { - if (capabilities.hasMultipleValues()) { + if (capabilities.hasMultipleValues().isTrue()) { return ColumnAnalysis.error("multi_value"); } @@ -181,7 +191,7 @@ private ColumnAnalysis analyzeNumericColumn( return new ColumnAnalysis( capabilities.getType().name(), - capabilities.hasMultipleValues(), + capabilities.hasMultipleValues().isTrue(), size, null, null, @@ -231,7 +241,7 @@ private ColumnAnalysis analyzeStringColumn( return new ColumnAnalysis( capabilities.getType().name(), - capabilities.hasMultipleValues(), + capabilities.hasMultipleValues().isTrue(), size, analyzingCardinality() ? cardinality : 0, min, @@ -308,7 +318,7 @@ public Long accumulate(Long accumulated, Cursor cursor) return new ColumnAnalysis( capabilities.getType().name(), - capabilities.hasMultipleValues(), + capabilities.hasMultipleValues().isTrue(), size, cardinality, min, @@ -324,7 +334,7 @@ private ColumnAnalysis analyzeComplexColumn( ) { try (final ComplexColumn complexColumn = columnHolder != null ? (ComplexColumn) columnHolder.getColumn() : null) { - final boolean hasMultipleValues = capabilities != null && capabilities.hasMultipleValues(); + final boolean hasMultipleValues = capabilities != null && capabilities.hasMultipleValues().isTrue(); long size = 0; if (analyzingSize() && complexColumn != null) { diff --git a/processing/src/main/java/org/apache/druid/segment/ColumnProcessors.java b/processing/src/main/java/org/apache/druid/segment/ColumnProcessors.java index 5fb698a484e4..05e85fdd4c24 100644 --- a/processing/src/main/java/org/apache/druid/segment/ColumnProcessors.java +++ b/processing/src/main/java/org/apache/druid/segment/ColumnProcessors.java @@ -197,6 +197,6 @@ private static T makeProcessorInternal( */ private static boolean mayBeMultiValue(@Nullable final ColumnCapabilities capabilities) { - return capabilities == null || !capabilities.isComplete() || capabilities.hasMultipleValues(); + return capabilities == null || capabilities.hasMultipleValues().isMaybeTrue(); } } diff --git a/processing/src/main/java/org/apache/druid/segment/ColumnSelectorBitmapIndexSelector.java b/processing/src/main/java/org/apache/druid/segment/ColumnSelectorBitmapIndexSelector.java index 79b6e8900745..bd6de7a02d57 100644 --- a/processing/src/main/java/org/apache/druid/segment/ColumnSelectorBitmapIndexSelector.java +++ b/processing/src/main/java/org/apache/druid/segment/ColumnSelectorBitmapIndexSelector.java @@ -27,6 +27,7 @@ import org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector; import org.apache.druid.segment.column.BaseColumn; import org.apache.druid.segment.column.BitmapIndex; +import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.column.ColumnHolder; import org.apache.druid.segment.column.DictionaryEncodedColumn; import org.apache.druid.segment.column.NumericColumn; @@ -157,14 +158,18 @@ public void close() throws IOException } @Override - public boolean hasMultipleValues(final String dimension) + public ColumnCapabilities.Capable hasMultipleValues(final String dimension) { if (isVirtualColumn(dimension)) { return virtualColumns.getVirtualColumn(dimension).capabilities(dimension).hasMultipleValues(); } final ColumnHolder columnHolder = index.getColumnHolder(dimension); - return columnHolder != null && columnHolder.getCapabilities().hasMultipleValues(); + // if ColumnHolder is null, the column doesn't exist, but report as not having multiple values so that + // the empty bitmap will be used + return columnHolder != null + ? columnHolder.getCapabilities().hasMultipleValues() + : ColumnCapabilities.Capable.FALSE; } @Override diff --git a/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java b/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java index 0c8b3c280c6e..9e5a12921c7c 100644 --- a/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java +++ b/processing/src/main/java/org/apache/druid/segment/DimensionHandlerUtils.java @@ -289,15 +289,23 @@ public static T makeVectorProcessor( final VectorColumnSelectorFactory selectorFactory ) { - final ColumnCapabilities capabilities = getEffectiveCapabilities( + final ColumnCapabilities originalCapabilities = + selectorFactory.getColumnCapabilities(dimensionSpec.getDimension()); + + final ColumnCapabilities effectiveCapabilites = getEffectiveCapabilities( dimensionSpec, - selectorFactory.getColumnCapabilities(dimensionSpec.getDimension()) + originalCapabilities ); - final ValueType type = capabilities.getType(); + final ValueType type = effectiveCapabilites.getType(); + + // vector selectors should never have null column capabilities, these signify a non-existent column, and complex + // columns should never be treated as a multi-value column, so always use single value string processor + final boolean forceSingleValue = + originalCapabilities == null || ValueType.COMPLEX.equals(originalCapabilities.getType()); if (type == ValueType.STRING) { - if (capabilities.hasMultipleValues()) { + if (!forceSingleValue && effectiveCapabilites.hasMultipleValues().isMaybeTrue()) { return strategyFactory.makeMultiValueDimensionProcessor( selectorFactory.makeMultiValueDimensionSelector(dimensionSpec) ); @@ -328,7 +336,7 @@ public static T makeVectorProcessor( selectorFactory.makeValueSelector(dimensionSpec.getDimension()) ); } else { - throw new ISE("Unsupported type[%s]", capabilities.getType()); + throw new ISE("Unsupported type[%s]", effectiveCapabilites.getType()); } } } diff --git a/processing/src/main/java/org/apache/druid/segment/DimensionIndexer.java b/processing/src/main/java/org/apache/druid/segment/DimensionIndexer.java index 99921eb79c08..cf7631db08bb 100644 --- a/processing/src/main/java/org/apache/druid/segment/DimensionIndexer.java +++ b/processing/src/main/java/org/apache/druid/segment/DimensionIndexer.java @@ -127,6 +127,19 @@ public interface DimensionIndexer */ EncodedKeyComponentType processRowValsToUnsortedEncodedKeyComponent(@Nullable Object dimValues, boolean reportParseExceptions); + /** + * This method will be called while building an {@link IncrementalIndex} whenever a known dimension column (either + * through an explicit schema on the ingestion spec, or auto-discovered while processing rows) is absent in any row + * that is processed, to allow an indexer to account for any missing rows if necessary. Useful so that a string + * {@link DimensionSelector} built on top of an {@link IncrementalIndex} may accurately report + * {@link DimensionSelector#nameLookupPossibleInAdvance()} by allowing it to track if it has any implicit null valued + * rows. + * + * At index persist/merge time all missing columns for a row will be explicitly replaced with the value appropriate + * null or default value. + */ + void setSparseIndexed(); + /** * Gives the estimated size in bytes for the given key * diff --git a/processing/src/main/java/org/apache/druid/segment/DoubleDimensionIndexer.java b/processing/src/main/java/org/apache/druid/segment/DoubleDimensionIndexer.java index 6645e1fc7328..b802f7555135 100644 --- a/processing/src/main/java/org/apache/druid/segment/DoubleDimensionIndexer.java +++ b/processing/src/main/java/org/apache/druid/segment/DoubleDimensionIndexer.java @@ -47,6 +47,12 @@ public Double processRowValsToUnsortedEncodedKeyComponent(@Nullable Object dimVa return DimensionHandlerUtils.convertObjectToDouble(dimValues, reportParseExceptions); } + @Override + public void setSparseIndexed() + { + // no-op, double columns do not have a dictionary to track null values + } + @Override public long estimateEncodedKeyComponentSize(Double key) { diff --git a/processing/src/main/java/org/apache/druid/segment/FloatDimensionIndexer.java b/processing/src/main/java/org/apache/druid/segment/FloatDimensionIndexer.java index 41328828de2b..dce58a23b23f 100644 --- a/processing/src/main/java/org/apache/druid/segment/FloatDimensionIndexer.java +++ b/processing/src/main/java/org/apache/druid/segment/FloatDimensionIndexer.java @@ -48,6 +48,12 @@ public Float processRowValsToUnsortedEncodedKeyComponent(@Nullable Object dimVal return DimensionHandlerUtils.convertObjectToFloat(dimValues, reportParseExceptions); } + @Override + public void setSparseIndexed() + { + // no-op, float columns do not have a dictionary to track null values + } + @Override public long estimateEncodedKeyComponentSize(Float key) { diff --git a/processing/src/main/java/org/apache/druid/segment/IndexMergerV9.java b/processing/src/main/java/org/apache/druid/segment/IndexMergerV9.java index bdf0bccf672a..066b8dc41ea6 100644 --- a/processing/src/main/java/org/apache/druid/segment/IndexMergerV9.java +++ b/processing/src/main/java/org/apache/druid/segment/IndexMergerV9.java @@ -165,7 +165,7 @@ private File makeIndexFiles( progress.progress(); final Map metricsValueTypes = new TreeMap<>(Comparators.naturalNullsFirst()); final Map metricTypeNames = new TreeMap<>(Comparators.naturalNullsFirst()); - final List dimCapabilities = Lists.newArrayListWithCapacity(mergedDimensions.size()); + final List dimCapabilities = Lists.newArrayListWithCapacity(mergedDimensions.size()); mergeCapabilities(adapters, mergedDimensions, metricsValueTypes, metricTypeNames, dimCapabilities); final Map handlers = makeDimensionHandlers(mergedDimensions, dimCapabilities); @@ -716,18 +716,22 @@ private void mergeCapabilities( final List mergedDimensions, final Map metricsValueTypes, final Map metricTypeNames, - final List dimCapabilities + final List dimCapabilities ) { - final Map capabilitiesMap = new HashMap<>(); + final Map capabilitiesMap = new HashMap<>(); for (IndexableAdapter adapter : adapters) { for (String dimension : adapter.getDimensionNames()) { ColumnCapabilities capabilities = adapter.getCapabilities(dimension); - capabilitiesMap.computeIfAbsent(dimension, d -> new ColumnCapabilitiesImpl().setIsComplete(true)).merge(capabilities); + capabilitiesMap.compute(dimension, (d, existingCapabilities) -> + ColumnCapabilitiesImpl.snapshot(capabilities) + .merge(ColumnCapabilitiesImpl.snapshot(existingCapabilities))); } for (String metric : adapter.getMetricNames()) { ColumnCapabilities capabilities = adapter.getCapabilities(metric); - capabilitiesMap.computeIfAbsent(metric, m -> new ColumnCapabilitiesImpl().setIsComplete(true)).merge(capabilities); + capabilitiesMap.compute(metric, (m, existingCapabilities) -> + ColumnCapabilitiesImpl.snapshot(capabilities) + .merge(ColumnCapabilitiesImpl.snapshot(existingCapabilities))); metricsValueTypes.put(metric, capabilities.getType()); metricTypeNames.put(metric, adapter.getMetricType(metric)); } @@ -1002,7 +1006,7 @@ public File append( private Map makeDimensionHandlers( final List mergedDimensions, - final List dimCapabilities + final List dimCapabilities ) { Map handlers = new LinkedHashMap<>(); diff --git a/processing/src/main/java/org/apache/druid/segment/LongDimensionIndexer.java b/processing/src/main/java/org/apache/druid/segment/LongDimensionIndexer.java index d35724398f9a..f2a91278f6bb 100644 --- a/processing/src/main/java/org/apache/druid/segment/LongDimensionIndexer.java +++ b/processing/src/main/java/org/apache/druid/segment/LongDimensionIndexer.java @@ -48,6 +48,12 @@ public Long processRowValsToUnsortedEncodedKeyComponent(@Nullable Object dimValu return DimensionHandlerUtils.convertObjectToLong(dimValues, reportParseExceptions); } + @Override + public void setSparseIndexed() + { + // no-op, long columns do not have a dictionary to track null values + } + @Override public long estimateEncodedKeyComponentSize(Long key) { diff --git a/processing/src/main/java/org/apache/druid/segment/RowBasedColumnSelectorFactory.java b/processing/src/main/java/org/apache/druid/segment/RowBasedColumnSelectorFactory.java index 7b21d1f0186c..77e8978e2bee 100644 --- a/processing/src/main/java/org/apache/druid/segment/RowBasedColumnSelectorFactory.java +++ b/processing/src/main/java/org/apache/druid/segment/RowBasedColumnSelectorFactory.java @@ -96,7 +96,7 @@ static ColumnCapabilities getColumnCapabilities( { if (ColumnHolder.TIME_COLUMN_NAME.equals(columnName)) { // TIME_COLUMN_NAME is handled specially; override the provided rowSignature. - return new ColumnCapabilitiesImpl().setType(ValueType.LONG).setIsComplete(true); + return ColumnCapabilitiesImpl.createSimpleNumericColumnCapabilities(ValueType.LONG); } else { final ValueType valueType = rowSignature.getColumnType(columnName).orElse(null); @@ -105,12 +105,13 @@ static ColumnCapabilities getColumnCapabilities( // causes expression selectors to always treat us as arrays. If we might have multiple values (i.e. if our type // is nonnumeric), set isComplete false to compensate. if (valueType != null) { + if (valueType.isNumeric()) { + return ColumnCapabilitiesImpl.createSimpleNumericColumnCapabilities(valueType); + } return new ColumnCapabilitiesImpl() .setType(valueType) .setDictionaryValuesUnique(false) - .setDictionaryValuesSorted(false) - // Numeric types should be reported as complete, but not STRING or COMPLEX (because we don't have full info) - .setIsComplete(valueType.isNumeric()); + .setDictionaryValuesSorted(false); } else { return null; } diff --git a/processing/src/main/java/org/apache/druid/segment/StringDimensionIndexer.java b/processing/src/main/java/org/apache/druid/segment/StringDimensionIndexer.java index ada146d56d41..c0200e1e2eff 100644 --- a/processing/src/main/java/org/apache/druid/segment/StringDimensionIndexer.java +++ b/processing/src/main/java/org/apache/druid/segment/StringDimensionIndexer.java @@ -233,7 +233,8 @@ public String getValueFromSortedId(int index) private final DimensionDictionary dimLookup; private final MultiValueHandling multiValueHandling; private final boolean hasBitmapIndexes; - private boolean hasMultipleValues = false; + private volatile boolean hasMultipleValues = false; + private volatile boolean isSparse = false; @Nullable private SortedDimensionDictionary sortedLookup; @@ -301,6 +302,12 @@ public int[] processRowValsToUnsortedEncodedKeyComponent(@Nullable Object dimVal return encodedDimensionValues; } + @Override + public void setSparseIndexed() + { + isSparse = true; + } + @Override public long estimateEncodedKeyComponentSize(int[] key) { @@ -623,7 +630,9 @@ public String lookupName(int id) @Override public boolean nameLookupPossibleInAdvance() { - return true; + // name lookup is possible in advance if we got a value for every row (setSparseIndexed was not called on this + // column) or we've encountered an actual null value and it is present in our dictionary + return !isSparse || dimLookup.idForNull != ABSENT_VALUE_ID; } @Nullable diff --git a/processing/src/main/java/org/apache/druid/segment/StringDimensionMergerV9.java b/processing/src/main/java/org/apache/druid/segment/StringDimensionMergerV9.java index cff7ef99f805..abb4637dc7b8 100644 --- a/processing/src/main/java/org/apache/druid/segment/StringDimensionMergerV9.java +++ b/processing/src/main/java/org/apache/druid/segment/StringDimensionMergerV9.java @@ -221,7 +221,7 @@ protected void setupEncodedValueWriter() throws IOException final CompressionStrategy compressionStrategy = indexSpec.getDimensionCompression(); String filenameBase = StringUtils.format("%s.forward_dim", dimensionName); - if (capabilities.hasMultipleValues()) { + if (capabilities.hasMultipleValues().isTrue()) { if (compressionStrategy != CompressionStrategy.UNCOMPRESSED) { encodedValueSerializer = V3CompressedVSizeColumnarMultiIntsSerializer.create( dimensionName, @@ -533,7 +533,7 @@ public boolean canSkip() public ColumnDescriptor makeColumnDescriptor() { // Now write everything - boolean hasMultiValue = capabilities.hasMultipleValues(); + boolean hasMultiValue = capabilities.hasMultipleValues().isTrue(); final CompressionStrategy compressionStrategy = indexSpec.getDimensionCompression(); final BitmapSerdeFactory bitmapSerdeFactory = indexSpec.getBitmapSerdeFactory(); diff --git a/processing/src/main/java/org/apache/druid/segment/column/ColumnBuilder.java b/processing/src/main/java/org/apache/druid/segment/column/ColumnBuilder.java index 1b7163e0829b..cde454b70524 100644 --- a/processing/src/main/java/org/apache/druid/segment/column/ColumnBuilder.java +++ b/processing/src/main/java/org/apache/druid/segment/column/ColumnBuilder.java @@ -118,7 +118,6 @@ public ColumnHolder build() .setDictionaryValuesUnique(dictionaryEncoded) .setHasSpatialIndexes(spatialIndex != null) .setHasMultipleValues(hasMultipleValues) - .setIsComplete(true) .setFilterable(filterable), columnSupplier, bitmapIndex, diff --git a/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilities.java b/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilities.java index 53f7440e87ef..a9af25b4602c 100644 --- a/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilities.java +++ b/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilities.java @@ -19,33 +19,26 @@ package org.apache.druid.segment.column; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import org.apache.druid.java.util.common.StringUtils; + +import javax.annotation.Nullable; + /** */ public interface ColumnCapabilities { ValueType getType(); - boolean isDictionaryEncoded(); Capable areDictionaryValuesSorted(); Capable areDictionaryValuesUnique(); boolean isRunLengthEncoded(); boolean hasBitmapIndexes(); boolean hasSpatialIndexes(); - boolean hasMultipleValues(); + Capable hasMultipleValues(); boolean isFilterable(); - /** - * This property indicates that this {@link ColumnCapabilities} is "complete" in that all properties can be expected - * to supply valid responses. This is mostly a hack to work around {@link ColumnCapabilities} generators that - * fail to set {@link #hasMultipleValues()} even when the associated column really could have multiple values. - * Until this situation is sorted out, if this method returns false, callers are encouraged to ignore - * {@link #hasMultipleValues()} and treat that property as if it were unknown. - * - * todo: replace all booleans with {@link Capable} and this method can be dropped - */ - boolean isComplete(); - - enum Capable { FALSE, @@ -57,6 +50,21 @@ public boolean isTrue() return this == TRUE; } + public boolean isMaybeTrue() + { + return isTrue() || isUnknown(); + } + + public boolean isUnknown() + { + return this == UNKNOWN; + } + + public Capable coerceUnknownToBoolean(boolean unknownIsTrue) + { + return this == UNKNOWN ? Capable.of(unknownIsTrue) : this; + } + public Capable and(Capable other) { if (this == UNKNOWN || other == UNKNOWN) { @@ -65,9 +73,36 @@ public Capable and(Capable other) return this == TRUE && other == TRUE ? TRUE : FALSE; } + public Capable or(Capable other) + { + if (this == TRUE) { + return TRUE; + } + return other; + } + public static Capable of(boolean bool) { return bool ? TRUE : FALSE; } + + @JsonCreator + public static Capable ofNullable(@Nullable Boolean bool) + { + return bool == null ? Capable.UNKNOWN : of(bool); + } + + @JsonValue + @Nullable + public Boolean toJson() + { + return this == UNKNOWN ? null : isTrue(); + } + + @Override + public String toString() + { + return StringUtils.toLowerCase(super.toString()); + } } } diff --git a/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilitiesImpl.java b/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilitiesImpl.java index bee28ebdf832..9ddbd04a372f 100644 --- a/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilitiesImpl.java +++ b/processing/src/main/java/org/apache/druid/segment/column/ColumnCapabilitiesImpl.java @@ -31,15 +31,65 @@ */ public class ColumnCapabilitiesImpl implements ColumnCapabilities { - public static ColumnCapabilitiesImpl copyOf(final ColumnCapabilities other) + public static ColumnCapabilitiesImpl copyOf(@Nullable final ColumnCapabilities other) { final ColumnCapabilitiesImpl capabilities = new ColumnCapabilitiesImpl(); - capabilities.merge(other); - capabilities.setFilterable(other.isFilterable()); - capabilities.setIsComplete(other.isComplete()); + if (other != null) { + capabilities.type = other.getType(); + capabilities.dictionaryEncoded = other.isDictionaryEncoded(); + capabilities.runLengthEncoded = other.isRunLengthEncoded(); + capabilities.hasInvertedIndexes = other.hasBitmapIndexes(); + capabilities.hasSpatialIndexes = other.hasSpatialIndexes(); + capabilities.hasMultipleValues = other.hasMultipleValues(); + capabilities.dictionaryValuesSorted = other.areDictionaryValuesSorted(); + capabilities.dictionaryValuesUnique = other.areDictionaryValuesUnique(); + capabilities.filterable = other.isFilterable(); + } return capabilities; } + /** + * Used at indexing time to finalize all {@link Capable#UNKNOWN} values to + * {@link Capable#FALSE}, in order to present a snapshot of the state of the this column + */ + @Nullable + public static ColumnCapabilitiesImpl snapshot(@Nullable final ColumnCapabilities capabilities) + { + return snapshot(capabilities, false); + } + + /** + * Used at indexing time to finalize all {@link Capable#UNKNOWN} values to + * {@link Capable#FALSE} or {@link Capable#TRUE}, in order to present a snapshot of the state of the this column + */ + @Nullable + public static ColumnCapabilitiesImpl snapshot(@Nullable final ColumnCapabilities capabilities, boolean unknownIsTrue) + { + if (capabilities == null) { + return null; + } + ColumnCapabilitiesImpl copy = copyOf(capabilities); + copy.hasMultipleValues = copy.hasMultipleValues.coerceUnknownToBoolean(unknownIsTrue); + copy.dictionaryValuesSorted = copy.dictionaryValuesSorted.coerceUnknownToBoolean(unknownIsTrue); + copy.dictionaryValuesUnique = copy.dictionaryValuesUnique.coerceUnknownToBoolean(unknownIsTrue); + return copy; + } + + + /** + * Create a no frills, simple column with {@link ValueType} set and everything else false + */ + public static ColumnCapabilitiesImpl createSimpleNumericColumnCapabilities(ValueType valueType) + { + return new ColumnCapabilitiesImpl().setType(valueType) + .setHasMultipleValues(false) + .setHasBitmapIndexes(false) + .setDictionaryEncoded(false) + .setDictionaryValuesSorted(false) + .setDictionaryValuesUnique(false) + .setHasSpatialIndexes(false); + } + @Nullable private ValueType type = null; @@ -47,7 +97,7 @@ public static ColumnCapabilitiesImpl copyOf(final ColumnCapabilities other) private boolean runLengthEncoded = false; private boolean hasInvertedIndexes = false; private boolean hasSpatialIndexes = false; - private boolean hasMultipleValues = false; + private Capable hasMultipleValues = Capable.UNKNOWN; // These capabilities are computed at query time and not persisted in the segment files. @JsonIgnore @@ -56,8 +106,6 @@ public static ColumnCapabilitiesImpl copyOf(final ColumnCapabilities other) private Capable dictionaryValuesUnique = Capable.UNKNOWN; @JsonIgnore private boolean filterable; - @JsonIgnore - private boolean complete = false; @Override @JsonProperty @@ -144,14 +192,14 @@ public ColumnCapabilitiesImpl setHasSpatialIndexes(boolean hasSpatialIndexes) @Override @JsonProperty("hasMultipleValues") - public boolean hasMultipleValues() + public Capable hasMultipleValues() { return hasMultipleValues; } public ColumnCapabilitiesImpl setHasMultipleValues(boolean hasMultipleValues) { - this.hasMultipleValues = hasMultipleValues; + this.hasMultipleValues = Capable.of(hasMultipleValues); return this; } @@ -171,22 +219,10 @@ public ColumnCapabilitiesImpl setFilterable(boolean filterable) return this; } - @Override - public boolean isComplete() - { - return complete; - } - - public ColumnCapabilitiesImpl setIsComplete(boolean complete) - { - this.complete = complete; - return this; - } - - public void merge(ColumnCapabilities other) + public ColumnCapabilities merge(@Nullable ColumnCapabilities other) { if (other == null) { - return; + return this; } if (type == null) { @@ -201,10 +237,11 @@ public void merge(ColumnCapabilities other) this.runLengthEncoded |= other.isRunLengthEncoded(); this.hasInvertedIndexes |= other.hasBitmapIndexes(); this.hasSpatialIndexes |= other.hasSpatialIndexes(); - this.hasMultipleValues |= other.hasMultipleValues(); - this.complete &= other.isComplete(); // these should always be the same? this.filterable &= other.isFilterable(); + this.hasMultipleValues = this.hasMultipleValues.or(other.hasMultipleValues()); this.dictionaryValuesSorted = this.dictionaryValuesSorted.and(other.areDictionaryValuesSorted()); this.dictionaryValuesUnique = this.dictionaryValuesUnique.and(other.areDictionaryValuesUnique()); + + return this; } } diff --git a/processing/src/main/java/org/apache/druid/segment/filter/ExpressionFilter.java b/processing/src/main/java/org/apache/druid/segment/filter/ExpressionFilter.java index ef7613c6137c..0baa59490712 100644 --- a/processing/src/main/java/org/apache/druid/segment/filter/ExpressionFilter.java +++ b/processing/src/main/java/org/apache/druid/segment/filter/ExpressionFilter.java @@ -115,7 +115,7 @@ public boolean supportsBitmapIndex(final BitmapIndexSelector selector) // multiple values. The lack of multiple values is important because expression filters treat multi-value // arrays as nulls, which doesn't permit index based filtering. final String column = Iterables.getOnlyElement(requiredBindings.get()); - return selector.getBitmapIndex(column) != null && !selector.hasMultipleValues(column); + return selector.getBitmapIndex(column) != null && !selector.hasMultipleValues(column).isMaybeTrue(); } else { // Multi-column expression. return false; diff --git a/processing/src/main/java/org/apache/druid/segment/filter/Filters.java b/processing/src/main/java/org/apache/druid/segment/filter/Filters.java index b6a717bff1b1..990127f99f04 100644 --- a/processing/src/main/java/org/apache/druid/segment/filter/Filters.java +++ b/processing/src/main/java/org/apache/druid/segment/filter/Filters.java @@ -414,7 +414,7 @@ static boolean supportsSelectivityEstimation( if (filter.supportsBitmapIndex(indexSelector)) { final ColumnHolder columnHolder = columnSelector.getColumnHolder(dimension); if (columnHolder != null) { - return !columnHolder.getCapabilities().hasMultipleValues(); + return !columnHolder.getCapabilities().hasMultipleValues().isMaybeTrue(); } } return false; diff --git a/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndex.java b/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndex.java index c169d15f2230..649aea9b6997 100644 --- a/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndex.java +++ b/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndex.java @@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterators; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; import com.google.errorprone.annotations.concurrent.GuardedBy; @@ -89,6 +90,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentMap; @@ -327,9 +329,10 @@ protected IncrementalIndex( } //__time capabilities - ColumnCapabilitiesImpl timeCapabilities = new ColumnCapabilitiesImpl().setIsComplete(true); - timeCapabilities.setType(ValueType.LONG); - columnCapabilities.put(ColumnHolder.TIME_COLUMN_NAME, timeCapabilities); + columnCapabilities.put( + ColumnHolder.TIME_COLUMN_NAME, + ColumnCapabilitiesImpl.createSimpleNumericColumnCapabilities(ValueType.LONG) + ); // This should really be more generic List spatialDimensions = dimensionsSpec.getSpatialDimensions(); @@ -640,12 +643,15 @@ IncrementalIndexRowResult toIncrementalIndexRow(InputRow row) } final List rowDimensions = row.getDimensions(); - Object[] dims; List overflow = null; long dimsKeySize = 0; List parseExceptionMessages = new ArrayList<>(); synchronized (dimensionDescs) { + // all known dimensions are assumed missing until we encounter in the rowDimensions + Set absentDimensions = Sets.newHashSet(dimensionDescs.keySet()); + + // first, process dimension values present in the row dims = new Object[dimensionDescs.size()]; for (String dimension : rowDimensions) { if (Strings.isNullOrEmpty(dimension)) { @@ -656,18 +662,13 @@ IncrementalIndexRowResult toIncrementalIndexRow(InputRow row) DimensionDesc desc = dimensionDescs.get(dimension); if (desc != null) { capabilities = desc.getCapabilities(); + absentDimensions.remove(dimension); } else { wasNewDim = true; capabilities = columnCapabilities.get(dimension); if (capabilities == null) { - capabilities = new ColumnCapabilitiesImpl(); // For schemaless type discovery, assume everything is a String for now, can change later. - capabilities.setType(ValueType.STRING); - capabilities.setDictionaryEncoded(true); - capabilities.setHasBitmapIndexes(true); - capabilities.setDictionaryValuesSorted(false); - capabilities.setDictionaryValuesUnique(true); - capabilities.setIsComplete(true); + capabilities = makeCapabilitiesFromValueType(ValueType.STRING); columnCapabilities.put(dimension, capabilities); } DimensionHandler handler = DimensionHandlerUtils.getHandlerFromCapabilities(dimension, capabilities, null); @@ -677,23 +678,24 @@ IncrementalIndexRowResult toIncrementalIndexRow(InputRow row) DimensionIndexer indexer = desc.getIndexer(); Object dimsKey = null; try { - dimsKey = indexer.processRowValsToUnsortedEncodedKeyComponent( - row.getRaw(dimension), - true - ); + dimsKey = indexer.processRowValsToUnsortedEncodedKeyComponent(row.getRaw(dimension), true); } catch (ParseException pe) { parseExceptionMessages.add(pe.getMessage()); } dimsKeySize += indexer.estimateEncodedKeyComponentSize(dimsKey); // Set column capabilities as data is coming in - if (!capabilities.hasMultipleValues() && + if (!capabilities.hasMultipleValues().isTrue() && dimsKey != null && handler.getLengthOfEncodedKeyComponent(dimsKey) > 1) { capabilities.setHasMultipleValues(true); } if (wasNewDim) { + // unless this is the first row we are processing, all newly discovered columns will be sparse + if (maxIngestedEventTime != null) { + indexer.setSparseIndexed(); + } if (overflow == null) { overflow = new ArrayList<>(); } @@ -713,6 +715,11 @@ IncrementalIndexRowResult toIncrementalIndexRow(InputRow row) dims[desc.getIndex()] = dimsKey; } } + + // process any dimensions with missing values in the row + for (String missing : absentDimensions) { + dimensionDescs.get(missing).getIndexer().setSparseIndexed(); + } } if (overflow != null) { @@ -923,16 +930,16 @@ public List getDimensionOrder() private ColumnCapabilitiesImpl makeCapabilitiesFromValueType(ValueType type) { - ColumnCapabilitiesImpl capabilities = new ColumnCapabilitiesImpl(); - capabilities.setDictionaryEncoded(type == ValueType.STRING); - capabilities.setHasBitmapIndexes(type == ValueType.STRING); if (type == ValueType.STRING) { - capabilities.setDictionaryValuesUnique(true); - capabilities.setDictionaryValuesSorted(false); + // we start out as not having multiple values, but this might change as we encounter them + return new ColumnCapabilitiesImpl().setType(type) + .setHasBitmapIndexes(true) + .setDictionaryEncoded(true) + .setDictionaryValuesUnique(true) + .setDictionaryValuesSorted(false); + } else { + return ColumnCapabilitiesImpl.createSimpleNumericColumnCapabilities(type); } - capabilities.setType(type); - capabilities.setIsComplete(true); - return capabilities; } /** @@ -988,6 +995,7 @@ public StorageAdapter toStorageAdapter() return new IncrementalIndexStorageAdapter(this); } + @Nullable public ColumnCapabilities getCapabilities(String column) { return columnCapabilities.get(column); @@ -1124,18 +1132,18 @@ public MetricDesc(int index, AggregatorFactory factory) this.name = factory.getName(); String typeInfo = factory.getTypeName(); - this.capabilities = new ColumnCapabilitiesImpl().setIsComplete(true); if ("float".equalsIgnoreCase(typeInfo)) { - capabilities.setType(ValueType.FLOAT); + capabilities = ColumnCapabilitiesImpl.createSimpleNumericColumnCapabilities(ValueType.FLOAT); this.type = typeInfo; } else if ("long".equalsIgnoreCase(typeInfo)) { - capabilities.setType(ValueType.LONG); + capabilities = ColumnCapabilitiesImpl.createSimpleNumericColumnCapabilities(ValueType.LONG); this.type = typeInfo; } else if ("double".equalsIgnoreCase(typeInfo)) { - capabilities.setType(ValueType.DOUBLE); + capabilities = ColumnCapabilitiesImpl.createSimpleNumericColumnCapabilities(ValueType.DOUBLE); this.type = typeInfo; } else { - capabilities.setType(ValueType.COMPLEX); + // in an ideal world complex type reports its actual column capabilities... + capabilities = ColumnCapabilitiesImpl.createSimpleNumericColumnCapabilities(ValueType.COMPLEX); this.type = ComplexMetrics.getSerdeForType(typeInfo).getTypeName(); } } diff --git a/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndexStorageAdapter.java b/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndexStorageAdapter.java index cc792a5314c6..8e8520d458b7 100644 --- a/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndexStorageAdapter.java +++ b/processing/src/main/java/org/apache/druid/segment/incremental/IncrementalIndexStorageAdapter.java @@ -39,7 +39,6 @@ import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.column.ColumnCapabilitiesImpl; import org.apache.druid.segment.column.ColumnHolder; -import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.data.Indexed; import org.apache.druid.segment.data.ListIndexed; import org.apache.druid.segment.filter.BooleanValueMatcher; @@ -150,16 +149,23 @@ public ColumnCapabilities getColumnCapabilities(String column) // at index-persisting time to determine if we need a multi-value column or not. However, that means we // need to tweak the capabilities here in the StorageAdapter (a query-time construct), so at query time // they appear multi-valued. + // + // Note that this could be improved if we snapshot the capabilities at cursor creation time and feed those through + // to the StringDimensionIndexer so the selector built on top of it can produce values from the snapshot state of + // multi-valuedness at cursor creation time, instead of the latest state, and getSnapshotColumnCapabilities could + // be removed. + return ColumnCapabilitiesImpl.snapshot(index.getCapabilities(column), true); + } - final ColumnCapabilities capabilitiesFromIndex = index.getCapabilities(column); - final IncrementalIndex.DimensionDesc dimensionDesc = index.getDimension(column); - if (dimensionDesc != null && dimensionDesc.getCapabilities().getType() == ValueType.STRING) { - final ColumnCapabilitiesImpl retVal = ColumnCapabilitiesImpl.copyOf(capabilitiesFromIndex); - retVal.setHasMultipleValues(true); - return retVal; - } else { - return capabilitiesFromIndex; - } + /** + * Sad workaround for {@link org.apache.druid.query.metadata.SegmentAnalyzer} to deal with the fact that the + * response from {@link #getColumnCapabilities} is not accurate for string columns, in that it reports all string + * string columns as having multiple values. This method returns the actual capabilities of the underlying + * {@link IncrementalIndex}at the time this method is called. + */ + public ColumnCapabilities getSnapshotColumnCapabilities(String column) + { + return ColumnCapabilitiesImpl.snapshot(index.getCapabilities(column)); } @Override diff --git a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableColumnSelectorFactory.java b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableColumnSelectorFactory.java index 7e6466d78288..00cb51ffffff 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableColumnSelectorFactory.java +++ b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableColumnSelectorFactory.java @@ -58,8 +58,9 @@ static ColumnCapabilities columnCapabilities(final IndexedTable table, final Str capabilities.setDictionaryValuesSorted(false); capabilities.setDictionaryValuesUnique(false); + capabilities.setHasMultipleValues(false); - return capabilities.setIsComplete(true); + return capabilities; } else { return null; } diff --git a/processing/src/main/java/org/apache/druid/segment/vector/QueryableIndexVectorColumnSelectorFactory.java b/processing/src/main/java/org/apache/druid/segment/vector/QueryableIndexVectorColumnSelectorFactory.java index b9cfe4f03d69..269ac38429bb 100644 --- a/processing/src/main/java/org/apache/druid/segment/vector/QueryableIndexVectorColumnSelectorFactory.java +++ b/processing/src/main/java/org/apache/druid/segment/vector/QueryableIndexVectorColumnSelectorFactory.java @@ -85,7 +85,7 @@ public MultiValueDimensionVectorSelector makeMultiValueDimensionSelector(final D if (holder == null || !holder.getCapabilities().isDictionaryEncoded() || holder.getCapabilities().getType() != ValueType.STRING - || !holder.getCapabilities().hasMultipleValues()) { + || !holder.getCapabilities().hasMultipleValues().isMaybeTrue()) { throw new ISE( "Column[%s] is not a multi-value string column, do not ask for a multi-value selector", spec.getDimension() @@ -125,7 +125,7 @@ public SingleValueDimensionVectorSelector makeSingleValueDimensionSelector(final return NilVectorSelector.create(offset); } - if (holder.getCapabilities().hasMultipleValues()) { + if (holder.getCapabilities().hasMultipleValues().isMaybeTrue()) { // Asking for a single-value dimension selector on a multi-value column gets you an error. throw new ISE("Column[%s] is multi-value, do not ask for a single-value selector", spec.getDimension()); } diff --git a/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java b/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java index 5ae6987df88a..5ab4e4694a2b 100644 --- a/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java +++ b/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionSelectors.java @@ -154,8 +154,7 @@ public static ColumnValueSelector makeExprEvalSelector( } else if (capabilities != null && capabilities.getType() == ValueType.STRING && capabilities.isDictionaryEncoded() - && capabilities.isComplete() - && !capabilities.hasMultipleValues() + && !capabilities.hasMultipleValues().isMaybeTrue() && exprDetails.getArrayBindings().isEmpty()) { // Optimization for expressions that hit one scalar string column and nothing else. return new SingleStringInputCachingExpressionColumnValueSelector( @@ -227,7 +226,7 @@ public static DimensionSelector makeDimensionSelector( if (capabilities != null && capabilities.getType() == ValueType.STRING && capabilities.isDictionaryEncoded() - && capabilities.isComplete() + && !capabilities.hasMultipleValues().isUnknown() && !exprDetails.hasInputArrays() && !exprDetails.isOutputArray() ) { @@ -356,7 +355,7 @@ private static Expr.ObjectBinding createBindings( final ColumnCapabilities columnCapabilities = columnSelectorFactory .getColumnCapabilities(columnName); final ValueType nativeType = columnCapabilities != null ? columnCapabilities.getType() : null; - final boolean multiVal = columnCapabilities != null && columnCapabilities.hasMultipleValues(); + final boolean multiVal = columnCapabilities != null && columnCapabilities.hasMultipleValues().isTrue(); final Supplier supplier; if (nativeType == ValueType.FLOAT) { @@ -597,11 +596,11 @@ private static Pair, Set> examineColumnSelectorFactoryArrays for (String column : columns) { final ColumnCapabilities capabilities = columnSelectorFactory.getColumnCapabilities(column); if (capabilities != null) { - if (capabilities.hasMultipleValues()) { + if (capabilities.hasMultipleValues().isTrue()) { actualArrays.add(column); } else if ( - !capabilities.isComplete() && capabilities.getType().equals(ValueType.STRING) && + capabilities.hasMultipleValues().isMaybeTrue() && !exprDetails.getArrayBindings().contains(column) ) { unknownIfArrays.add(column); diff --git a/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionVirtualColumn.java b/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionVirtualColumn.java index 482365152931..59b846fa9a99 100644 --- a/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionVirtualColumn.java +++ b/processing/src/main/java/org/apache/druid/segment/virtual/ExpressionVirtualColumn.java @@ -131,10 +131,10 @@ public ColumnValueSelector makeColumnValueSelector(String columnName, ColumnS @Override public ColumnCapabilities capabilities(String columnName) { - // Note: Ideally we would only "setHasMultipleValues(true)" if the expression in question could potentially return - // multiple values. However, we don't currently have a good way of determining this, so to be safe we always - // set the flag. - return new ColumnCapabilitiesImpl().setType(outputType).setHasMultipleValues(true); + // Note: Ideally we would fill out additional information instead of leaving capabilities as 'unknown', e.g. examine + // if the expression in question could potentially return multiple values and anything else. However, we don't + // currently have a good way of determining this, so fill this out more once we do + return new ColumnCapabilitiesImpl().setType(outputType); } @Override diff --git a/processing/src/test/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2Test.java b/processing/src/test/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2Test.java index d71d70a1aac9..75a12a90cb6c 100644 --- a/processing/src/test/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2Test.java +++ b/processing/src/test/java/org/apache/druid/query/groupby/epinephelinae/GroupByQueryEngineV2Test.java @@ -47,8 +47,7 @@ public void testCanPushDownLimitForSegmentStringSelector() .setHasMultipleValues(false) .setDictionaryEncoded(true) .setDictionaryValuesSorted(true) - .setDictionaryValuesUnique(true) - .setIsComplete(true); + .setDictionaryValuesUnique(true); EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).once(); EasyMock.replay(factory); Assert.assertTrue(GroupByQueryEngineV2.canPushDownLimit(factory, DIM)); @@ -63,8 +62,7 @@ public void testCanPushDownLimitForIncrementalStringSelector() .setHasMultipleValues(false) .setDictionaryEncoded(false) .setDictionaryValuesSorted(false) - .setDictionaryValuesUnique(true) - .setIsComplete(true); + .setDictionaryValuesUnique(true); EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).once(); EasyMock.replay(factory); Assert.assertFalse(GroupByQueryEngineV2.canPushDownLimit(factory, DIM)); @@ -79,8 +77,7 @@ public void testCanPushDownLimitForExpressionStringSelector() .setHasMultipleValues(false) .setDictionaryEncoded(false) .setDictionaryValuesSorted(false) - .setDictionaryValuesUnique(false) - .setIsComplete(true); + .setDictionaryValuesUnique(false); EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).once(); EasyMock.replay(factory); Assert.assertFalse(GroupByQueryEngineV2.canPushDownLimit(factory, DIM)); @@ -95,8 +92,7 @@ public void testCanPushDownLimitForJoinStringSelector() .setHasMultipleValues(false) .setDictionaryEncoded(true) .setDictionaryValuesSorted(false) - .setDictionaryValuesUnique(false) - .setIsComplete(true); + .setDictionaryValuesUnique(false); EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).once(); EasyMock.replay(factory); Assert.assertFalse(GroupByQueryEngineV2.canPushDownLimit(factory, DIM)); @@ -111,8 +107,7 @@ public void testCanPushDownLimitForNumericSelector() .setHasMultipleValues(false) .setDictionaryEncoded(false) .setDictionaryValuesSorted(false) - .setDictionaryValuesUnique(false) - .setIsComplete(true); + .setDictionaryValuesUnique(false); EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).anyTimes(); EasyMock.replay(factory); Assert.assertTrue(GroupByQueryEngineV2.canPushDownLimit(factory, DIM)); @@ -131,8 +126,7 @@ public void testCanPushDownLimitForComplexSelector() .setHasMultipleValues(false) .setDictionaryEncoded(false) .setDictionaryValuesSorted(false) - .setDictionaryValuesUnique(false) - .setIsComplete(true); + .setDictionaryValuesUnique(false); EasyMock.expect(factory.getColumnCapabilities(DIM)).andReturn(capabilities).once(); EasyMock.replay(factory); Assert.assertTrue(GroupByQueryEngineV2.canPushDownLimit(factory, DIM)); diff --git a/processing/src/test/java/org/apache/druid/query/lookup/LookupSegmentTest.java b/processing/src/test/java/org/apache/druid/query/lookup/LookupSegmentTest.java index ce6035142374..3ca72aa5a698 100644 --- a/processing/src/test/java/org/apache/druid/query/lookup/LookupSegmentTest.java +++ b/processing/src/test/java/org/apache/druid/query/lookup/LookupSegmentTest.java @@ -137,9 +137,8 @@ public void test_asStorageAdapter_getColumnCapabilitiesK() // Note: the "k" column does not actually have multiple values, but the RowBasedStorageAdapter doesn't allow // reporting complete single-valued capabilities. It would be good to change this in the future, so query engines // running on top of lookups can take advantage of singly-valued optimizations. - Assert.assertFalse(capabilities.hasMultipleValues()); + Assert.assertTrue(capabilities.hasMultipleValues().isUnknown()); Assert.assertFalse(capabilities.isDictionaryEncoded()); - Assert.assertFalse(capabilities.isComplete()); } @Test @@ -151,9 +150,8 @@ public void test_asStorageAdapter_getColumnCapabilitiesV() // reporting complete single-valued capabilities. It would be good to change this in the future, so query engines // running on top of lookups can take advantage of singly-valued optimizations. Assert.assertEquals(ValueType.STRING, capabilities.getType()); - Assert.assertFalse(capabilities.hasMultipleValues()); + Assert.assertTrue(capabilities.hasMultipleValues().isUnknown()); Assert.assertFalse(capabilities.isDictionaryEncoded()); - Assert.assertFalse(capabilities.isComplete()); } @Test diff --git a/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataQueryTest.java b/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataQueryTest.java index 5f044ec339f6..9da06719d9c2 100644 --- a/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataQueryTest.java +++ b/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataQueryTest.java @@ -227,7 +227,7 @@ public SegmentMetadataQueryTest( "placement", new ColumnAnalysis( ValueType.STRING.toString(), - !mmap1, + false, preferedSize1, 1, "preferred", @@ -268,7 +268,7 @@ public SegmentMetadataQueryTest( "placement", new ColumnAnalysis( ValueType.STRING.toString(), - !mmap2, + false, placementSize2, 1, null, @@ -304,7 +304,7 @@ public void testSegmentMetadataQueryWithRollupMerge() "placement", new ColumnAnalysis( ValueType.STRING.toString(), - !mmap1 || !mmap2, + false, 0, 0, null, @@ -372,7 +372,7 @@ public void testSegmentMetadataQueryWithHasMultipleValuesMerge() "placement", new ColumnAnalysis( ValueType.STRING.toString(), - !mmap1 || !mmap2, + false, 0, 1, null, @@ -440,7 +440,7 @@ public void testSegmentMetadataQueryWithComplexColumnMerge() "placement", new ColumnAnalysis( ValueType.STRING.toString(), - !mmap1 || !mmap2, + false, 0, 1, null, @@ -509,7 +509,7 @@ public void testSegmentMetadataQueryWithDefaultAnalysisMerge() } ColumnAnalysis analysis = new ColumnAnalysis( ValueType.STRING.toString(), - !mmap1 || !mmap2, + false, size1 + size2, 1, "preferred", @@ -530,7 +530,7 @@ public void testSegmentMetadataQueryWithDefaultAnalysisMerge2() } ColumnAnalysis analysis = new ColumnAnalysis( ValueType.STRING.toString(), - !mmap1 || !mmap2, + false, size1 + size2, 3, "spot", @@ -551,7 +551,7 @@ public void testSegmentMetadataQueryWithDefaultAnalysisMerge3() } ColumnAnalysis analysis = new ColumnAnalysis( ValueType.STRING.toString(), - !mmap1 || !mmap2, + false, size1 + size2, 9, "automotive", @@ -637,7 +637,7 @@ public void testSegmentMetadataQueryWithNoAnalysisTypesMerge() "placement", new ColumnAnalysis( ValueType.STRING.toString(), - !mmap1 || !mmap2, + false, 0, 0, null, @@ -699,7 +699,7 @@ public void testSegmentMetadataQueryWithAggregatorsMerge() "placement", new ColumnAnalysis( ValueType.STRING.toString(), - !mmap1 || !mmap2, + false, 0, 0, null, @@ -757,7 +757,7 @@ public void testSegmentMetadataQueryWithTimestampSpecMerge() "placement", new ColumnAnalysis( ValueType.STRING.toString(), - !mmap1 || !mmap2, + false, 0, 0, null, @@ -815,7 +815,7 @@ public void testSegmentMetadataQueryWithQueryGranularityMerge() "placement", new ColumnAnalysis( ValueType.STRING.toString(), - !mmap1 || !mmap2, + false, 0, 0, null, diff --git a/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataUnionQueryTest.java b/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataUnionQueryTest.java index ccfcce7e563f..55d8b18babd3 100644 --- a/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataUnionQueryTest.java +++ b/processing/src/test/java/org/apache/druid/query/metadata/SegmentMetadataUnionQueryTest.java @@ -102,7 +102,7 @@ public void testSegmentMetadataUnionQuery() "placement", new ColumnAnalysis( ValueType.STRING.toString(), - !mmap, + false, mmap ? 43524 : 43056, 1, "preferred", diff --git a/processing/src/test/java/org/apache/druid/segment/IndexMergerV9WithSpatialIndexTest.java b/processing/src/test/java/org/apache/druid/segment/IndexMergerV9WithSpatialIndexTest.java index 1c68a21568d3..b4525c018eee 100644 --- a/processing/src/test/java/org/apache/druid/segment/IndexMergerV9WithSpatialIndexTest.java +++ b/processing/src/test/java/org/apache/druid/segment/IndexMergerV9WithSpatialIndexTest.java @@ -48,6 +48,7 @@ import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.incremental.IncrementalIndexSchema; import org.apache.druid.segment.writeout.SegmentWriteOutMediumFactory; +import org.apache.druid.testing.InitializedNullHandlingTest; import org.joda.time.Interval; import org.junit.Test; import org.junit.runner.RunWith; @@ -66,7 +67,7 @@ /** */ @RunWith(Parameterized.class) -public class IndexMergerV9WithSpatialIndexTest +public class IndexMergerV9WithSpatialIndexTest extends InitializedNullHandlingTest { public static final int NUM_POINTS = 5000; diff --git a/processing/src/test/java/org/apache/druid/segment/QueryableIndexColumnCapabilitiesTest.java b/processing/src/test/java/org/apache/druid/segment/QueryableIndexColumnCapabilitiesTest.java index dc102e688164..c7783e992197 100644 --- a/processing/src/test/java/org/apache/druid/segment/QueryableIndexColumnCapabilitiesTest.java +++ b/processing/src/test/java/org/apache/druid/segment/QueryableIndexColumnCapabilitiesTest.java @@ -38,6 +38,7 @@ import org.apache.druid.query.aggregation.LongSumAggregatorFactory; import org.apache.druid.query.aggregation.hyperloglog.HyperUniquesAggregatorFactory; import org.apache.druid.segment.column.ColumnCapabilities; +import org.apache.druid.segment.column.ColumnCapabilitiesImpl; import org.apache.druid.segment.column.ColumnHolder; import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.incremental.IncrementalIndex; @@ -152,9 +153,12 @@ public void testStringColumn() Assert.assertTrue(caps.isDictionaryEncoded()); Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue()); Assert.assertTrue(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertFalse(caps.hasMultipleValues()); + // multi-value is unknown unless explicitly set to 'true' + Assert.assertTrue(caps.hasMultipleValues().isUnknown()); + // at index merge or query time we 'complete' the capabilities to take a snapshot of the current state, + // coercing any 'UNKNOWN' values to false + Assert.assertFalse(ColumnCapabilitiesImpl.snapshot(caps).hasMultipleValues().isMaybeTrue()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertTrue(caps.isComplete()); caps = MMAP_INDEX.getColumnHolder("d1").getCapabilities(); Assert.assertEquals(ValueType.STRING, caps.getType()); @@ -162,9 +166,8 @@ public void testStringColumn() Assert.assertTrue(caps.isDictionaryEncoded()); Assert.assertTrue(caps.areDictionaryValuesSorted().isTrue()); Assert.assertTrue(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertFalse(caps.hasMultipleValues()); + Assert.assertFalse(caps.hasMultipleValues().isMaybeTrue()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertTrue(caps.isComplete()); } @Test @@ -176,9 +179,8 @@ public void testMultiStringColumn() Assert.assertTrue(caps.isDictionaryEncoded()); Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue()); Assert.assertTrue(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertTrue(caps.hasMultipleValues()); + Assert.assertTrue(caps.hasMultipleValues().isTrue()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertTrue(caps.isComplete()); caps = MMAP_INDEX.getColumnHolder("d2").getCapabilities(); Assert.assertEquals(ValueType.STRING, caps.getType()); @@ -186,9 +188,8 @@ public void testMultiStringColumn() Assert.assertTrue(caps.isDictionaryEncoded()); Assert.assertTrue(caps.areDictionaryValuesSorted().isTrue()); Assert.assertTrue(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertTrue(caps.hasMultipleValues()); + Assert.assertTrue(caps.hasMultipleValues().isTrue()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertTrue(caps.isComplete()); } @Test @@ -206,8 +207,7 @@ private void assertNonStringColumnCapabilities(ColumnCapabilities caps, ValueTyp Assert.assertFalse(caps.isDictionaryEncoded()); Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue()); Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertFalse(caps.hasMultipleValues()); + Assert.assertFalse(caps.hasMultipleValues().isMaybeTrue()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertTrue(caps.isComplete()); } } diff --git a/processing/src/test/java/org/apache/druid/segment/RowBasedColumnSelectorFactoryTest.java b/processing/src/test/java/org/apache/druid/segment/RowBasedColumnSelectorFactoryTest.java index 8886c7d2d8f1..e12dac4743cd 100644 --- a/processing/src/test/java/org/apache/druid/segment/RowBasedColumnSelectorFactoryTest.java +++ b/processing/src/test/java/org/apache/druid/segment/RowBasedColumnSelectorFactoryTest.java @@ -54,9 +54,8 @@ public void testCapabilitiesTime() Assert.assertFalse(caps.isDictionaryEncoded()); Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue()); Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertFalse(caps.hasMultipleValues()); + Assert.assertFalse(caps.hasMultipleValues().isMaybeTrue()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertTrue(caps.isComplete()); } @Test @@ -69,9 +68,8 @@ public void testCapabilitiesString() Assert.assertFalse(caps.isDictionaryEncoded()); Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue()); Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertFalse(caps.hasMultipleValues()); + Assert.assertTrue(caps.hasMultipleValues().isUnknown()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertFalse(caps.isComplete()); } @Test @@ -84,9 +82,8 @@ public void testCapabilitiesLong() Assert.assertFalse(caps.isDictionaryEncoded()); Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue()); Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertFalse(caps.hasMultipleValues()); + Assert.assertFalse(caps.hasMultipleValues().isMaybeTrue()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertTrue(caps.isComplete()); } @Test @@ -99,9 +96,8 @@ public void testCapabilitiesFloat() Assert.assertFalse(caps.isDictionaryEncoded()); Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue()); Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertFalse(caps.hasMultipleValues()); + Assert.assertFalse(caps.hasMultipleValues().isMaybeTrue()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertTrue(caps.isComplete()); } @Test @@ -114,9 +110,8 @@ public void testCapabilitiesDouble() Assert.assertFalse(caps.isDictionaryEncoded()); Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue()); Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertFalse(caps.hasMultipleValues()); + Assert.assertFalse(caps.hasMultipleValues().isMaybeTrue()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertTrue(caps.isComplete()); } @Test @@ -129,9 +124,8 @@ public void testCapabilitiesComplex() Assert.assertFalse(caps.isDictionaryEncoded()); Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue()); Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertFalse(caps.hasMultipleValues()); + Assert.assertTrue(caps.hasMultipleValues().isUnknown()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertFalse(caps.isComplete()); } @Test diff --git a/processing/src/test/java/org/apache/druid/segment/RowBasedStorageAdapterTest.java b/processing/src/test/java/org/apache/druid/segment/RowBasedStorageAdapterTest.java index a4fc2f254b78..652a59033fab 100644 --- a/processing/src/test/java/org/apache/druid/segment/RowBasedStorageAdapterTest.java +++ b/processing/src/test/java/org/apache/druid/segment/RowBasedStorageAdapterTest.java @@ -332,8 +332,7 @@ public void test_getColumnCapabilities_float() final ColumnCapabilities capabilities = adapter.getColumnCapabilities(ValueType.FLOAT.name()); Assert.assertEquals(ValueType.FLOAT, capabilities.getType()); - Assert.assertFalse(capabilities.hasMultipleValues()); - Assert.assertTrue(capabilities.isComplete()); + Assert.assertFalse(capabilities.hasMultipleValues().isMaybeTrue()); } @Test @@ -343,8 +342,7 @@ public void test_getColumnCapabilities_double() final ColumnCapabilities capabilities = adapter.getColumnCapabilities(ValueType.DOUBLE.name()); Assert.assertEquals(ValueType.DOUBLE, capabilities.getType()); - Assert.assertFalse(capabilities.hasMultipleValues()); - Assert.assertTrue(capabilities.isComplete()); + Assert.assertFalse(capabilities.hasMultipleValues().isMaybeTrue()); } @Test @@ -354,8 +352,7 @@ public void test_getColumnCapabilities_long() final ColumnCapabilities capabilities = adapter.getColumnCapabilities(ValueType.LONG.name()); Assert.assertEquals(ValueType.LONG, capabilities.getType()); - Assert.assertFalse(capabilities.hasMultipleValues()); - Assert.assertTrue(capabilities.isComplete()); + Assert.assertFalse(capabilities.hasMultipleValues().isMaybeTrue()); } @Test @@ -369,8 +366,7 @@ public void test_getColumnCapabilities_string() // Note: unlike numeric types, STRING-typed columns might have multiple values, so they report as incomplete. It // would be good in the future to support some way of changing this, when it is known ahead of time that // multi-valuedness is definitely happening or is definitely impossible. - Assert.assertFalse(capabilities.hasMultipleValues()); - Assert.assertFalse(capabilities.isComplete()); + Assert.assertTrue(capabilities.hasMultipleValues().isUnknown()); } @Test @@ -382,8 +378,7 @@ public void test_getColumnCapabilities_complex() // Note: unlike numeric types, COMPLEX-typed columns report that they are incomplete. Assert.assertEquals(ValueType.COMPLEX, capabilities.getType()); - Assert.assertFalse(capabilities.hasMultipleValues()); - Assert.assertFalse(capabilities.isComplete()); + Assert.assertTrue(capabilities.hasMultipleValues().isUnknown()); } @Test diff --git a/processing/src/test/java/org/apache/druid/segment/column/ColumnCapabilitiesImplTest.java b/processing/src/test/java/org/apache/druid/segment/column/ColumnCapabilitiesImplTest.java index 53e93c7a3b54..e221edd9c73f 100644 --- a/processing/src/test/java/org/apache/druid/segment/column/ColumnCapabilitiesImplTest.java +++ b/processing/src/test/java/org/apache/druid/segment/column/ColumnCapabilitiesImplTest.java @@ -47,7 +47,7 @@ public void testSerde() throws Exception Assert.assertTrue(cc.isDictionaryEncoded()); Assert.assertFalse(cc.isRunLengthEncoded()); Assert.assertTrue(cc.hasSpatialIndexes()); - Assert.assertTrue(cc.hasMultipleValues()); + Assert.assertTrue(cc.hasMultipleValues().isTrue()); Assert.assertTrue(cc.hasBitmapIndexes()); Assert.assertFalse(cc.isFilterable()); } @@ -72,7 +72,7 @@ public void testDeserialization() throws Exception Assert.assertTrue(cc.isDictionaryEncoded()); Assert.assertTrue(cc.isRunLengthEncoded()); Assert.assertTrue(cc.hasSpatialIndexes()); - Assert.assertTrue(cc.hasMultipleValues()); + Assert.assertTrue(cc.hasMultipleValues().isTrue()); Assert.assertTrue(cc.hasBitmapIndexes()); Assert.assertFalse(cc.isFilterable()); } diff --git a/processing/src/test/java/org/apache/druid/segment/filter/ExtractionDimFilterTest.java b/processing/src/test/java/org/apache/druid/segment/filter/ExtractionDimFilterTest.java index c7a560c19616..3ed33b8e3fc6 100644 --- a/processing/src/test/java/org/apache/druid/segment/filter/ExtractionDimFilterTest.java +++ b/processing/src/test/java/org/apache/druid/segment/filter/ExtractionDimFilterTest.java @@ -36,6 +36,7 @@ import org.apache.druid.query.filter.SelectorDimFilter; import org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector; import org.apache.druid.segment.column.BitmapIndex; +import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.data.BitmapSerdeFactory; import org.apache.druid.segment.data.CloseableIndexed; import org.apache.druid.segment.data.ConciseBitmapSerdeFactory; @@ -146,9 +147,9 @@ public Iterator iterator() } @Override - public boolean hasMultipleValues(final String dimension) + public ColumnCapabilities.Capable hasMultipleValues(final String dimension) { - return true; + return ColumnCapabilities.Capable.TRUE; } @Override diff --git a/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java b/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java index 20cfdb786fb0..13cc45fa590d 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java @@ -137,9 +137,8 @@ public void getColumnCapabilitiesForStringColumn() Assert.assertEquals(ValueType.STRING, capabilities.getType()); Assert.assertTrue(capabilities.isDictionaryEncoded()); Assert.assertFalse(capabilities.hasBitmapIndexes()); - Assert.assertFalse(capabilities.hasMultipleValues()); + Assert.assertFalse(capabilities.hasMultipleValues().isMaybeTrue()); Assert.assertFalse(capabilities.hasSpatialIndexes()); - Assert.assertTrue(capabilities.isComplete()); } @Test @@ -149,9 +148,8 @@ public void getColumnCapabilitiesForLongColumn() Assert.assertEquals(ValueType.LONG, capabilities.getType()); Assert.assertFalse(capabilities.isDictionaryEncoded()); Assert.assertFalse(capabilities.hasBitmapIndexes()); - Assert.assertFalse(capabilities.hasMultipleValues()); + Assert.assertFalse(capabilities.hasMultipleValues().isMaybeTrue()); Assert.assertFalse(capabilities.hasSpatialIndexes()); - Assert.assertTrue(capabilities.isComplete()); } @Test diff --git a/processing/src/test/java/org/apache/druid/segment/virtual/ExpressionVirtualColumnTest.java b/processing/src/test/java/org/apache/druid/segment/virtual/ExpressionVirtualColumnTest.java index 526c63c60c17..16e090dc3d53 100644 --- a/processing/src/test/java/org/apache/druid/segment/virtual/ExpressionVirtualColumnTest.java +++ b/processing/src/test/java/org/apache/druid/segment/virtual/ExpressionVirtualColumnTest.java @@ -357,8 +357,7 @@ public ColumnCapabilities getColumnCapabilities(String column) { return new ColumnCapabilitiesImpl().setType(ValueType.STRING) .setHasMultipleValues(true) - .setDictionaryEncoded(true) - .setIsComplete(true); + .setDictionaryEncoded(true); } }; final BaseObjectColumnValueSelector selectorImplicit = @@ -814,9 +813,9 @@ public void testCapabilities() Assert.assertFalse(caps.isDictionaryEncoded()); Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue()); Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertTrue(caps.hasMultipleValues()); + Assert.assertTrue(caps.hasMultipleValues().isUnknown()); + Assert.assertTrue(caps.hasMultipleValues().isMaybeTrue()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertFalse(caps.isComplete()); caps = Z_CONCAT_X.capabilities("expr"); Assert.assertEquals(ValueType.STRING, caps.getType()); @@ -824,8 +823,8 @@ public void testCapabilities() Assert.assertFalse(caps.isDictionaryEncoded()); Assert.assertFalse(caps.areDictionaryValuesSorted().isTrue()); Assert.assertFalse(caps.areDictionaryValuesUnique().isTrue()); - Assert.assertTrue(caps.hasMultipleValues()); + Assert.assertTrue(caps.hasMultipleValues().isUnknown()); + Assert.assertTrue(caps.hasMultipleValues().isMaybeTrue()); Assert.assertFalse(caps.hasSpatialIndexes()); - Assert.assertFalse(caps.isComplete()); } } diff --git a/processing/src/test/java/org/apache/druid/segment/virtual/VirtualColumnsTest.java b/processing/src/test/java/org/apache/druid/segment/virtual/VirtualColumnsTest.java index b1a9eeeab14d..597dbb6ee4e4 100644 --- a/processing/src/test/java/org/apache/druid/segment/virtual/VirtualColumnsTest.java +++ b/processing/src/test/java/org/apache/druid/segment/virtual/VirtualColumnsTest.java @@ -417,7 +417,7 @@ public boolean isNull() @Override public ColumnCapabilities capabilities(String columnName) { - return new ColumnCapabilitiesImpl().setType(ValueType.LONG); + return ColumnCapabilitiesImpl.createSimpleNumericColumnCapabilities(ValueType.LONG); } @Override From c1cc0f8f8f88a3ce1b5a6cf56ea1b0746e45beb3 Mon Sep 17 00:00:00 2001 From: Mainak Ghosh Date: Fri, 5 Jun 2020 12:42:42 -0700 Subject: [PATCH 061/107] Empty partitionDimension has less rollup compared to when explicitly specified (#9861) * Empty partitionDimension has less rollup compared to the case when it is explicitly specified * Adding a unit test for the empty partitionDimension scenario. Fixing another test which was failing * Fixing CI Build Inspection Issue * Addressing all review comments * Updating the javadocs for the hash method in HashBasedNumberedShardSpec --- .../partition/HashBasedNumberedShardSpec.java | 10 +++ .../task/CachingLocalSegmentAllocator.java | 5 +- .../common/task/CachingSegmentAllocator.java | 1 - .../druid/indexing/common/task/IndexTask.java | 32 +------ ...nearlyPartitionedSequenceNameFunction.java | 1 - .../common/task/SegmentAllocators.java | 3 + .../indexing/common/task/ShardSpecs.java | 62 +++++++++++++ .../PartialHashSegmentGenerateTask.java | 1 + .../PartialRangeSegmentGenerateTask.java | 1 + .../indexing/common/task/IndexTaskTest.java | 4 +- ...itionCachingLocalSegmentAllocatorTest.java | 2 + .../indexing/common/task/ShardSpecsTest.java | 87 +++++++++++++++++++ ...itionCachingLocalSegmentAllocatorTest.java | 2 + 13 files changed, 174 insertions(+), 37 deletions(-) create mode 100644 indexing-service/src/main/java/org/apache/druid/indexing/common/task/ShardSpecs.java create mode 100644 indexing-service/src/test/java/org/apache/druid/indexing/common/task/ShardSpecsTest.java diff --git a/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpec.java index a03fddceaf3a..2d8f525d6fc9 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpec.java @@ -76,6 +76,16 @@ public boolean isInChunk(long timestamp, InputRow inputRow) return (((long) hash(timestamp, inputRow)) - getPartitionNum()) % getPartitions() == 0; } + /** + * This method calculates the hash based on whether {@param partitionDimensions} is null or not. + * If yes, then both {@param timestamp} and dimension columns in {@param inputRow} are used {@link Rows#toGroupKey} + * Or else, columns in {@param partitionDimensions} are used + * + * @param timestamp should be bucketed with query granularity + * @param inputRow row from input data + * + * @return hash value + */ protected int hash(long timestamp, InputRow inputRow) { final List groupKey = getGroupKey(timestamp, inputRow); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingLocalSegmentAllocator.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingLocalSegmentAllocator.java index 05f0a77a4f47..9040c9acd3f9 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingLocalSegmentAllocator.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingLocalSegmentAllocator.java @@ -26,9 +26,9 @@ import org.apache.druid.indexing.common.actions.LockListAction; import org.apache.druid.indexing.common.actions.SurrogateAction; import org.apache.druid.indexing.common.actions.TaskAction; -import org.apache.druid.indexing.common.task.IndexTask.ShardSpecs; import org.apache.druid.indexing.common.task.batch.parallel.SupervisorTaskAccess; import org.apache.druid.java.util.common.ISE; +import org.apache.druid.java.util.common.granularity.Granularity; import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; import org.apache.druid.timeline.partition.ShardSpec; import org.joda.time.Interval; @@ -71,6 +71,7 @@ Map> create( TaskToolbox toolbox, String dataSource, String taskId, + Granularity queryGranularity, @Nullable SupervisorTaskAccess supervisorTaskAccess, IntervalToSegmentIdsCreator intervalToSegmentIdsCreator ) throws IOException @@ -112,7 +113,7 @@ Map> create( sequenceNameToSegmentId.put(getSequenceName(interval, segmentIdentifier.getShardSpec()), segmentIdentifier); } } - shardSpecs = new ShardSpecs(shardSpecMap); + shardSpecs = new ShardSpecs(shardSpecMap, queryGranularity); } private static String findVersion(Map intervalToVersion, Interval interval) diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingSegmentAllocator.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingSegmentAllocator.java index 86ae307a196c..176d45e2d2c4 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingSegmentAllocator.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingSegmentAllocator.java @@ -19,7 +19,6 @@ package org.apache.druid.indexing.common.task; -import org.apache.druid.indexing.common.task.IndexTask.ShardSpecs; import org.apache.druid.segment.realtime.appenderator.SegmentAllocator; /** diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/IndexTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/IndexTask.java index 712340f4fd70..14d806b54c88 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/IndexTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/IndexTask.java @@ -108,7 +108,6 @@ import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.partition.HashBasedNumberedShardSpec; import org.apache.druid.timeline.partition.NumberedShardSpec; -import org.apache.druid.timeline.partition.ShardSpec; import org.apache.druid.utils.CircularBuffer; import org.codehaus.plexus.util.FileUtils; import org.joda.time.Interval; @@ -887,6 +886,7 @@ private TaskStatus generateAndPublishSegments( toolbox, getDataSource(), getId(), + dataSchema.getGranularitySpec().getQueryGranularity(), null, (CompletePartitionAnalysis) partitionAnalysis ); @@ -1013,36 +1013,6 @@ private static SegmentsAndCommitMetadata awaitPublish( } } - /** - * This class represents a map of (Interval, ShardSpec) and is used for easy shardSpec generation. - */ - static class ShardSpecs - { - private final Map> map; - - ShardSpecs(final Map> map) - { - this.map = map; - } - - /** - * Return a shardSpec for the given interval and input row. - * - * @param interval interval for shardSpec - * @param row input row - * - * @return a shardSpec - */ - ShardSpec getShardSpec(Interval interval, InputRow row) - { - final List shardSpecs = map.get(interval); - if (shardSpecs == null || shardSpecs.isEmpty()) { - throw new ISE("Failed to get shardSpec for interval[%s]", interval); - } - return shardSpecs.get(0).getLookup(shardSpecs).getShardSpec(row.getTimestampFromEpoch(), row); - } - } - private static InputFormat getInputFormat(IndexIngestionSpec ingestionSchema) { return ingestionSchema.getIOConfig().getNonNullInputFormat(); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/NonLinearlyPartitionedSequenceNameFunction.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/NonLinearlyPartitionedSequenceNameFunction.java index 3031a7a736bc..c34aa2995983 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/NonLinearlyPartitionedSequenceNameFunction.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/NonLinearlyPartitionedSequenceNameFunction.java @@ -20,7 +20,6 @@ package org.apache.druid.indexing.common.task; import org.apache.druid.data.input.InputRow; -import org.apache.druid.indexing.common.task.IndexTask.ShardSpecs; import org.apache.druid.timeline.partition.ShardSpec; import org.joda.time.Interval; diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SegmentAllocators.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SegmentAllocators.java index 3a6b1f51686f..de88c68f65a6 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SegmentAllocators.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SegmentAllocators.java @@ -23,6 +23,7 @@ import org.apache.druid.indexing.common.TaskToolbox; import org.apache.druid.indexing.common.task.batch.parallel.SupervisorTaskAccess; import org.apache.druid.indexing.common.task.batch.partition.CompletePartitionAnalysis; +import org.apache.druid.java.util.common.granularity.Granularity; import org.apache.druid.segment.indexing.DataSchema; import org.apache.druid.segment.realtime.appenderator.SegmentAllocator; @@ -77,6 +78,7 @@ public static CachingSegmentAllocator forNonLinearPartitioning( final TaskToolbox toolbox, final String dataSource, final String taskId, + final Granularity queryGranularity, final @Nullable SupervisorTaskAccess supervisorTaskAccess, final CompletePartitionAnalysis partitionAnalysis ) throws IOException @@ -85,6 +87,7 @@ public static CachingSegmentAllocator forNonLinearPartitioning( toolbox, dataSource, taskId, + queryGranularity, supervisorTaskAccess, partitionAnalysis::convertToIntervalToSegmentIds ); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/ShardSpecs.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/ShardSpecs.java new file mode 100644 index 000000000000..42f7ce1cd531 --- /dev/null +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/ShardSpecs.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.indexing.common.task; + +import org.apache.druid.data.input.InputRow; +import org.apache.druid.java.util.common.ISE; +import org.apache.druid.java.util.common.granularity.Granularity; +import org.apache.druid.timeline.partition.ShardSpec; +import org.joda.time.Interval; + +import java.util.List; +import java.util.Map; + +/** + * This class represents a map of (Interval, ShardSpec) and is used for easy shardSpec generation. + */ +public class ShardSpecs +{ + private final Map> map; + private Granularity queryGranularity; + + ShardSpecs(final Map> map, Granularity queryGranularity) + { + this.map = map; + this.queryGranularity = queryGranularity; + } + + /** + * Return a shardSpec for the given interval and input row. + * + * @param interval interval for shardSpec + * @param row input row + * + * @return a shardSpec + */ + ShardSpec getShardSpec(Interval interval, InputRow row) + { + final List shardSpecs = map.get(interval); + if (shardSpecs == null || shardSpecs.isEmpty()) { + throw new ISE("Failed to get shardSpec for interval[%s]", interval); + } + final long truncatedTimestamp = queryGranularity.bucketStart(row.getTimestamp()).getMillis(); + return shardSpecs.get(0).getLookup(shardSpecs).getShardSpec(truncatedTimestamp, row); + } +} diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateTask.java index 7be462f7840b..bf33faef292e 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateTask.java @@ -137,6 +137,7 @@ CachingSegmentAllocator createSegmentAllocator(TaskToolbox toolbox, ParallelInde toolbox, getDataSource(), getId(), + granularitySpec.getQueryGranularity(), new SupervisorTaskAccess(supervisorTaskId, taskClient), createHashPartitionAnalysisFromPartitionsSpec(granularitySpec, partitionsSpec) ); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateTask.java index 4236226f3eb6..60bbb7aac9c8 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateTask.java @@ -161,6 +161,7 @@ CachingSegmentAllocator createSegmentAllocator(TaskToolbox toolbox, ParallelInde toolbox, getDataSource(), getId(), + ingestionSchema.getDataSchema().getGranularitySpec().getQueryGranularity(), new SupervisorTaskAccess(supervisorTaskId, taskClient), partitionAnalysis ); diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java index 2cb48e67ba3f..8668a937aa18 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java @@ -1561,8 +1561,8 @@ public void testCsvWithHeaderOfEmptyColumns() throws Exception Assert.assertTrue( StringUtils.format("Actual dimensions: %s", dimensions), - dimensions.equals(Sets.newHashSet("dim", "column_3")) || - dimensions.equals(Sets.newHashSet("column_2", "column_3")) + dimensions.equals(Sets.newHashSet("column_2")) || + dimensions.equals(Sets.newHashSet("dim", "column_2", "column_3")) ); Assert.assertEquals(Collections.singletonList("val"), segment.getMetrics()); diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RangePartitionCachingLocalSegmentAllocatorTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RangePartitionCachingLocalSegmentAllocatorTest.java index 855698df84fc..12a16152fcb1 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RangePartitionCachingLocalSegmentAllocatorTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RangePartitionCachingLocalSegmentAllocatorTest.java @@ -30,6 +30,7 @@ import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.java.util.common.granularity.NoneGranularity; import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.partition.PartitionBoundaries; @@ -104,6 +105,7 @@ public void setup() throws IOException toolbox, DATASOURCE, TASKID, + new NoneGranularity(), new SupervisorTaskAccessWithNullClient(SUPERVISOR_TASKID), partitionAnalysis ); diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/ShardSpecsTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/ShardSpecsTest.java new file mode 100644 index 000000000000..64b0a0cad15f --- /dev/null +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/ShardSpecsTest.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.indexing.common.task; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import org.apache.druid.data.input.InputRow; +import org.apache.druid.data.input.MapBasedInputRow; +import org.apache.druid.indexing.common.TestUtils; +import org.apache.druid.java.util.common.DateTimes; +import org.apache.druid.java.util.common.Intervals; +import org.apache.druid.java.util.common.granularity.Granularities; +import org.apache.druid.timeline.partition.HashBasedNumberedShardSpec; +import org.apache.druid.timeline.partition.ShardSpec; +import org.joda.time.Interval; +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ShardSpecsTest extends IngestionTestBase +{ + private final TestUtils testUtils = new TestUtils(); + private final ObjectMapper jsonMapper = testUtils.getTestObjectMapper(); + + public ShardSpecsTest() + { + } + + @Test + public void testShardSpecSelectionWithNullPartitionDimension() + { + ShardSpec spec1 = new HashBasedNumberedShardSpec(0, 2, null, jsonMapper); + ShardSpec spec2 = new HashBasedNumberedShardSpec(1, 2, null, jsonMapper); + + Map> shardSpecMap = new HashMap<>(); + shardSpecMap.put(Intervals.of("2014-01-01T00:00:00.000Z/2014-01-02T00:00:00.000Z"), Lists.newArrayList(spec1, spec2)); + + ShardSpecs shardSpecs = new ShardSpecs(shardSpecMap, Granularities.HOUR); + String visitorId = "visitorId"; + String clientType = "clientType"; + long timestamp1 = DateTimes.of("2014-01-01T00:00:00.000Z").getMillis(); + InputRow row1 = new MapBasedInputRow(timestamp1, + Lists.newArrayList(visitorId, clientType), + ImmutableMap.of(visitorId, "0", clientType, "iphone") + ); + + long timestamp2 = DateTimes.of("2014-01-01T00:30:20.456Z").getMillis(); + InputRow row2 = new MapBasedInputRow(timestamp2, + Lists.newArrayList(visitorId, clientType), + ImmutableMap.of(visitorId, "0", clientType, "iphone") + ); + + long timestamp3 = DateTimes.of("2014-01-01T10:10:20.456Z").getMillis(); + InputRow row3 = new MapBasedInputRow(timestamp3, + Lists.newArrayList(visitorId, clientType), + ImmutableMap.of(visitorId, "0", clientType, "iphone") + ); + + ShardSpec spec3 = shardSpecs.getShardSpec(Intervals.of("2014-01-01T00:00:00.000Z/2014-01-02T00:00:00.000Z"), row1); + ShardSpec spec4 = shardSpecs.getShardSpec(Intervals.of("2014-01-01T00:00:00.000Z/2014-01-02T00:00:00.000Z"), row2); + ShardSpec spec5 = shardSpecs.getShardSpec(Intervals.of("2014-01-01T00:00:00.000Z/2014-01-02T00:00:00.000Z"), row3); + + Assert.assertSame(true, spec3 == spec4); + Assert.assertSame(false, spec3 == spec5); + } +} diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionCachingLocalSegmentAllocatorTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionCachingLocalSegmentAllocatorTest.java index 5dc9560d57fd..16c20b342acc 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionCachingLocalSegmentAllocatorTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionCachingLocalSegmentAllocatorTest.java @@ -37,6 +37,7 @@ import org.apache.druid.indexing.common.task.batch.partition.HashPartitionAnalysis; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.java.util.common.granularity.NoneGranularity; import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.partition.HashBasedNumberedShardSpec; @@ -82,6 +83,7 @@ public void setup() throws IOException toolbox, DATASOURCE, TASKID, + new NoneGranularity(), new SupervisorTaskAccessWithNullClient(SUPERVISOR_TASKID), partitionAnalysis ); From 4e339be6c35ab65075db334573bf95c9d0ad907b Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn <52679095+maytasm@users.noreply.github.com> Date: Fri, 5 Jun 2020 11:19:42 -1000 Subject: [PATCH 062/107] Add git pre-commit hook to source control (#9554) * Add git pre-commit hook to source control * Changed hook to pre-push and simply hook to run all checkstyle * Clean up setup-hooks * Add apache header * Add apache header * add documentation to intellij-setup.md * retrigger tests * update Co-authored-by: Maytas Monsereenusorn <52679095+maytasm3@users.noreply.github.com> --- dev/intellij-setup.md | 5 +++++ hooks/pre-push.sh | 17 +++++++++++++++++ setup-hooks.sh | 17 +++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100755 hooks/pre-push.sh create mode 100755 setup-hooks.sh diff --git a/dev/intellij-setup.md b/dev/intellij-setup.md index 9d455d1fd995..1cedeb0d156d 100644 --- a/dev/intellij-setup.md +++ b/dev/intellij-setup.md @@ -34,6 +34,11 @@ an alias name. You can do this in Using `File` -> `Project Structure...` -> `Pla ## Code Style The Code Style is available in XML format at [druid_intellij_formatting.xml](druid_intellij_formatting.xml) and can be [imported into IntelliJ](https://www.jetbrains.com/help/idea/2017.1/copying-code-style-settings.html). +## Git Checkstyle Verification Hook (Optional) +Git Checkstyle pre-commit hook can be installed to automatically run checkstyle verification before committing, +saving cycle from avoiding the checkstyle failing later in Travis/CI environment. +The hook can be setup easily by running the /setup-hooks.sh script. + ## Metadata The installation of a MySQL metadata store is outside the scope of this document, but instructions on setting up MySQL can be found at [docs/development/extensions-core/mysql.md](/docs/development/extensions-core/mysql.md). This assumes you followed the example there and have a database named `druid` with proper permissions for a user named `druid` and a password of `diurd`. diff --git a/hooks/pre-push.sh b/hooks/pre-push.sh new file mode 100755 index 000000000000..a0928db75baa --- /dev/null +++ b/hooks/pre-push.sh @@ -0,0 +1,17 @@ +#!/bin/bash -eu +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +mvn checkstyle:checkstyle --fail-at-end diff --git a/setup-hooks.sh b/setup-hooks.sh new file mode 100755 index 000000000000..85c60d1a7250 --- /dev/null +++ b/setup-hooks.sh @@ -0,0 +1,17 @@ +#!/bin/bash -eu +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ln -s ../../hooks/pre-push.sh .git/hooks/pre-push From c7918904067a0686ddbd05040c45461a97edd771 Mon Sep 17 00:00:00 2001 From: Yuanli Han <44718283+yuanlihan@users.noreply.github.com> Date: Tue, 9 Jun 2020 00:54:39 +0800 Subject: [PATCH 063/107] Fix compact partially overlapping segments (#9905) * fix compact overlapping segments * fix comment * fix CI failure --- .../indexing/common/task/CompactionTask.java | 47 +++++++++++-------- .../common/task/CompactionTaskTest.java | 29 +++++++----- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CompactionTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CompactionTask.java index dac3bd9a0952..59c9019e6397 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CompactionTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CompactionTask.java @@ -61,6 +61,7 @@ import org.apache.druid.indexing.input.DruidInputSource; import org.apache.druid.indexing.overlord.Segments; import org.apache.druid.java.util.common.ISE; +import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.JodaUtils; import org.apache.druid.java.util.common.Pair; import org.apache.druid.java.util.common.RE; @@ -98,11 +99,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.TreeMap; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -524,10 +523,30 @@ static List createIngestionSchema( .add(p) ); - final List specs = new ArrayList<>(intervalToSegments.size()); - for (Entry>> entry : intervalToSegments.entrySet()) { - final Interval interval = entry.getKey(); - final List> segmentsToCompact = entry.getValue(); + // unify overlapping intervals to ensure overlapping segments compacting in the same indexSpec + List>>> intervalToSegmentsUnified = new ArrayList<>(); + Interval union = null; + List> segments = new ArrayList<>(); + for (Map.Entry>> entry : intervalToSegments.entrySet()) { + Interval cur = entry.getKey(); + if (union == null) { + union = cur; + segments.addAll(entry.getValue()); + } else if (union.overlaps(cur)) { + union = Intervals.utc(union.getStartMillis(), Math.max(union.getEndMillis(), cur.getEndMillis())); + segments.addAll(entry.getValue()); + } else { + intervalToSegmentsUnified.add(Pair.of(union, segments)); + union = cur; + segments = new ArrayList<>(entry.getValue()); + } + } + intervalToSegmentsUnified.add(Pair.of(union, segments)); + + final List specs = new ArrayList<>(intervalToSegmentsUnified.size()); + for (Pair>> entry : intervalToSegmentsUnified) { + final Interval interval = entry.lhs; + final List> segmentsToCompact = entry.rhs; final DataSchema dataSchema = createDataSchema( segmentProvider.dataSource, segmentsToCompact, @@ -710,20 +729,8 @@ private static DimensionsSpec createDimensionsSpec(List intervalComparator = Comparators.intervalsByStartThenEnd(); - for (int i = 0; i < queryableIndices.size() - 1; i++) { - final Interval shouldBeSmaller = queryableIndices.get(i).lhs.getDataInterval(); - final Interval shouldBeLarger = queryableIndices.get(i + 1).lhs.getDataInterval(); - Preconditions.checkState( - intervalComparator.compare(shouldBeSmaller, shouldBeLarger) <= 0, - "QueryableIndexes are not sorted! Interval[%s] of segment[%s] is laster than interval[%s] of segment[%s]", - shouldBeSmaller, - queryableIndices.get(i).rhs.getId(), - shouldBeLarger, - queryableIndices.get(i + 1).rhs.getId() - ); - } + // sort timelineSegments in order of interval, see https://github.com/apache/druid/pull/9905 + queryableIndices.sort((o1, o2) -> Comparators.intervalsByStartThenEnd().compare(o1.rhs.getInterval(), o2.rhs.getInterval())); int index = 0; for (Pair pair : Lists.reverse(queryableIndices)) { diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskTest.java index 1d4bec4f99e1..66ee8595d25d 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskTest.java @@ -73,7 +73,6 @@ import org.apache.druid.jackson.DefaultObjectMapper; import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.Intervals; -import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.java.util.common.granularity.Granularities; import org.apache.druid.java.util.common.granularity.Granularity; import org.apache.druid.java.util.common.granularity.PeriodGranularity; @@ -160,7 +159,11 @@ public class CompactionTaskTest Intervals.of("2017-03-01/2017-04-01"), Intervals.of("2017-04-01/2017-05-01"), Intervals.of("2017-05-01/2017-06-01"), - Intervals.of("2017-06-01/2017-07-01") + Intervals.of("2017-06-01/2017-07-01"), + // overlapping intervals + Intervals.of("2017-06-01/2017-06-02"), + Intervals.of("2017-06-15/2017-06-16"), + Intervals.of("2017-06-30/2017-07-01") ); private static final Map MIXED_TYPE_COLUMN_MAP = new HashMap<>(); private static final ParallelIndexTuningConfig TUNING_CONFIG = createTuningConfig(); @@ -191,12 +194,17 @@ public static void setupClass() MIXED_TYPE_COLUMN_MAP.put(Intervals.of("2017-05-01/2017-06-01"), new DoubleDimensionSchema(MIXED_TYPE_COLUMN)); MIXED_TYPE_COLUMN_MAP.put(Intervals.of("2017-06-01/2017-07-01"), new DoubleDimensionSchema(MIXED_TYPE_COLUMN)); + MIXED_TYPE_COLUMN_MAP.put(Intervals.of("2017-06-01/2017-06-02"), new DoubleDimensionSchema(MIXED_TYPE_COLUMN)); + MIXED_TYPE_COLUMN_MAP.put(Intervals.of("2017-06-15/2017-06-16"), new DoubleDimensionSchema(MIXED_TYPE_COLUMN)); + MIXED_TYPE_COLUMN_MAP.put(Intervals.of("2017-06-30/2017-07-01"), new DoubleDimensionSchema(MIXED_TYPE_COLUMN)); + DIMENSIONS = new HashMap<>(); AGGREGATORS = new ArrayList<>(); DIMENSIONS.put(ColumnHolder.TIME_COLUMN_NAME, new LongDimensionSchema(ColumnHolder.TIME_COLUMN_NAME)); DIMENSIONS.put(TIMESTAMP_COLUMN, new LongDimensionSchema(TIMESTAMP_COLUMN)); - for (int i = 0; i < SEGMENT_INTERVALS.size(); i++) { + int numUmbrellaIntervals = 6; + for (int i = 0; i < numUmbrellaIntervals; i++) { final StringDimensionSchema schema = new StringDimensionSchema( "string_dim_" + i, null, @@ -204,15 +212,15 @@ public static void setupClass() ); DIMENSIONS.put(schema.getName(), schema); } - for (int i = 0; i < SEGMENT_INTERVALS.size(); i++) { + for (int i = 0; i < numUmbrellaIntervals; i++) { final LongDimensionSchema schema = new LongDimensionSchema("long_dim_" + i); DIMENSIONS.put(schema.getName(), schema); } - for (int i = 0; i < SEGMENT_INTERVALS.size(); i++) { + for (int i = 0; i < numUmbrellaIntervals; i++) { final FloatDimensionSchema schema = new FloatDimensionSchema("float_dim_" + i); DIMENSIONS.put(schema.getName(), schema); } - for (int i = 0; i < SEGMENT_INTERVALS.size(); i++) { + for (int i = 0; i < numUmbrellaIntervals; i++) { final DoubleDimensionSchema schema = new DoubleDimensionSchema("double_dim_" + i); DIMENSIONS.put(schema.getName(), schema); } @@ -224,14 +232,13 @@ public static void setupClass() AGGREGATORS.add(new DoubleLastAggregatorFactory("agg_4", "double_dim_4")); for (int i = 0; i < SEGMENT_INTERVALS.size(); i++) { - final Interval segmentInterval = Intervals.of(StringUtils.format("2017-0%d-01/2017-0%d-01", (i + 1), (i + 2))); SEGMENT_MAP.put( new DataSegment( DATA_SOURCE, - segmentInterval, - "version", + SEGMENT_INTERVALS.get(i), + "version_" + i, ImmutableMap.of(), - findDimensions(i, segmentInterval), + findDimensions(i, SEGMENT_INTERVALS.get(i)), AGGREGATORS.stream().map(AggregatorFactory::getName).collect(Collectors.toList()), new NumberedShardSpec(0, 1), 0, @@ -285,7 +292,7 @@ private static List findDimensions(int startIndex, Interval segmentInter dimensions.add(TIMESTAMP_COLUMN); for (int i = 0; i < 6; i++) { int postfix = i + startIndex; - postfix = postfix >= 6 ? postfix - 6 : postfix; + postfix = postfix % 6; dimensions.add("string_dim_" + postfix); dimensions.add("long_dim_" + postfix); dimensions.add("float_dim_" + postfix); From 85f81868c59702ac26f80ebe7366749fc8ffc37a Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Mon, 8 Jun 2020 17:40:29 -0700 Subject: [PATCH 064/107] fix NilVectorSelector filter optimization (#9989) --- .../segment/vector/NilVectorSelector.java | 2 +- .../segment/filter/ValueMatchersTest.java | 152 ++++++++++++++++++ 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 processing/src/test/java/org/apache/druid/segment/filter/ValueMatchersTest.java diff --git a/processing/src/main/java/org/apache/druid/segment/vector/NilVectorSelector.java b/processing/src/main/java/org/apache/druid/segment/vector/NilVectorSelector.java index 3aeb32c1f262..9cc6dea06760 100644 --- a/processing/src/main/java/org/apache/druid/segment/vector/NilVectorSelector.java +++ b/processing/src/main/java/org/apache/druid/segment/vector/NilVectorSelector.java @@ -142,7 +142,7 @@ public String lookupName(final int id) @Override public boolean nameLookupPossibleInAdvance() { - return false; + return true; } @Nullable diff --git a/processing/src/test/java/org/apache/druid/segment/filter/ValueMatchersTest.java b/processing/src/test/java/org/apache/druid/segment/filter/ValueMatchersTest.java new file mode 100644 index 000000000000..6698c3ab6ac1 --- /dev/null +++ b/processing/src/test/java/org/apache/druid/segment/filter/ValueMatchersTest.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.filter; + +import com.google.common.collect.ImmutableList; +import org.apache.druid.segment.DimensionSelector; +import org.apache.druid.segment.SimpleAscendingOffset; +import org.apache.druid.segment.data.GenericIndexed; +import org.apache.druid.segment.data.VSizeColumnarInts; +import org.apache.druid.segment.data.VSizeColumnarMultiInts; +import org.apache.druid.segment.serde.DictionaryEncodedColumnSupplier; +import org.apache.druid.segment.vector.NilVectorSelector; +import org.apache.druid.segment.vector.NoFilterVectorOffset; +import org.apache.druid.testing.InitializedNullHandlingTest; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class ValueMatchersTest extends InitializedNullHandlingTest +{ + private DictionaryEncodedColumnSupplier supplierSingleConstant; + private DictionaryEncodedColumnSupplier supplierSingle; + private DictionaryEncodedColumnSupplier supplierMulti; + + @Before + public void setup() + { + supplierSingleConstant = new DictionaryEncodedColumnSupplier( + GenericIndexed.fromIterable(ImmutableList.of("value"), GenericIndexed.STRING_STRATEGY), + () -> VSizeColumnarInts.fromArray(new int[]{0}), + null, + 0 + ); + supplierSingle = new DictionaryEncodedColumnSupplier( + GenericIndexed.fromIterable(ImmutableList.of("value", "value2"), GenericIndexed.STRING_STRATEGY), + () -> VSizeColumnarInts.fromArray(new int[]{0, 0, 1, 0, 1}), + null, + 0 + ); + supplierMulti = new DictionaryEncodedColumnSupplier( + GenericIndexed.fromIterable(ImmutableList.of("value"), GenericIndexed.STRING_STRATEGY), + null, + () -> VSizeColumnarMultiInts.fromIterable( + ImmutableList.of( + VSizeColumnarInts.fromArray(new int[]{0, 0}), + VSizeColumnarInts.fromArray(new int[]{0}) + ) + ), + 0 + ); + } + @Test + public void testNullDimensionSelectorCanBeBoolean() + { + Boolean resultMatchNull = ValueMatchers.toBooleanIfPossible( + DimensionSelector.constant(null), + false, + string -> string == null + ); + Assert.assertNotNull(resultMatchNull); + Assert.assertTrue(resultMatchNull); + + Boolean resultMatchNotNull = ValueMatchers.toBooleanIfPossible( + DimensionSelector.constant(null), + false, + string -> string != null + ); + Assert.assertNotNull(resultMatchNotNull); + Assert.assertFalse(resultMatchNotNull); + + Boolean resultMatchNonNilConstant = ValueMatchers.toBooleanIfPossible( + supplierSingleConstant.get().makeDimensionSelector(new SimpleAscendingOffset(1), null), + false, + string -> string != null + ); + Assert.assertNotNull(resultMatchNonNilConstant); + Assert.assertTrue(resultMatchNonNilConstant); + + Boolean resultMatchNonNil = ValueMatchers.toBooleanIfPossible( + supplierSingle.get().makeDimensionSelector(new SimpleAscendingOffset(1), null), + false, + string -> string != null + ); + Assert.assertNull(resultMatchNonNil); + + Boolean resultMatchNonNilMulti = ValueMatchers.toBooleanIfPossible( + supplierMulti.get().makeDimensionSelector(new SimpleAscendingOffset(1), null), + true, + string -> string != null + ); + Assert.assertNull(resultMatchNonNilMulti); + } + + @Test + public void testNilVectorSelectorCanBeBoolean() + { + Boolean resultMatchNull = ValueMatchers.toBooleanIfPossible( + NilVectorSelector.create(new NoFilterVectorOffset(10, 0, 100)), + false, + string -> string == null + ); + Assert.assertNotNull(resultMatchNull); + Assert.assertTrue(resultMatchNull); + + Boolean resultMatchNotNull = ValueMatchers.toBooleanIfPossible( + NilVectorSelector.create(new NoFilterVectorOffset(10, 0, 100)), + false, + string -> string != null + ); + Assert.assertNotNull(resultMatchNotNull); + Assert.assertFalse(resultMatchNotNull); + + Boolean resultMatchNotNilConstant = ValueMatchers.toBooleanIfPossible( + supplierSingleConstant.get().makeSingleValueDimensionVectorSelector(new NoFilterVectorOffset(10, 0, 1)), + false, + string -> string != null + ); + Assert.assertNotNull(resultMatchNotNilConstant); + Assert.assertTrue(resultMatchNotNilConstant); + + Boolean resultMatchNotNil = ValueMatchers.toBooleanIfPossible( + supplierSingle.get().makeSingleValueDimensionVectorSelector(new NoFilterVectorOffset(10, 0, 1)), + false, + string -> string != null + ); + Assert.assertNull(resultMatchNotNil); + + Boolean resultMatchNotNilMulti = ValueMatchers.toBooleanIfPossible( + supplierMulti.get().makeSingleValueDimensionVectorSelector(new NoFilterVectorOffset(10, 0, 1)), + true, + string -> string != null + ); + Assert.assertNull(resultMatchNotNilMulti); + } +} From 1ab2fd1f6d35f96ad1b731882ac35669f7777ba1 Mon Sep 17 00:00:00 2001 From: Jonathan Wei Date: Mon, 8 Jun 2020 20:15:59 -0700 Subject: [PATCH 065/107] Load broadcast datasources on broker and tasks (#9971) * Load broadcast datasources on broker and tasks * Add javadocs * Support HTTP segment management * Fix indexer maxSize * inspection fix * Make segment cache optional on non-historicals * Fix build * Fix inspections, some coverage, failed tests * More tests * Add CliIndexer to MainTest * Fix inspection * Rename UnprunedDataSegment to LoadableDataSegment * Address PR comments * Fix --- .../druid/indexing/kafka/KafkaIndexTask.java | 6 + .../indexing/kafka/KafkaIndexTaskTest.java | 1 + .../indexing/kinesis/KinesisIndexTask.java | 6 + .../kinesis/KinesisIndexTaskTest.java | 2 +- .../indexing/common/task/AbstractTask.java | 6 + .../AppenderatorDriverRealtimeIndexTask.java | 6 + .../common/task/RealtimeIndexTask.java | 6 + .../druid/indexing/common/task/Task.java | 5 + .../indexing/overlord/ForkingTaskRunner.java | 7 + ...penderatorDriverRealtimeIndexTaskTest.java | 1 + .../indexing/common/task/IndexTaskTest.java | 2 + .../common/task/RealtimeIndexTaskTest.java | 6 + server/pom.xml | 4 - .../druid/client/CachingClusteredClient.java | 4 +- .../org/apache/druid/client/DruidServer.java | 14 +- .../discovery/DruidNodeDiscoveryProvider.java | 3 +- .../segment/loading/SegmentLoaderConfig.java | 5 +- .../SegmentLoaderLocalCacheManager.java | 1 - ...egmentServerAnnouncerLifecycleHandler.java | 104 ---------- ...oordinatorBasedSegmentHandoffNotifier.java | 2 +- .../coordination/DruidServerMetadata.java | 12 +- .../coordination/LoadableDataSegment.java | 81 ++++++++ .../SegmentChangeRequestLoad.java | 14 +- .../coordination/SegmentLoadDropHandler.java | 28 ++- .../druid/server/coordination/ServerType.java | 8 + .../server/coordinator/BalancerStrategy.java | 9 +- .../CachingCostBalancerStrategyFactory.java | 6 +- .../coordinator/CostBalancerStrategy.java | 8 +- .../server/coordinator/DruidCluster.java | 45 +++- .../server/coordinator/DruidCoordinator.java | 2 +- .../DruidCoordinatorRuntimeParams.java | 23 ++- .../coordinator/RandomBalancerStrategy.java | 5 +- .../coordinator/ReservoirSegmentSampler.java | 16 +- .../server/coordinator/ServerHolder.java | 5 + .../coordinator/duty/BalanceSegments.java | 5 +- .../server/coordinator/duty/RunRules.java | 14 +- .../rules/BroadcastDistributionRule.java | 52 ++--- .../ForeverBroadcastDistributionRule.java | 21 +- .../IntervalBroadcastDistributionRule.java | 28 +-- .../PeriodBroadcastDistributionRule.java | 29 +-- .../server/http/DataSourcesResource.java | 2 +- ...inatorBasedSegmentHandoffNotifierTest.java | 2 +- .../SegmentLoadDropHandlerTest.java | 193 +++++++++++++----- .../coordination/ZkCoordinatorTest.java | 68 +++++- .../coordinator/BalanceSegmentsTest.java | 45 +++- .../coordinator/DruidClusterBuilder.java | 9 +- .../ReservoirSegmentSamplerTest.java | 7 +- .../BroadcastDistributionRuleSerdeTest.java | 19 +- .../rules/BroadcastDistributionRuleTest.java | 18 +- .../java/org/apache/druid/cli/CliBroker.java | 17 +- .../java/org/apache/druid/cli/CliIndexer.java | 20 +- .../java/org/apache/druid/cli/CliPeon.java | 32 +-- .../java/org/apache/druid/cli/MainTest.java | 4 +- .../druid/sql/calcite/schema/DruidSchema.java | 6 +- .../druid/sql/calcite/CalciteQueryTest.java | 2 +- 55 files changed, 699 insertions(+), 347 deletions(-) delete mode 100644 server/src/main/java/org/apache/druid/segment/realtime/CliIndexerDataSegmentServerAnnouncerLifecycleHandler.java create mode 100644 server/src/main/java/org/apache/druid/server/coordination/LoadableDataSegment.java diff --git a/extensions-core/kafka-indexing-service/src/main/java/org/apache/druid/indexing/kafka/KafkaIndexTask.java b/extensions-core/kafka-indexing-service/src/main/java/org/apache/druid/indexing/kafka/KafkaIndexTask.java index 9f7f9977efb2..6bf40866b5ea 100644 --- a/extensions-core/kafka-indexing-service/src/main/java/org/apache/druid/indexing/kafka/KafkaIndexTask.java +++ b/extensions-core/kafka-indexing-service/src/main/java/org/apache/druid/indexing/kafka/KafkaIndexTask.java @@ -191,4 +191,10 @@ public String getType() { return TYPE; } + + @Override + public boolean supportsQueries() + { + return true; + } } diff --git a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/KafkaIndexTaskTest.java b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/KafkaIndexTaskTest.java index 46a6a0418ba9..eb41749c33ad 100644 --- a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/KafkaIndexTaskTest.java +++ b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/KafkaIndexTaskTest.java @@ -345,6 +345,7 @@ public void testRunAfterDataInserted() throws Exception INPUT_FORMAT ) ); + Assert.assertTrue(task.supportsQueries()); final ListenableFuture future = runTask(task); diff --git a/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisIndexTask.java b/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisIndexTask.java index bfd375830497..9eee3bf6274d 100644 --- a/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisIndexTask.java +++ b/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisIndexTask.java @@ -137,6 +137,12 @@ public String getType() return TYPE; } + @Override + public boolean supportsQueries() + { + return true; + } + @VisibleForTesting AWSCredentialsConfig getAwsCredentialsConfig() { diff --git a/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisIndexTaskTest.java b/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisIndexTaskTest.java index 142f81f21016..48ca434e7fd8 100644 --- a/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisIndexTaskTest.java +++ b/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisIndexTaskTest.java @@ -347,8 +347,8 @@ public void testRunAfterDataInserted() throws Exception null, false ) - ); + Assert.assertTrue(task.supportsQueries()); final ListenableFuture future = runTask(task); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AbstractTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AbstractTask.java index 40745bfe32f5..728f3dedfd0d 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AbstractTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AbstractTask.java @@ -145,6 +145,12 @@ public QueryRunner getQueryRunner(Query query) return null; } + @Override + public boolean supportsQueries() + { + return false; + } + @Override public String getClasspathPrefix() { diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTask.java index c96b7f5b9002..9a786fa36ea8 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTask.java @@ -245,6 +245,12 @@ public QueryRunner getQueryRunner(Query query) return (queryPlus, responseContext) -> queryPlus.run(appenderator, responseContext); } + @Override + public boolean supportsQueries() + { + return true; + } + @Override public boolean isReady(TaskActionClient taskActionClient) { diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/RealtimeIndexTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/RealtimeIndexTask.java index ed2ddd2c604e..055a3fea3be7 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/RealtimeIndexTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/RealtimeIndexTask.java @@ -196,6 +196,12 @@ public QueryRunner getQueryRunner(Query query) return plumber.getQueryRunner(query); } + @Override + public boolean supportsQueries() + { + return true; + } + @Override public boolean isReady(TaskActionClient taskActionClient) { diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/Task.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/Task.java index c069c73cec70..4f18c81bc7c7 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/Task.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/Task.java @@ -146,6 +146,11 @@ default int getPriority() */ QueryRunner getQueryRunner(Query query); + /** + * @return true if this Task type is queryable, such as streaming ingestion tasks + */ + boolean supportsQueries(); + /** * Returns an extra classpath that should be prepended to the default classpath when running this task. If no * extra classpath should be prepended, this should return null or the empty string. diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java index 2f1abc1d7f53..10f5e6b5e9c2 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java @@ -327,6 +327,13 @@ public TaskStatus call() command.add(nodeType); } + // If the task type is queryable, we need to load broadcast segments on the peon, used for + // join queries + if (task.supportsQueries()) { + command.add("--loadBroadcastSegments"); + command.add("true"); + } + if (!taskFile.exists()) { jsonMapper.writeValue(taskFile, task); } diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTaskTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTaskTest.java index de3fa29a489b..513a590be9fd 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTaskTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/AppenderatorDriverRealtimeIndexTaskTest.java @@ -337,6 +337,7 @@ public void testBasics() throws Exception { expectPublishedSegments(1); final AppenderatorDriverRealtimeIndexTask task = makeRealtimeTask(null); + Assert.assertTrue(task.supportsQueries()); final ListenableFuture statusFuture = runTask(task); // Wait for firehose to show up, it starts off null. diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java index 8668a937aa18..6e3fcf54dcf2 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java @@ -229,6 +229,8 @@ public void testDeterminePartitions() throws Exception appenderatorsManager ); + Assert.assertFalse(indexTask.supportsQueries()); + final List segments = runTask(indexTask).rhs; Assert.assertEquals(2, segments.size()); diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RealtimeIndexTaskTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RealtimeIndexTaskTest.java index 8cc21d02e19a..12ea2145d582 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RealtimeIndexTaskTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RealtimeIndexTaskTest.java @@ -189,6 +189,12 @@ public void testDefaultResource() Assert.assertEquals(task.getId(), task.getTaskResource().getAvailabilityGroup()); } + @Test(timeout = 60_000L) + public void testSupportsQueries() + { + final RealtimeIndexTask task = makeRealtimeTask(null); + Assert.assertTrue(task.supportsQueries()); + } @Test(timeout = 60_000L, expected = ExecutionException.class) public void testHandoffTimeout() throws Exception diff --git a/server/pom.xml b/server/pom.xml index 5d68a91dfd89..ddce092c1cd4 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -291,10 +291,6 @@ javax.validation validation-api - - org.hibernate - hibernate-validator - com.google.errorprone error_prone_annotations diff --git a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java index ae4dc1689ccf..c382be3493e0 100644 --- a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java +++ b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java @@ -452,7 +452,7 @@ private String computeCurrentEtag(final Set segments, @Nu Hasher hasher = Hashing.sha1().newHasher(); boolean hasOnlyHistoricalSegments = true; for (SegmentServerSelector p : segments) { - if (!p.getServer().pick().getServer().segmentReplicatable()) { + if (!p.getServer().pick().getServer().isSegmentReplicationTarget()) { hasOnlyHistoricalSegments = false; break; } @@ -633,7 +633,7 @@ private void addSequencesFromServer( if (isBySegment) { serverResults = getBySegmentServerResults(serverRunner, segmentsOfServer, maxQueuedBytesPerServer); - } else if (!server.segmentReplicatable() || !populateCache) { + } else if (!server.isSegmentReplicationTarget() || !populateCache) { serverResults = getSimpleServerResults(serverRunner, segmentsOfServer, maxQueuedBytesPerServer); } else { serverResults = getAndCacheServerResults(serverRunner, segmentsOfServer, maxQueuedBytesPerServer); diff --git a/server/src/main/java/org/apache/druid/client/DruidServer.java b/server/src/main/java/org/apache/druid/client/DruidServer.java index ddcba54f1c3c..6c52866d0586 100644 --- a/server/src/main/java/org/apache/druid/client/DruidServer.java +++ b/server/src/main/java/org/apache/druid/client/DruidServer.java @@ -137,9 +137,19 @@ public String getTier() return metadata.getTier(); } - public boolean segmentReplicatable() + public boolean isSegmentReplicationTarget() { - return metadata.segmentReplicatable(); + return metadata.isSegmentReplicationTarget(); + } + + public boolean isSegmentBroadcastTarget() + { + return metadata.isSegmentBroadcastTarget(); + } + + public boolean isSegmentReplicationOrBroadcastTarget() + { + return metadata.isSegmentReplicationTarget() || metadata.isSegmentBroadcastTarget(); } @JsonProperty diff --git a/server/src/main/java/org/apache/druid/discovery/DruidNodeDiscoveryProvider.java b/server/src/main/java/org/apache/druid/discovery/DruidNodeDiscoveryProvider.java index 733202981839..898ce7c75956 100644 --- a/server/src/main/java/org/apache/druid/discovery/DruidNodeDiscoveryProvider.java +++ b/server/src/main/java/org/apache/druid/discovery/DruidNodeDiscoveryProvider.java @@ -44,7 +44,8 @@ public abstract class DruidNodeDiscoveryProvider private static final Map> SERVICE_TO_NODE_TYPES = ImmutableMap.of( LookupNodeService.DISCOVERY_SERVICE_KEY, ImmutableSet.of(NodeRole.BROKER, NodeRole.HISTORICAL, NodeRole.PEON, NodeRole.INDEXER), - DataNodeService.DISCOVERY_SERVICE_KEY, ImmutableSet.of(NodeRole.HISTORICAL, NodeRole.PEON, NodeRole.INDEXER), + DataNodeService.DISCOVERY_SERVICE_KEY, + ImmutableSet.of(NodeRole.HISTORICAL, NodeRole.PEON, NodeRole.INDEXER, NodeRole.BROKER), WorkerNodeService.DISCOVERY_SERVICE_KEY, ImmutableSet.of(NodeRole.MIDDLE_MANAGER, NodeRole.INDEXER) ); diff --git a/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderConfig.java b/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderConfig.java index c6c57233738c..39b3bde3129d 100644 --- a/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderConfig.java +++ b/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderConfig.java @@ -23,9 +23,9 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import org.apache.druid.utils.JvmUtils; -import org.hibernate.validator.constraints.NotEmpty; import java.io.File; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -34,8 +34,7 @@ public class SegmentLoaderConfig { @JsonProperty - @NotEmpty - private List locations = null; + private List locations = Collections.emptyList(); @JsonProperty("lazyLoadOnStart") private boolean lazyLoadOnStart = false; diff --git a/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderLocalCacheManager.java b/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderLocalCacheManager.java index 398ad679b484..b2ac7e8f35b0 100644 --- a/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderLocalCacheManager.java +++ b/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderLocalCacheManager.java @@ -89,7 +89,6 @@ public SegmentLoaderLocalCacheManager( this.indexIO = indexIO; this.config = config; this.jsonMapper = mapper; - this.locations = new ArrayList<>(); for (StorageLocationConfig locationConfig : config.getLocations()) { locations.add( diff --git a/server/src/main/java/org/apache/druid/segment/realtime/CliIndexerDataSegmentServerAnnouncerLifecycleHandler.java b/server/src/main/java/org/apache/druid/segment/realtime/CliIndexerDataSegmentServerAnnouncerLifecycleHandler.java deleted file mode 100644 index e874a30c86d9..000000000000 --- a/server/src/main/java/org/apache/druid/segment/realtime/CliIndexerDataSegmentServerAnnouncerLifecycleHandler.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.segment.realtime; - -import com.google.common.base.Throwables; -import com.google.inject.Inject; -import org.apache.druid.concurrent.LifecycleLock; -import org.apache.druid.guice.ManageLifecycle; -import org.apache.druid.java.util.common.lifecycle.LifecycleStart; -import org.apache.druid.java.util.common.lifecycle.LifecycleStop; -import org.apache.druid.java.util.emitter.EmittingLogger; -import org.apache.druid.server.coordination.DataSegmentServerAnnouncer; - -import java.io.IOException; - -/** - * Ties the {@link DataSegmentServerAnnouncer} announce/unannounce to the lifecycle start and stop. - * - * Analogous to {@link org.apache.druid.server.coordination.SegmentLoadDropHandler} on the Historicals, - * but without segment cache management. - */ -@ManageLifecycle -public class CliIndexerDataSegmentServerAnnouncerLifecycleHandler -{ - private static final EmittingLogger LOG = new EmittingLogger(CliIndexerDataSegmentServerAnnouncerLifecycleHandler.class); - - private final DataSegmentServerAnnouncer dataSegmentServerAnnouncer; - - private final LifecycleLock lifecycleLock = new LifecycleLock(); - - @Inject - public CliIndexerDataSegmentServerAnnouncerLifecycleHandler( - DataSegmentServerAnnouncer dataSegmentServerAnnouncer - ) - { - this.dataSegmentServerAnnouncer = dataSegmentServerAnnouncer; - } - - @LifecycleStart - public void start() throws IOException - { - if (!lifecycleLock.canStart()) { - throw new RuntimeException("Lifecycle lock could not start"); - } - - try { - if (lifecycleLock.isStarted()) { - return; - } - - LOG.info("Starting..."); - try { - dataSegmentServerAnnouncer.announce(); - } - catch (Exception e) { - Throwables.propagateIfPossible(e, IOException.class); - throw new RuntimeException(e); - } - LOG.info("Started."); - lifecycleLock.started(); - } - finally { - lifecycleLock.exitStart(); - } - } - - @LifecycleStop - public void stop() - { - if (!lifecycleLock.canStop()) { - throw new RuntimeException("Lifecycle lock could not stop"); - } - - if (!lifecycleLock.isStarted()) { - return; - } - - LOG.info("Stopping..."); - try { - dataSegmentServerAnnouncer.unannounce(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - LOG.info("Stopped."); - } -} diff --git a/server/src/main/java/org/apache/druid/segment/realtime/plumber/CoordinatorBasedSegmentHandoffNotifier.java b/server/src/main/java/org/apache/druid/segment/realtime/plumber/CoordinatorBasedSegmentHandoffNotifier.java index bbee720a88ce..2e97258a52da 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/plumber/CoordinatorBasedSegmentHandoffNotifier.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/plumber/CoordinatorBasedSegmentHandoffNotifier.java @@ -142,7 +142,7 @@ static boolean isHandOffComplete(List serverView, Segm && segmentLoadInfo.getSegment().getShardSpec().getPartitionNum() == descriptor.getPartitionNumber() && segmentLoadInfo.getSegment().getVersion().compareTo(descriptor.getVersion()) >= 0 - && segmentLoadInfo.getServers().stream().anyMatch(DruidServerMetadata::segmentReplicatable)) { + && segmentLoadInfo.getServers().stream().anyMatch(DruidServerMetadata::isSegmentReplicationOrBroadcastTarget)) { return true; } } diff --git a/server/src/main/java/org/apache/druid/server/coordination/DruidServerMetadata.java b/server/src/main/java/org/apache/druid/server/coordination/DruidServerMetadata.java index e3673bbc9cae..3fda41b08dab 100644 --- a/server/src/main/java/org/apache/druid/server/coordination/DruidServerMetadata.java +++ b/server/src/main/java/org/apache/druid/server/coordination/DruidServerMetadata.java @@ -107,11 +107,21 @@ public int getPriority() return priority; } - public boolean segmentReplicatable() + public boolean isSegmentReplicationTarget() { return type.isSegmentReplicationTarget(); } + public boolean isSegmentBroadcastTarget() + { + return type.isSegmentBroadcastTarget(); + } + + public boolean isSegmentReplicationOrBroadcastTarget() + { + return isSegmentReplicationTarget() || isSegmentBroadcastTarget(); + } + @Override public boolean equals(Object o) { diff --git a/server/src/main/java/org/apache/druid/server/coordination/LoadableDataSegment.java b/server/src/main/java/org/apache/druid/server/coordination/LoadableDataSegment.java new file mode 100644 index 000000000000..4f4f7a5b1d19 --- /dev/null +++ b/server/src/main/java/org/apache/druid/server/coordination/LoadableDataSegment.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.server.coordination; + +import com.fasterxml.jackson.annotation.JacksonInject; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.apache.druid.jackson.CommaListJoinDeserializer; +import org.apache.druid.timeline.CompactionState; +import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.partition.ShardSpec; +import org.joda.time.Interval; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; + +/** + * A deserialization aid used by {@link SegmentChangeRequestLoad}. The broker prunes the loadSpec from segments + * for efficiency reasons, but the broker does need the loadSpec when it loads broadcast segments. + * + * This class always uses the non-pruning default {@link PruneSpecsHolder}. + */ +public class LoadableDataSegment extends DataSegment +{ + @JsonCreator + public LoadableDataSegment( + @JsonProperty("dataSource") String dataSource, + @JsonProperty("interval") Interval interval, + @JsonProperty("version") String version, + // use `Map` *NOT* `LoadSpec` because we want to do lazy materialization to prevent dependency pollution + @JsonProperty("loadSpec") @Nullable Map loadSpec, + @JsonProperty("dimensions") + @JsonDeserialize(using = CommaListJoinDeserializer.class) + @Nullable + List dimensions, + @JsonProperty("metrics") + @JsonDeserialize(using = CommaListJoinDeserializer.class) + @Nullable + List metrics, + @JsonProperty("shardSpec") @Nullable ShardSpec shardSpec, + @JsonProperty("lastCompactionState") @Nullable CompactionState lastCompactionState, + @JsonProperty("binaryVersion") Integer binaryVersion, + @JsonProperty("size") long size, + @JacksonInject PruneSpecsHolder pruneSpecsHolder + ) + { + super( + dataSource, + interval, + version, + loadSpec, + dimensions, + metrics, + shardSpec, + lastCompactionState, + binaryVersion, + size, + PruneSpecsHolder.DEFAULT + ); + + } +} diff --git a/server/src/main/java/org/apache/druid/server/coordination/SegmentChangeRequestLoad.java b/server/src/main/java/org/apache/druid/server/coordination/SegmentChangeRequestLoad.java index 097e02523032..130c7b50d80c 100644 --- a/server/src/main/java/org/apache/druid/server/coordination/SegmentChangeRequestLoad.java +++ b/server/src/main/java/org/apache/druid/server/coordination/SegmentChangeRequestLoad.java @@ -35,14 +35,26 @@ public class SegmentChangeRequestLoad implements DataSegmentChangeRequest { private final DataSegment segment; + /** + * To avoid pruning of the loadSpec on the broker, needed when the broker is loading broadcast segments, + * we deserialize into an {@link LoadableDataSegment}, which never removes the loadSpec. + */ @JsonCreator public SegmentChangeRequestLoad( - @JsonUnwrapped DataSegment segment + @JsonUnwrapped LoadableDataSegment segment ) { this.segment = segment; } + public SegmentChangeRequestLoad( + DataSegment segment + ) + { + this.segment = segment; + } + + @Override public void go(DataSegmentChangeHandler handler, @Nullable DataSegmentChangeCallback callback) { diff --git a/server/src/main/java/org/apache/druid/server/coordination/SegmentLoadDropHandler.java b/server/src/main/java/org/apache/druid/server/coordination/SegmentLoadDropHandler.java index 51aefc1b8d67..87a19365e6f5 100644 --- a/server/src/main/java/org/apache/druid/server/coordination/SegmentLoadDropHandler.java +++ b/server/src/main/java/org/apache/druid/server/coordination/SegmentLoadDropHandler.java @@ -34,6 +34,8 @@ import com.google.common.util.concurrent.SettableFuture; import com.google.inject.Inject; import org.apache.druid.guice.ManageLifecycle; +import org.apache.druid.guice.ServerTypeConfig; +import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.concurrent.Execs; import org.apache.druid.java.util.common.lifecycle.LifecycleStart; @@ -104,7 +106,8 @@ public SegmentLoadDropHandler( SegmentLoaderConfig config, DataSegmentAnnouncer announcer, DataSegmentServerAnnouncer serverAnnouncer, - SegmentManager segmentManager + SegmentManager segmentManager, + ServerTypeConfig serverTypeConfig ) { this( @@ -116,7 +119,8 @@ public SegmentLoadDropHandler( Executors.newScheduledThreadPool( config.getNumLoadingThreads(), Execs.makeThreadFactory("SimpleDataSegmentChangeHandler-%s") - ) + ), + serverTypeConfig ); } @@ -127,7 +131,8 @@ public SegmentLoadDropHandler( DataSegmentAnnouncer announcer, DataSegmentServerAnnouncer serverAnnouncer, SegmentManager segmentManager, - ScheduledExecutorService exec + ScheduledExecutorService exec, + ServerTypeConfig serverTypeConfig ) { this.jsonMapper = jsonMapper; @@ -139,6 +144,13 @@ public SegmentLoadDropHandler( this.exec = exec; this.segmentsToDelete = new ConcurrentSkipListSet<>(); + if (config.getLocations().isEmpty()) { + if (ServerType.HISTORICAL.equals(serverTypeConfig.getServerType())) { + throw new IAE("Segment cache locations must be set on historicals."); + } else { + log.info("Not starting SegmentLoadDropHandler with empty segment cache locations."); + } + } requestStatuses = CacheBuilder.newBuilder().maximumSize(config.getStatusQueueMaxSize()).initialCapacity(8).build(); } @@ -152,8 +164,10 @@ public void start() throws IOException log.info("Starting..."); try { - loadLocalCache(); - serverAnnouncer.announce(); + if (!config.getLocations().isEmpty()) { + loadLocalCache(); + serverAnnouncer.announce(); + } } catch (Exception e) { Throwables.propagateIfPossible(e, IOException.class); @@ -174,7 +188,9 @@ public void stop() log.info("Stopping..."); try { - serverAnnouncer.unannounce(); + if (!config.getLocations().isEmpty()) { + serverAnnouncer.unannounce(); + } } catch (Exception e) { throw new RuntimeException(e); diff --git a/server/src/main/java/org/apache/druid/server/coordination/ServerType.java b/server/src/main/java/org/apache/druid/server/coordination/ServerType.java index 42fb65a3fdfb..0b860a1b0afe 100644 --- a/server/src/main/java/org/apache/druid/server/coordination/ServerType.java +++ b/server/src/main/java/org/apache/druid/server/coordination/ServerType.java @@ -63,6 +63,14 @@ public boolean isSegmentReplicationTarget() { return false; } + }, + + BROKER { + @Override + public boolean isSegmentReplicationTarget() + { + return false; + } }; /** diff --git a/server/src/main/java/org/apache/druid/server/coordinator/BalancerStrategy.java b/server/src/main/java/org/apache/druid/server/coordinator/BalancerStrategy.java index d9fea81e3a95..889c167c8ef5 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/BalancerStrategy.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/BalancerStrategy.java @@ -26,6 +26,7 @@ import java.util.Iterator; import java.util.List; import java.util.NavigableSet; +import java.util.Set; /** * This interface describes the coordinator balancing strategy, which is responsible for making decisions on where @@ -56,11 +57,17 @@ public interface BalancerStrategy /** * Pick the best segment to move from one of the supplied set of servers according to the balancing strategy. * @param serverHolders set of historicals to consider for moving segments + * @param broadcastDatasources Datasources that contain segments which were loaded via broadcast rules. + * Balancing strategies should avoid rebalancing segments for such datasources, since + * they should be loaded on all servers anyway. + * NOTE: this should really be handled on a per-segment basis, to properly support + * the interval or period-based broadcast rules. For simplicity of the initial + * implementation, only forever broadcast rules are supported. * @return {@link BalancerSegmentHolder} containing segment to move and server it currently resides on, or null if * there are no segments to pick from (i. e. all provided serverHolders are empty). */ @Nullable - BalancerSegmentHolder pickSegmentToMove(List serverHolders); + BalancerSegmentHolder pickSegmentToMove(List serverHolders, Set broadcastDatasources); /** * Returns an iterator for a set of servers to drop from, ordered by preference of which server to drop from first diff --git a/server/src/main/java/org/apache/druid/server/coordinator/CachingCostBalancerStrategyFactory.java b/server/src/main/java/org/apache/druid/server/coordinator/CachingCostBalancerStrategyFactory.java index 4a1989df24df..1741087e8c5c 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/CachingCostBalancerStrategyFactory.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/CachingCostBalancerStrategyFactory.java @@ -71,7 +71,7 @@ public CachingCostBalancerStrategyFactory( @Override public ServerView.CallbackAction segmentAdded(DruidServerMetadata server, DataSegment segment) { - if (server.segmentReplicatable()) { + if (server.isSegmentReplicationTarget()) { clusterCostCacheBuilder.addSegment(server.getName(), segment); } return ServerView.CallbackAction.CONTINUE; @@ -80,7 +80,7 @@ public ServerView.CallbackAction segmentAdded(DruidServerMetadata server, DataSe @Override public ServerView.CallbackAction segmentRemoved(DruidServerMetadata server, DataSegment segment) { - if (server.segmentReplicatable()) { + if (server.isSegmentReplicationTarget()) { clusterCostCacheBuilder.removeSegment(server.getName(), segment); } return ServerView.CallbackAction.CONTINUE; @@ -98,7 +98,7 @@ public ServerView.CallbackAction segmentViewInitialized() serverInventoryView.registerServerRemovedCallback( executor, server -> { - if (server.segmentReplicatable()) { + if (server.isSegmentReplicationTarget()) { clusterCostCacheBuilder.removeServer(server.getName()); } return ServerView.CallbackAction.CONTINUE; diff --git a/server/src/main/java/org/apache/druid/server/coordinator/CostBalancerStrategy.java b/server/src/main/java/org/apache/druid/server/coordinator/CostBalancerStrategy.java index 4fd4164f3001..5d656d643f99 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/CostBalancerStrategy.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/CostBalancerStrategy.java @@ -35,6 +35,7 @@ import java.util.Iterator; import java.util.List; import java.util.NavigableSet; +import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; @@ -211,9 +212,12 @@ static double computeJointSegmentsCost(final DataSegment segment, final Iterable @Override - public BalancerSegmentHolder pickSegmentToMove(final List serverHolders) + public BalancerSegmentHolder pickSegmentToMove( + final List serverHolders, + Set broadcastDatasources + ) { - return ReservoirSegmentSampler.getRandomBalancerSegmentHolder(serverHolders); + return ReservoirSegmentSampler.getRandomBalancerSegmentHolder(serverHolders, broadcastDatasources); } @Override diff --git a/server/src/main/java/org/apache/druid/server/coordinator/DruidCluster.java b/server/src/main/java/org/apache/druid/server/coordinator/DruidCluster.java index 318f663609b0..8fb4ccb056d4 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/DruidCluster.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/DruidCluster.java @@ -47,24 +47,28 @@ public class DruidCluster @VisibleForTesting static DruidCluster createDruidClusterFromBuilderInTest( @Nullable Set realtimes, - Map> historicals + Map> historicals, + @Nullable Set brokers ) { - return new DruidCluster(realtimes, historicals); + return new DruidCluster(realtimes, historicals, brokers); } private final Set realtimes; private final Map> historicals; + private final Set brokers; public DruidCluster() { this.realtimes = new HashSet<>(); this.historicals = new HashMap<>(); + this.brokers = new HashSet<>(); } private DruidCluster( @Nullable Set realtimes, - Map> historicals + Map> historicals, + @Nullable Set brokers ) { this.realtimes = realtimes == null ? new HashSet<>() : new HashSet<>(realtimes); @@ -72,6 +76,7 @@ private DruidCluster( historicals, holders -> CollectionUtils.newTreeSet(Comparator.reverseOrder(), holders) ); + this.brokers = brokers == null ? new HashSet<>() : new HashSet<>(brokers); } public void add(ServerHolder serverHolder) @@ -87,7 +92,11 @@ public void add(ServerHolder serverHolder) addHistorical(serverHolder); break; case INDEXER_EXECUTOR: - throw new IAE("unsupported server type[%s]", serverHolder.getServer().getType()); + addRealtime(serverHolder); + break; + case BROKER: + addBroker(serverHolder); + break; default: throw new IAE("unknown server type[%s]", serverHolder.getServer().getType()); } @@ -108,6 +117,11 @@ private void addHistorical(ServerHolder serverHolder) tierServers.add(serverHolder); } + private void addBroker(ServerHolder serverHolder) + { + brokers.add(serverHolder); + } + public Set getRealtimes() { return realtimes; @@ -118,6 +132,12 @@ public Map> getHistoricals() return historicals; } + + public Set getBrokers() + { + return brokers; + } + public Iterable getTierNames() { return historicals.keySet(); @@ -135,6 +155,7 @@ public Collection getAllServers() final List allServers = new ArrayList<>(historicalSize + realtimeSize); historicals.values().forEach(allServers::addAll); + allServers.addAll(brokers); allServers.addAll(realtimes); return allServers; } @@ -146,7 +167,7 @@ public Iterable> getSortedHistoricalsByTier() public boolean isEmpty() { - return historicals.isEmpty() && realtimes.isEmpty(); + return historicals.isEmpty() && realtimes.isEmpty() && brokers.isEmpty(); } public boolean hasHistoricals() @@ -159,9 +180,19 @@ public boolean hasRealtimes() return !realtimes.isEmpty(); } + public boolean hasBrokers() + { + return !brokers.isEmpty(); + } + public boolean hasTier(String tier) { - NavigableSet servers = historicals.get(tier); - return (servers != null) && !servers.isEmpty(); + NavigableSet historicalServers = historicals.get(tier); + boolean historicalsHasTier = (historicalServers != null) && !historicalServers.isEmpty(); + if (historicalsHasTier) { + return true; + } + + return false; } } diff --git a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java index f8c3f43c76f5..36a414e780d9 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java @@ -761,7 +761,7 @@ List prepareCurrentServers() List currentServers = serverInventoryView .getInventory() .stream() - .filter(DruidServer::segmentReplicatable) + .filter(DruidServer::isSegmentReplicationOrBroadcastTarget) .map(DruidServer::toImmutableDruidServer) .collect(Collectors.toList()); diff --git a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java index 7bae11e61db0..3337b8e7647a 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java @@ -34,7 +34,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.TreeSet; import java.util.concurrent.TimeUnit; @@ -70,6 +72,7 @@ private static TreeSet createUsedSegmentsSet(Iterable private final CoordinatorStats stats; private final DateTime balancerReferenceTimestamp; private final BalancerStrategy balancerStrategy; + private final Set broadcastDatasources; private DruidCoordinatorRuntimeParams( long startTimeNanos, @@ -85,7 +88,8 @@ private DruidCoordinatorRuntimeParams( CoordinatorCompactionConfig coordinatorCompactionConfig, CoordinatorStats stats, DateTime balancerReferenceTimestamp, - BalancerStrategy balancerStrategy + BalancerStrategy balancerStrategy, + Set broadcastDatasources ) { this.startTimeNanos = startTimeNanos; @@ -102,6 +106,7 @@ private DruidCoordinatorRuntimeParams( this.stats = stats; this.balancerReferenceTimestamp = balancerReferenceTimestamp; this.balancerStrategy = balancerStrategy; + this.broadcastDatasources = broadcastDatasources; } public long getStartTimeNanos() @@ -180,6 +185,11 @@ public BalancerStrategy getBalancerStrategy() return balancerStrategy; } + public Set getBroadcastDatasources() + { + return broadcastDatasources; + } + public boolean coordinatorIsLeadingEnoughTimeToMarkAsUnusedOvershadowedSegements() { long nanosElapsedSinceCoordinatorStart = System.nanoTime() - getStartTimeNanos(); @@ -256,6 +266,7 @@ public static class Builder private CoordinatorStats stats; private DateTime balancerReferenceTimestamp; private BalancerStrategy balancerStrategy; + private Set broadcastDatasources; private Builder() { @@ -272,6 +283,7 @@ private Builder() this.coordinatorDynamicConfig = CoordinatorDynamicConfig.builder().build(); this.coordinatorCompactionConfig = CoordinatorCompactionConfig.empty(); this.balancerReferenceTimestamp = DateTimes.nowUtc(); + this.broadcastDatasources = new HashSet<>(); } Builder( @@ -324,7 +336,8 @@ public DruidCoordinatorRuntimeParams build() coordinatorCompactionConfig, stats, balancerReferenceTimestamp, - balancerStrategy + balancerStrategy, + broadcastDatasources ); } @@ -436,5 +449,11 @@ public Builder withBalancerStrategy(BalancerStrategy balancerStrategy) this.balancerStrategy = balancerStrategy; return this; } + + public Builder withBroadcastDatasources(Set broadcastDatasources) + { + this.broadcastDatasources = broadcastDatasources; + return this; + } } } diff --git a/server/src/main/java/org/apache/druid/server/coordinator/RandomBalancerStrategy.java b/server/src/main/java/org/apache/druid/server/coordinator/RandomBalancerStrategy.java index 8b0b30698175..72fdedf6e453 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/RandomBalancerStrategy.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/RandomBalancerStrategy.java @@ -26,6 +26,7 @@ import java.util.Iterator; import java.util.List; import java.util.NavigableSet; +import java.util.Set; import java.util.concurrent.ThreadLocalRandom; public class RandomBalancerStrategy implements BalancerStrategy @@ -51,9 +52,9 @@ public ServerHolder findNewSegmentHomeBalancer(DataSegment proposalSegment, List } @Override - public BalancerSegmentHolder pickSegmentToMove(List serverHolders) + public BalancerSegmentHolder pickSegmentToMove(List serverHolders, Set broadcastDatasources) { - return ReservoirSegmentSampler.getRandomBalancerSegmentHolder(serverHolders); + return ReservoirSegmentSampler.getRandomBalancerSegmentHolder(serverHolders, broadcastDatasources); } @Override diff --git a/server/src/main/java/org/apache/druid/server/coordinator/ReservoirSegmentSampler.java b/server/src/main/java/org/apache/druid/server/coordinator/ReservoirSegmentSampler.java index c2c4a7ad10c4..7181d52e152a 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/ReservoirSegmentSampler.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/ReservoirSegmentSampler.java @@ -22,19 +22,33 @@ import org.apache.druid.timeline.DataSegment; import java.util.List; +import java.util.Set; import java.util.concurrent.ThreadLocalRandom; final class ReservoirSegmentSampler { - static BalancerSegmentHolder getRandomBalancerSegmentHolder(final List serverHolders) + static BalancerSegmentHolder getRandomBalancerSegmentHolder( + final List serverHolders, + Set broadcastDatasources + ) { ServerHolder fromServerHolder = null; DataSegment proposalSegment = null; int numSoFar = 0; for (ServerHolder server : serverHolders) { + if (!server.getServer().getType().isSegmentReplicationTarget()) { + // if the server only handles broadcast segments (which don't need to be rebalanced), we have nothing to do + continue; + } + for (DataSegment segment : server.getServer().iterateAllSegments()) { + if (broadcastDatasources.contains(segment.getDataSource())) { + // we don't need to rebalance segments that were assigned via broadcast rules + continue; + } + int randNum = ThreadLocalRandom.current().nextInt(numSoFar + 1); // w.p. 1 / (numSoFar+1), swap out the server and segment if (randNum == numSoFar) { diff --git a/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java b/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java index ba96566a4dfd..26fa9a54c7ff 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java @@ -122,6 +122,11 @@ public boolean isLoadingSegment(DataSegment segment) return peon.getSegmentsToLoad().contains(segment); } + public boolean isDroppingSegment(DataSegment segment) + { + return peon.getSegmentsToDrop().contains(segment); + } + public int getNumberOfSegmentsInQueue() { return peon.getNumberOfSegmentsInQueue(); diff --git a/server/src/main/java/org/apache/druid/server/coordinator/duty/BalanceSegments.java b/server/src/main/java/org/apache/druid/server/coordinator/duty/BalanceSegments.java index d42ca635bd62..a1c5237ddd15 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/duty/BalanceSegments.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/duty/BalanceSegments.java @@ -187,7 +187,10 @@ private Pair balanceServers( //noinspection ForLoopThatDoesntUseLoopVariable for (int iter = 0; (moved + unmoved) < maxSegmentsToMove; ++iter) { - final BalancerSegmentHolder segmentToMoveHolder = strategy.pickSegmentToMove(toMoveFrom); + final BalancerSegmentHolder segmentToMoveHolder = strategy.pickSegmentToMove( + toMoveFrom, + params.getBroadcastDatasources() + ); if (segmentToMoveHolder == null) { log.info("All servers to move segments from are empty, ending run."); break; diff --git a/server/src/main/java/org/apache/druid/server/coordinator/duty/RunRules.java b/server/src/main/java/org/apache/druid/server/coordinator/duty/RunRules.java index 5288bb35858f..3dc7b4d2f918 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/duty/RunRules.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/duty/RunRules.java @@ -28,11 +28,13 @@ import org.apache.druid.server.coordinator.DruidCoordinator; import org.apache.druid.server.coordinator.DruidCoordinatorRuntimeParams; import org.apache.druid.server.coordinator.ReplicationThrottler; +import org.apache.druid.server.coordinator.rules.BroadcastDistributionRule; import org.apache.druid.server.coordinator.rules.Rule; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.SegmentId; import org.joda.time.DateTime; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -101,6 +103,7 @@ public DruidCoordinatorRuntimeParams run(DruidCoordinatorRuntimeParams params) final List segmentsWithMissingRules = Lists.newArrayListWithCapacity(MAX_MISSING_RULES); int missingRules = 0; + final Set broadcastDatasources = new HashSet<>(); for (DataSegment segment : params.getUsedSegments()) { if (overshadowed.contains(segment.getId())) { // Skipping overshadowed segments @@ -112,6 +115,12 @@ public DruidCoordinatorRuntimeParams run(DruidCoordinatorRuntimeParams params) if (rule.appliesTo(segment, now)) { stats.accumulate(rule.run(coordinator, paramsWithReplicationManager, segment)); foundMatchingRule = true; + + // The set of broadcast datasources is used by BalanceSegments, so it's important that RunRules + // executes before BalanceSegments + if (rule instanceof BroadcastDistributionRule) { + broadcastDatasources.add(segment.getDataSource()); + } break; } } @@ -131,6 +140,9 @@ public DruidCoordinatorRuntimeParams run(DruidCoordinatorRuntimeParams params) .emit(); } - return params.buildFromExisting().withCoordinatorStats(stats).build(); + return params.buildFromExisting() + .withCoordinatorStats(stats) + .withBroadcastDatasources(broadcastDatasources) + .build(); } } diff --git a/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java b/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java index 658171236adc..35ff39ea6505 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java @@ -20,6 +20,7 @@ package org.apache.druid.server.coordinator.rules; import org.apache.druid.java.util.emitter.EmittingLogger; +import org.apache.druid.server.coordination.ServerType; import org.apache.druid.server.coordinator.CoordinatorStats; import org.apache.druid.server.coordinator.DruidCoordinator; import org.apache.druid.server.coordinator.DruidCoordinatorRuntimeParams; @@ -27,8 +28,8 @@ import org.apache.druid.timeline.DataSegment; import java.util.HashSet; -import java.util.List; import java.util.Set; +import java.util.stream.Collectors; public abstract class BroadcastDistributionRule implements Rule { @@ -37,30 +38,35 @@ public abstract class BroadcastDistributionRule implements Rule @Override public CoordinatorStats run(DruidCoordinator coordinator, DruidCoordinatorRuntimeParams params, DataSegment segment) { - // Find servers which holds the segments of co-located data source - final Set loadServerHolders = new HashSet<>(); final Set dropServerHolders = new HashSet<>(); - final List colocatedDataSources = getColocatedDataSources(); - if (colocatedDataSources == null || colocatedDataSources.isEmpty()) { - loadServerHolders.addAll(params.getDruidCluster().getAllServers()); - } else { - params.getDruidCluster().getAllServers().forEach( - eachHolder -> { - if (!eachHolder.isDecommissioning() - && colocatedDataSources.stream() - .anyMatch(source -> eachHolder.getServer().getDataSource(source) != null)) { - loadServerHolders.add(eachHolder); - } else if (eachHolder.isServingSegment(segment)) { - if (!eachHolder.getPeon().getSegmentsToDrop().contains(segment)) { - dropServerHolders.add(eachHolder); - } - } - } - ); - } - final CoordinatorStats stats = new CoordinatorStats(); + // Find servers where we need to load the broadcast segments + final Set loadServerHolders = + params.getDruidCluster().getAllServers() + .stream() + .filter( + (serverHolder) -> { + ServerType serverType = serverHolder.getServer().getType(); + if (!serverType.isSegmentBroadcastTarget()) { + return false; + } + + final boolean isServingSegment = + serverHolder.isServingSegment(segment); + + if (serverHolder.isDecommissioning()) { + if (isServingSegment && !serverHolder.isDroppingSegment(segment)) { + dropServerHolders.add(serverHolder); + } + return false; + } + return !isServingSegment && !serverHolder.isLoadingSegment(segment); + } + ) + .collect(Collectors.toSet()); + + final CoordinatorStats stats = new CoordinatorStats(); return stats.accumulate(assign(loadServerHolders, segment)) .accumulate(drop(dropServerHolders, segment)); } @@ -110,6 +116,4 @@ private CoordinatorStats drop( return stats; } - - public abstract List getColocatedDataSources(); } diff --git a/server/src/main/java/org/apache/druid/server/coordinator/rules/ForeverBroadcastDistributionRule.java b/server/src/main/java/org/apache/druid/server/coordinator/rules/ForeverBroadcastDistributionRule.java index d095f1100aea..ef5094cbea4a 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/rules/ForeverBroadcastDistributionRule.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/rules/ForeverBroadcastDistributionRule.java @@ -25,21 +25,16 @@ import org.joda.time.DateTime; import org.joda.time.Interval; -import java.util.List; import java.util.Objects; public class ForeverBroadcastDistributionRule extends BroadcastDistributionRule { static final String TYPE = "broadcastForever"; - private final List colocatedDataSources; - @JsonCreator - public ForeverBroadcastDistributionRule( - @JsonProperty("colocatedDataSources") List colocatedDataSources - ) + public ForeverBroadcastDistributionRule() { - this.colocatedDataSources = colocatedDataSources; + } @Override @@ -49,13 +44,6 @@ public String getType() return TYPE; } - @Override - @JsonProperty - public List getColocatedDataSources() - { - return colocatedDataSources; - } - @Override public boolean appliesTo(DataSegment segment, DateTime referenceTimestamp) { @@ -79,13 +67,12 @@ public boolean equals(Object o) return false; } - ForeverBroadcastDistributionRule that = (ForeverBroadcastDistributionRule) o; - return Objects.equals(colocatedDataSources, that.colocatedDataSources); + return true; } @Override public int hashCode() { - return Objects.hash(getType(), colocatedDataSources); + return Objects.hash(getType()); } } diff --git a/server/src/main/java/org/apache/druid/server/coordinator/rules/IntervalBroadcastDistributionRule.java b/server/src/main/java/org/apache/druid/server/coordinator/rules/IntervalBroadcastDistributionRule.java index c40dff7268aa..b1bf29eedd20 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/rules/IntervalBroadcastDistributionRule.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/rules/IntervalBroadcastDistributionRule.java @@ -25,23 +25,19 @@ import org.joda.time.DateTime; import org.joda.time.Interval; -import java.util.List; import java.util.Objects; public class IntervalBroadcastDistributionRule extends BroadcastDistributionRule { static final String TYPE = "broadcastByInterval"; private final Interval interval; - private final List colocatedDataSources; @JsonCreator public IntervalBroadcastDistributionRule( - @JsonProperty("interval") Interval interval, - @JsonProperty("colocatedDataSources") List colocatedDataSources + @JsonProperty("interval") Interval interval ) { this.interval = interval; - this.colocatedDataSources = colocatedDataSources; } @Override @@ -51,13 +47,6 @@ public String getType() return TYPE; } - @Override - @JsonProperty - public List getColocatedDataSources() - { - return colocatedDataSources; - } - @Override public boolean appliesTo(DataSegment segment, DateTime referenceTimestamp) { @@ -79,26 +68,19 @@ public Interval getInterval() @Override public boolean equals(Object o) { - if (o == this) { + if (this == o) { return true; } - - if (o == null || o.getClass() != getClass()) { + if (o == null || getClass() != o.getClass()) { return false; } - IntervalBroadcastDistributionRule that = (IntervalBroadcastDistributionRule) o; - - if (!Objects.equals(interval, that.interval)) { - return false; - } - - return Objects.equals(colocatedDataSources, that.colocatedDataSources); + return Objects.equals(getInterval(), that.getInterval()); } @Override public int hashCode() { - return Objects.hash(getType(), interval, colocatedDataSources); + return Objects.hash(getInterval()); } } diff --git a/server/src/main/java/org/apache/druid/server/coordinator/rules/PeriodBroadcastDistributionRule.java b/server/src/main/java/org/apache/druid/server/coordinator/rules/PeriodBroadcastDistributionRule.java index 97c6e11cfba2..d48353d3e50a 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/rules/PeriodBroadcastDistributionRule.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/rules/PeriodBroadcastDistributionRule.java @@ -26,7 +26,6 @@ import org.joda.time.Interval; import org.joda.time.Period; -import java.util.List; import java.util.Objects; public class PeriodBroadcastDistributionRule extends BroadcastDistributionRule @@ -36,18 +35,15 @@ public class PeriodBroadcastDistributionRule extends BroadcastDistributionRule private final Period period; private final boolean includeFuture; - private final List colocatedDataSources; @JsonCreator public PeriodBroadcastDistributionRule( @JsonProperty("period") Period period, - @JsonProperty("includeFuture") Boolean includeFuture, - @JsonProperty("colocatedDataSources") List colocatedDataSources + @JsonProperty("includeFuture") Boolean includeFuture ) { this.period = period; this.includeFuture = includeFuture == null ? DEFAULT_INCLUDE_FUTURE : includeFuture; - this.colocatedDataSources = colocatedDataSources; } @Override @@ -57,13 +53,6 @@ public String getType() return TYPE; } - @Override - @JsonProperty - public List getColocatedDataSources() - { - return colocatedDataSources; - } - @Override public boolean appliesTo(DataSegment segment, DateTime referenceTimestamp) { @@ -94,25 +83,17 @@ public boolean equals(Object o) if (this == o) { return true; } - - if (o == null || o.getClass() != getClass()) { + if (o == null || getClass() != o.getClass()) { return false; } - PeriodBroadcastDistributionRule that = (PeriodBroadcastDistributionRule) o; - - if (!Objects.equals(period, that.period)) { - return false; - } - if (includeFuture != that.includeFuture) { - return false; - } - return Objects.equals(colocatedDataSources, that.colocatedDataSources); + return isIncludeFuture() == that.isIncludeFuture() && + Objects.equals(getPeriod(), that.getPeriod()); } @Override public int hashCode() { - return Objects.hash(getType(), period, colocatedDataSources); + return Objects.hash(getPeriod(), isIncludeFuture()); } } diff --git a/server/src/main/java/org/apache/druid/server/http/DataSourcesResource.java b/server/src/main/java/org/apache/druid/server/http/DataSourcesResource.java index 040297521b80..b6d310f1ba73 100644 --- a/server/src/main/java/org/apache/druid/server/http/DataSourcesResource.java +++ b/server/src/main/java/org/apache/druid/server/http/DataSourcesResource.java @@ -769,7 +769,7 @@ static boolean isSegmentLoaded(Iterable servedSegments && segmentLoadInfo.getSegment().getShardSpec().getPartitionNum() == descriptor.getPartitionNumber() && segmentLoadInfo.getSegment().getVersion().compareTo(descriptor.getVersion()) >= 0 && Iterables.any( - segmentLoadInfo.getServers(), DruidServerMetadata::segmentReplicatable + segmentLoadInfo.getServers(), DruidServerMetadata::isSegmentReplicationTarget )) { return true; } diff --git a/server/src/test/java/org/apache/druid/segment/realtime/plumber/CoordinatorBasedSegmentHandoffNotifierTest.java b/server/src/test/java/org/apache/druid/segment/realtime/plumber/CoordinatorBasedSegmentHandoffNotifierTest.java index 47157347a413..f5534bd46a3b 100644 --- a/server/src/test/java/org/apache/druid/segment/realtime/plumber/CoordinatorBasedSegmentHandoffNotifierTest.java +++ b/server/src/test/java/org/apache/druid/segment/realtime/plumber/CoordinatorBasedSegmentHandoffNotifierTest.java @@ -174,7 +174,7 @@ public void testHandoffChecksForAssignableServer() ) ); - Assert.assertFalse( + Assert.assertTrue( CoordinatorBasedSegmentHandoffNotifier.isHandOffComplete( Collections.singletonList( new ImmutableSegmentLoadInfo( diff --git a/server/src/test/java/org/apache/druid/server/coordination/SegmentLoadDropHandlerTest.java b/server/src/test/java/org/apache/druid/server/coordination/SegmentLoadDropHandlerTest.java index f5455fbbfb29..6d8ef0a8cc4e 100644 --- a/server/src/test/java/org/apache/druid/server/coordination/SegmentLoadDropHandlerTest.java +++ b/server/src/test/java/org/apache/druid/server/coordination/SegmentLoadDropHandlerTest.java @@ -24,6 +24,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ListenableFuture; +import org.apache.druid.guice.ServerTypeConfig; +import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.concurrent.Execs; import org.apache.druid.java.util.common.concurrent.ScheduledExecutorFactory; @@ -32,6 +34,7 @@ import org.apache.druid.segment.TestHelper; import org.apache.druid.segment.loading.CacheTestSegmentLoader; import org.apache.druid.segment.loading.SegmentLoaderConfig; +import org.apache.druid.segment.loading.StorageLocationConfig; import org.apache.druid.server.SegmentManager; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.partition.NoneShardSpec; @@ -39,12 +42,16 @@ import org.joda.time.Interval; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -67,6 +74,7 @@ public class SegmentLoadDropHandlerTest private final ObjectMapper jsonMapper = TestHelper.makeJsonMapper(); private SegmentLoadDropHandler segmentLoadDropHandler; + private DataSegmentAnnouncer announcer; private File infoDir; private AtomicInteger announceCount; @@ -74,22 +82,36 @@ public class SegmentLoadDropHandlerTest private CacheTestSegmentLoader segmentLoader; private SegmentManager segmentManager; private List scheduledRunnable; + private SegmentLoaderConfig segmentLoaderConfig; + private SegmentLoaderConfig segmentLoaderConfigNoLocations; + private ScheduledExecutorFactory scheduledExecutorFactory; + private List locations; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before public void setUp() { try { - infoDir = new File(File.createTempFile("blah", "blah2").getParent(), "ZkCoordinatorTest"); - infoDir.mkdirs(); - for (File file : infoDir.listFiles()) { - file.delete(); - } + infoDir = temporaryFolder.newFolder(); log.info("Creating tmp test files in [%s]", infoDir); } catch (IOException e) { throw new RuntimeException(e); } + locations = Collections.singletonList( + new StorageLocationConfig( + infoDir, + 100L, + 100d + ) + ); + scheduledRunnable = new ArrayList<>(); segmentLoader = new CacheTestSegmentLoader(); @@ -132,57 +154,91 @@ public void unannounceSegments(Iterable segments) } }; - segmentLoadDropHandler = new SegmentLoadDropHandler( - jsonMapper, - new SegmentLoaderConfig() - { - @Override - public File getInfoDir() - { - return infoDir; - } - @Override - public int getNumLoadingThreads() - { - return 5; - } + segmentLoaderConfig = new SegmentLoaderConfig() + { + @Override + public File getInfoDir() + { + return infoDir; + } - @Override - public int getAnnounceIntervalMillis() - { - return 50; - } + @Override + public int getNumLoadingThreads() + { + return 5; + } + + @Override + public int getAnnounceIntervalMillis() + { + return 50; + } + + @Override + public List getLocations() + { + return locations; + } + + @Override + public int getDropSegmentDelayMillis() + { + return 0; + } + }; + + segmentLoaderConfigNoLocations = new SegmentLoaderConfig() + { + @Override + public int getNumLoadingThreads() + { + return 5; + } + + @Override + public int getAnnounceIntervalMillis() + { + return 50; + } + + @Override + public int getDropSegmentDelayMillis() + { + return 0; + } + }; + + scheduledExecutorFactory = new ScheduledExecutorFactory() + { + @Override + public ScheduledExecutorService create(int corePoolSize, String nameFormat) + { + /* + Override normal behavoir by adding the runnable to a list so that you can make sure + all the shceduled runnables are executed by explicitly calling run() on each item in the list + */ + return new ScheduledThreadPoolExecutor(corePoolSize, Execs.makeThreadFactory(nameFormat)) + { @Override - public int getDropSegmentDelayMillis() + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { - return 0; + scheduledRunnable.add(command); + return null; } - }, + }; + } + }; + + segmentLoadDropHandler = new SegmentLoadDropHandler( + jsonMapper, + segmentLoaderConfig, announcer, EasyMock.createNiceMock(DataSegmentServerAnnouncer.class), segmentManager, - new ScheduledExecutorFactory() - { - @Override - public ScheduledExecutorService create(int corePoolSize, String nameFormat) - { - /* - Override normal behavoir by adding the runnable to a list so that you can make sure - all the shceduled runnables are executed by explicitly calling run() on each item in the list - */ - return new ScheduledThreadPoolExecutor(corePoolSize, Execs.makeThreadFactory(nameFormat)) - { - @Override - public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) - { - scheduledRunnable.add(command); - return null; - } - }; - } - }.create(5, "SegmentLoadDropHandlerTest-[%d]") + scheduledExecutorFactory.create(5, "SegmentLoadDropHandlerTest-[%d]"), + new ServerTypeConfig(ServerType.HISTORICAL) ); } @@ -220,6 +276,40 @@ Because another addSegment() call is executed, which removes the segment from se segmentLoadDropHandler.stop(); } + @Test + public void testSegmentLoading1BrokerWithNoLocations() throws Exception + { + SegmentLoadDropHandler segmentLoadDropHandlerBrokerWithNoLocations = new SegmentLoadDropHandler( + jsonMapper, + segmentLoaderConfigNoLocations, + announcer, + EasyMock.createNiceMock(DataSegmentServerAnnouncer.class), + segmentManager, + scheduledExecutorFactory.create(5, "SegmentLoadDropHandlerTest-brokerNoLocations-[%d]"), + new ServerTypeConfig(ServerType.BROKER) + ); + + segmentLoadDropHandlerBrokerWithNoLocations.start(); + segmentLoadDropHandler.stop(); + } + + @Test + public void testSegmentLoading1HistoricalWithNoLocations() + { + expectedException.expect(IAE.class); + expectedException.expectMessage("Segment cache locations must be set on historicals."); + + new SegmentLoadDropHandler( + jsonMapper, + segmentLoaderConfigNoLocations, + announcer, + EasyMock.createNiceMock(DataSegmentServerAnnouncer.class), + segmentManager, + scheduledExecutorFactory.create(5, "SegmentLoadDropHandlerTest-[%d]"), + new ServerTypeConfig(ServerType.HISTORICAL) + ); + } + /** * Steps: * 1. addSegment() succesfully loads the segment and annouces it @@ -382,13 +472,20 @@ public int getNumLoadingThreads() return 5; } + @Override + public List getLocations() + { + return locations; + } + @Override public int getAnnounceIntervalMillis() { return 50; } }, - announcer, EasyMock.createNiceMock(DataSegmentServerAnnouncer.class), segmentManager + announcer, EasyMock.createNiceMock(DataSegmentServerAnnouncer.class), segmentManager, + new ServerTypeConfig(ServerType.HISTORICAL) ); Set segments = new HashSet<>(); diff --git a/server/src/test/java/org/apache/druid/server/coordination/ZkCoordinatorTest.java b/server/src/test/java/org/apache/druid/server/coordination/ZkCoordinatorTest.java index 9bdd84ed70fb..c30a37cb01ad 100644 --- a/server/src/test/java/org/apache/druid/server/coordination/ZkCoordinatorTest.java +++ b/server/src/test/java/org/apache/druid/server/coordination/ZkCoordinatorTest.java @@ -23,10 +23,13 @@ import com.google.common.collect.ImmutableMap; import org.apache.curator.utils.ZKPaths; import org.apache.druid.curator.CuratorTestBase; +import org.apache.druid.guice.ServerTypeConfig; import org.apache.druid.java.util.common.Intervals; +import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.java.util.emitter.EmittingLogger; import org.apache.druid.segment.IndexIO; import org.apache.druid.segment.loading.SegmentLoaderConfig; +import org.apache.druid.segment.loading.StorageLocationConfig; import org.apache.druid.server.SegmentManager; import org.apache.druid.server.ServerTestHelper; import org.apache.druid.server.initialization.ZkPathsConfig; @@ -37,9 +40,15 @@ import org.easymock.EasyMock; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import java.io.File; +import java.io.IOException; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ScheduledExecutorService; @@ -47,6 +56,8 @@ */ public class ZkCoordinatorTest extends CuratorTestBase { + private static final Logger log = new Logger(ZkCoordinatorTest.class); + private final ObjectMapper jsonMapper = ServerTestHelper.MAPPER; private final DruidServerMetadata me = new DruidServerMetadata( "dummyServer", @@ -67,9 +78,31 @@ public String getBase() }; private ZkCoordinator zkCoordinator; + private File infoDir; + private List locations; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Before public void setUp() throws Exception { + try { + infoDir = temporaryFolder.newFolder(); + log.info("Creating tmp test files in [%s]", infoDir); + } + catch (IOException e) { + throw new RuntimeException(e); + } + + locations = Collections.singletonList( + new StorageLocationConfig( + infoDir, + 100L, + 100d + ) + ); + setupServerAndCurator(); curator.start(); curator.blockUntilConnected(); @@ -102,11 +135,42 @@ public void testLoadDrop() throws Exception SegmentLoadDropHandler segmentLoadDropHandler = new SegmentLoadDropHandler( ServerTestHelper.MAPPER, - new SegmentLoaderConfig(), + new SegmentLoaderConfig() { + @Override + public File getInfoDir() + { + return infoDir; + } + + @Override + public int getNumLoadingThreads() + { + return 5; + } + + @Override + public int getAnnounceIntervalMillis() + { + return 50; + } + + @Override + public List getLocations() + { + return locations; + } + + @Override + public int getDropSegmentDelayMillis() + { + return 0; + } + }, EasyMock.createNiceMock(DataSegmentAnnouncer.class), EasyMock.createNiceMock(DataSegmentServerAnnouncer.class), EasyMock.createNiceMock(SegmentManager.class), - EasyMock.createNiceMock(ScheduledExecutorService.class) + EasyMock.createNiceMock(ScheduledExecutorService.class), + new ServerTypeConfig(ServerType.HISTORICAL) ) { @Override diff --git a/server/src/test/java/org/apache/druid/server/coordinator/BalanceSegmentsTest.java b/server/src/test/java/org/apache/druid/server/coordinator/BalanceSegmentsTest.java index 084a119ebe76..f37c92cb1056 100644 --- a/server/src/test/java/org/apache/druid/server/coordinator/BalanceSegmentsTest.java +++ b/server/src/test/java/org/apache/druid/server/coordinator/BalanceSegmentsTest.java @@ -43,6 +43,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -66,9 +67,11 @@ public class BalanceSegmentsTest private DataSegment segment2; private DataSegment segment3; private DataSegment segment4; + private DataSegment segment5; private List segments; private ListeningExecutorService balancerStrategyExecutor; private BalancerStrategy balancerStrategy; + private Set broadcastDatasources; @Before public void setUp() @@ -82,6 +85,7 @@ public void setUp() segment2 = EasyMock.createMock(DataSegment.class); segment3 = EasyMock.createMock(DataSegment.class); segment4 = EasyMock.createMock(DataSegment.class); + segment5 = EasyMock.createMock(DataSegment.class); DateTime start1 = DateTimes.of("2012-01-01"); DateTime start2 = DateTimes.of("2012-02-01"); @@ -130,12 +134,24 @@ public void setUp() 0, 8L ); + segment5 = new DataSegment( + "datasourceBroadcast", + new Interval(start2, start2.plusHours(1)), + version.toString(), + new HashMap<>(), + new ArrayList<>(), + new ArrayList<>(), + NoneShardSpec.instance(), + 0, + 8L + ); segments = new ArrayList<>(); segments.add(segment1); segments.add(segment2); segments.add(segment3); segments.add(segment4); + segments.add(segment5); peon1 = new LoadQueuePeonTester(); peon2 = new LoadQueuePeonTester(); @@ -147,6 +163,8 @@ public void setUp() balancerStrategyExecutor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1)); balancerStrategy = new CostBalancerStrategyFactory().createBalancerStrategy(balancerStrategyExecutor); + + broadcastDatasources = Collections.singleton("datasourceBroadcast"); } @After @@ -187,10 +205,11 @@ public void testMoveToEmptyServerBalancer() ImmutableList.of(peon1, peon2) ) .withBalancerStrategy(predefinedPickOrderStrategy) + .withBroadcastDatasources(broadcastDatasources) .build(); params = new BalanceSegmentsTester(coordinator).run(params); - Assert.assertEquals(2, params.getCoordinatorStats().getTieredStat("movedCount", "normal")); + Assert.assertEquals(3, params.getCoordinatorStats().getTieredStat("movedCount", "normal")); } /** @@ -213,10 +232,10 @@ public void testMoveDecommissioningMaxPercentOfMaxSegmentsToMove() mockCoordinator(coordinator); BalancerStrategy strategy = EasyMock.createMock(BalancerStrategy.class); - EasyMock.expect(strategy.pickSegmentToMove(ImmutableList.of(new ServerHolder(druidServer2, peon2, false)))) + EasyMock.expect(strategy.pickSegmentToMove(ImmutableList.of(new ServerHolder(druidServer2, peon2, false)), broadcastDatasources)) .andReturn(new BalancerSegmentHolder(druidServer2, segment3)) .andReturn(new BalancerSegmentHolder(druidServer2, segment4)); - EasyMock.expect(strategy.pickSegmentToMove(EasyMock.anyObject())) + EasyMock.expect(strategy.pickSegmentToMove(EasyMock.anyObject(), EasyMock.anyObject())) .andReturn(new BalancerSegmentHolder(druidServer1, segment1)) .andReturn(new BalancerSegmentHolder(druidServer1, segment2)); @@ -237,6 +256,7 @@ public void testMoveDecommissioningMaxPercentOfMaxSegmentsToMove() .build() // ceil(3 * 0.6) = 2 segments from decommissioning servers ) .withBalancerStrategy(strategy) + .withBroadcastDatasources(broadcastDatasources) .build(); params = new BalanceSegmentsTester(coordinator).run(params); @@ -280,7 +300,7 @@ public void testMoveDecommissioningMaxPercentOfMaxSegmentsToMoveWithNoDecommissi mockCoordinator(coordinator); BalancerStrategy strategy = EasyMock.createMock(BalancerStrategy.class); - EasyMock.expect(strategy.pickSegmentToMove(EasyMock.anyObject())) + EasyMock.expect(strategy.pickSegmentToMove(EasyMock.anyObject(), EasyMock.anyObject())) .andReturn(new BalancerSegmentHolder(druidServer1, segment1)) .andReturn(new BalancerSegmentHolder(druidServer1, segment2)) .andReturn(new BalancerSegmentHolder(druidServer2, segment3)) @@ -303,6 +323,7 @@ public void testMoveDecommissioningMaxPercentOfMaxSegmentsToMoveWithNoDecommissi .build() ) .withBalancerStrategy(strategy) + .withBroadcastDatasources(broadcastDatasources) .build(); params = new BalanceSegmentsTester(coordinator).run(params); @@ -328,7 +349,7 @@ public void testMoveToDecommissioningServer() mockCoordinator(coordinator); BalancerStrategy strategy = EasyMock.createMock(BalancerStrategy.class); - EasyMock.expect(strategy.pickSegmentToMove(EasyMock.anyObject())) + EasyMock.expect(strategy.pickSegmentToMove(EasyMock.anyObject(), EasyMock.anyObject())) .andReturn(new BalancerSegmentHolder(druidServer1, segment1)) .anyTimes(); EasyMock.expect(strategy.findNewSegmentHomeBalancer(EasyMock.anyObject(), EasyMock.anyObject())).andAnswer(() -> { @@ -343,6 +364,7 @@ public void testMoveToDecommissioningServer() ImmutableList.of(false, true) ) .withBalancerStrategy(strategy) + .withBroadcastDatasources(broadcastDatasources) .build(); params = new BalanceSegmentsTester(coordinator).run(params); @@ -362,7 +384,7 @@ public void testMoveFromDecommissioningServer() ServerHolder holder2 = new ServerHolder(druidServer2, peon2, false); BalancerStrategy strategy = EasyMock.createMock(BalancerStrategy.class); - EasyMock.expect(strategy.pickSegmentToMove(EasyMock.anyObject())) + EasyMock.expect(strategy.pickSegmentToMove(EasyMock.anyObject(), EasyMock.anyObject())) .andReturn(new BalancerSegmentHolder(druidServer1, segment1)) .once(); EasyMock.expect(strategy.findNewSegmentHomeBalancer(EasyMock.anyObject(), EasyMock.anyObject())) @@ -377,6 +399,7 @@ public void testMoveFromDecommissioningServer() ) .withDynamicConfigs(CoordinatorDynamicConfig.builder().withMaxSegmentsToMove(1).build()) .withBalancerStrategy(strategy) + .withBroadcastDatasources(broadcastDatasources) .build(); params = new BalanceSegmentsTester(coordinator).run(params); @@ -412,6 +435,7 @@ public void testMoveMaxLoadQueueServerBalancer() ImmutableList.of(peon1, peon2) ) .withBalancerStrategy(predefinedPickOrderStrategy) + .withBroadcastDatasources(broadcastDatasources) .withDynamicConfigs( CoordinatorDynamicConfig .builder() @@ -451,6 +475,7 @@ public void testMoveSameSegmentTwice() ImmutableList.of(peon1, peon2) ) .withBalancerStrategy(predefinedPickOrderStrategy) + .withBroadcastDatasources(broadcastDatasources) .withDynamicConfigs( CoordinatorDynamicConfig.builder().withMaxSegmentsToMove( 2 @@ -542,6 +567,7 @@ private DruidCoordinatorRuntimeParams.Builder defaultRuntimeParamsBuilder( ) .withUsedSegmentsInTest(segments) .withDynamicConfigs(CoordinatorDynamicConfig.builder().withMaxSegmentsToMove(MAX_SEGMENTS_TO_MOVE).build()) + .withBroadcastDatasources(broadcastDatasources) .withBalancerStrategy(balancerStrategy); } @@ -611,7 +637,7 @@ public ServerHolder findNewSegmentHomeReplicator(DataSegment proposalSegment, Li } @Override - public BalancerSegmentHolder pickSegmentToMove(List serverHolders) + public BalancerSegmentHolder pickSegmentToMove(List serverHolders, Set broadcastDatasources) { return pickOrder.get(pickCounter.getAndIncrement() % pickOrder.size()); } @@ -635,9 +661,9 @@ private DruidCoordinatorRuntimeParams setupParamsForDecommissioningMaxPercentOfM // either decommissioning servers list or acitve ones (ie servers list is [2] or [1, 3]) BalancerStrategy strategy = EasyMock.createMock(BalancerStrategy.class); - EasyMock.expect(strategy.pickSegmentToMove(ImmutableList.of(new ServerHolder(druidServer2, peon2, true)))) + EasyMock.expect(strategy.pickSegmentToMove(ImmutableList.of(new ServerHolder(druidServer2, peon2, true)), broadcastDatasources)) .andReturn(new BalancerSegmentHolder(druidServer2, segment2)); - EasyMock.expect(strategy.pickSegmentToMove(EasyMock.anyObject())) + EasyMock.expect(strategy.pickSegmentToMove(EasyMock.anyObject(), EasyMock.anyObject())) .andReturn(new BalancerSegmentHolder(druidServer1, segment1)); EasyMock.expect(strategy.findNewSegmentHomeBalancer(EasyMock.anyObject(), EasyMock.anyObject())) .andReturn(new ServerHolder(druidServer3, peon3)) @@ -656,6 +682,7 @@ private DruidCoordinatorRuntimeParams setupParamsForDecommissioningMaxPercentOfM .build() ) .withBalancerStrategy(strategy) + .withBroadcastDatasources(broadcastDatasources) .build(); } } diff --git a/server/src/test/java/org/apache/druid/server/coordinator/DruidClusterBuilder.java b/server/src/test/java/org/apache/druid/server/coordinator/DruidClusterBuilder.java index 772b7aec1401..5fb100073eae 100644 --- a/server/src/test/java/org/apache/druid/server/coordinator/DruidClusterBuilder.java +++ b/server/src/test/java/org/apache/druid/server/coordinator/DruidClusterBuilder.java @@ -35,6 +35,7 @@ public static DruidClusterBuilder newBuilder() private @Nullable Set realtimes = null; private final Map> historicals = new HashMap<>(); + private @Nullable Set brokers = null; private DruidClusterBuilder() { @@ -46,6 +47,12 @@ public DruidClusterBuilder withRealtimes(ServerHolder... realtimes) return this; } + public DruidClusterBuilder withBrokers(ServerHolder... brokers) + { + this.brokers = new HashSet<>(Arrays.asList(brokers)); + return this; + } + public DruidClusterBuilder addTier(String tierName, ServerHolder... historicals) { if (this.historicals.putIfAbsent(tierName, Arrays.asList(historicals)) != null) { @@ -56,6 +63,6 @@ public DruidClusterBuilder addTier(String tierName, ServerHolder... historicals) public DruidCluster build() { - return DruidCluster.createDruidClusterFromBuilderInTest(realtimes, historicals); + return DruidCluster.createDruidClusterFromBuilderInTest(realtimes, historicals, brokers); } } diff --git a/server/src/test/java/org/apache/druid/server/coordinator/ReservoirSegmentSamplerTest.java b/server/src/test/java/org/apache/druid/server/coordinator/ReservoirSegmentSamplerTest.java index 73e829ce0e0c..8aef2f2e10fb 100644 --- a/server/src/test/java/org/apache/druid/server/coordinator/ReservoirSegmentSamplerTest.java +++ b/server/src/test/java/org/apache/druid/server/coordinator/ReservoirSegmentSamplerTest.java @@ -23,6 +23,7 @@ import org.apache.druid.client.ImmutableDruidServer; import org.apache.druid.client.ImmutableDruidServerTests; import org.apache.druid.java.util.common.DateTimes; +import org.apache.druid.server.coordination.ServerType; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.partition.NoneShardSpec; import org.easymock.EasyMock; @@ -136,6 +137,7 @@ public void setUp() @Test public void getRandomBalancerSegmentHolderTest() { + EasyMock.expect(druidServer1.getType()).andReturn(ServerType.HISTORICAL).atLeastOnce(); EasyMock.expect(druidServer1.getName()).andReturn("1").atLeastOnce(); EasyMock.expect(druidServer1.getCurrSize()).andReturn(30L).atLeastOnce(); EasyMock.expect(druidServer1.getMaxSize()).andReturn(100L).atLeastOnce(); @@ -143,6 +145,7 @@ public void getRandomBalancerSegmentHolderTest() EasyMock.expect(druidServer1.getSegment(EasyMock.anyObject())).andReturn(null).anyTimes(); EasyMock.replay(druidServer1); + EasyMock.expect(druidServer2.getType()).andReturn(ServerType.HISTORICAL).atLeastOnce(); EasyMock.expect(druidServer2.getName()).andReturn("2").atLeastOnce(); EasyMock.expect(druidServer2.getTier()).andReturn("normal").anyTimes(); EasyMock.expect(druidServer2.getCurrSize()).andReturn(30L).atLeastOnce(); @@ -151,6 +154,7 @@ public void getRandomBalancerSegmentHolderTest() EasyMock.expect(druidServer2.getSegment(EasyMock.anyObject())).andReturn(null).anyTimes(); EasyMock.replay(druidServer2); + EasyMock.expect(druidServer3.getType()).andReturn(ServerType.HISTORICAL).atLeastOnce(); EasyMock.expect(druidServer3.getName()).andReturn("3").atLeastOnce(); EasyMock.expect(druidServer3.getTier()).andReturn("normal").anyTimes(); EasyMock.expect(druidServer3.getCurrSize()).andReturn(30L).atLeastOnce(); @@ -159,6 +163,7 @@ public void getRandomBalancerSegmentHolderTest() EasyMock.expect(druidServer3.getSegment(EasyMock.anyObject())).andReturn(null).anyTimes(); EasyMock.replay(druidServer3); + EasyMock.expect(druidServer4.getType()).andReturn(ServerType.HISTORICAL).atLeastOnce(); EasyMock.expect(druidServer4.getName()).andReturn("4").atLeastOnce(); EasyMock.expect(druidServer4.getTier()).andReturn("normal").anyTimes(); EasyMock.expect(druidServer4.getCurrSize()).andReturn(30L).atLeastOnce(); @@ -186,7 +191,7 @@ public void getRandomBalancerSegmentHolderTest() Map segmentCountMap = new HashMap<>(); for (int i = 0; i < 5000; i++) { - segmentCountMap.put(ReservoirSegmentSampler.getRandomBalancerSegmentHolder(holderList).getSegment(), 1); + segmentCountMap.put(ReservoirSegmentSampler.getRandomBalancerSegmentHolder(holderList, Collections.emptySet()).getSegment(), 1); } for (DataSegment segment : segments) { diff --git a/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleSerdeTest.java b/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleSerdeTest.java index e3b51a51b02d..0dfe0eab0ac1 100644 --- a/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleSerdeTest.java +++ b/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleSerdeTest.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.apache.druid.jackson.DefaultObjectMapper; import org.apache.druid.java.util.common.Intervals; @@ -44,15 +43,15 @@ public class BroadcastDistributionRuleSerdeTest public static List constructorFeeder() { return Lists.newArrayList( - new Object[]{new ForeverBroadcastDistributionRule(ImmutableList.of("large_source1", "large_source2"))}, - new Object[]{new ForeverBroadcastDistributionRule(ImmutableList.of())}, - new Object[]{new ForeverBroadcastDistributionRule(null)}, - new Object[]{new IntervalBroadcastDistributionRule(Intervals.of("0/1000"), ImmutableList.of("large_source"))}, - new Object[]{new IntervalBroadcastDistributionRule(Intervals.of("0/1000"), ImmutableList.of())}, - new Object[]{new IntervalBroadcastDistributionRule(Intervals.of("0/1000"), null)}, - new Object[]{new PeriodBroadcastDistributionRule(new Period(1000), null, ImmutableList.of("large_source"))}, - new Object[]{new PeriodBroadcastDistributionRule(new Period(1000), null, ImmutableList.of())}, - new Object[]{new PeriodBroadcastDistributionRule(new Period(1000), null, null)} + new Object[]{new ForeverBroadcastDistributionRule()}, + new Object[]{new ForeverBroadcastDistributionRule()}, + new Object[]{new ForeverBroadcastDistributionRule()}, + new Object[]{new IntervalBroadcastDistributionRule(Intervals.of("0/1000"))}, + new Object[]{new IntervalBroadcastDistributionRule(Intervals.of("0/1000"))}, + new Object[]{new IntervalBroadcastDistributionRule(Intervals.of("0/1000"))}, + new Object[]{new PeriodBroadcastDistributionRule(new Period(1000), null)}, + new Object[]{new PeriodBroadcastDistributionRule(new Period(1000), null)}, + new Object[]{new PeriodBroadcastDistributionRule(new Period(1000), null)} ); } diff --git a/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleTest.java b/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleTest.java index 70ec3ebec052..c2d4fd3d0004 100644 --- a/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleTest.java +++ b/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleTest.java @@ -19,7 +19,6 @@ package org.apache.druid.server.coordinator.rules; -import com.google.common.collect.ImmutableList; import org.apache.druid.client.DruidServer; import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.Intervals; @@ -269,7 +268,7 @@ public void setUp() public void testBroadcastToSingleDataSource() { final ForeverBroadcastDistributionRule rule = - new ForeverBroadcastDistributionRule(ImmutableList.of("large_source")); + new ForeverBroadcastDistributionRule(); CoordinatorStats stats = rule.run( null, @@ -285,7 +284,7 @@ public void testBroadcastToSingleDataSource() smallSegment ); - Assert.assertEquals(3L, stats.getGlobalStat(LoadRule.ASSIGNED_COUNT)); + Assert.assertEquals(5L, stats.getGlobalStat(LoadRule.ASSIGNED_COUNT)); Assert.assertFalse(stats.hasPerTierStats()); Assert.assertTrue( @@ -295,10 +294,10 @@ public void testBroadcastToSingleDataSource() Assert.assertTrue( holdersOfLargeSegments2.stream() - .noneMatch(holder -> holder.getPeon().getSegmentsToLoad().contains(smallSegment)) + .allMatch(holder -> holder.getPeon().getSegmentsToLoad().contains(smallSegment)) ); - Assert.assertFalse(holderOfSmallSegment.getPeon().getSegmentsToLoad().contains(smallSegment)); + Assert.assertTrue(holderOfSmallSegment.isServingSegment(smallSegment)); } private static DruidCoordinatorRuntimeParams makeCoordinartorRuntimeParams( @@ -331,7 +330,7 @@ private static DruidCoordinatorRuntimeParams makeCoordinartorRuntimeParams( public void testBroadcastDecommissioning() { final ForeverBroadcastDistributionRule rule = - new ForeverBroadcastDistributionRule(ImmutableList.of("large_source")); + new ForeverBroadcastDistributionRule(); CoordinatorStats stats = rule.run( null, @@ -356,7 +355,6 @@ public void testBroadcastDecommissioning() public void testBroadcastToMultipleDataSources() { final ForeverBroadcastDistributionRule rule = new ForeverBroadcastDistributionRule( - ImmutableList.of("large_source", "large_source2") ); CoordinatorStats stats = rule.run( @@ -392,7 +390,7 @@ public void testBroadcastToMultipleDataSources() @Test public void testBroadcastToAllServers() { - final ForeverBroadcastDistributionRule rule = new ForeverBroadcastDistributionRule(null); + final ForeverBroadcastDistributionRule rule = new ForeverBroadcastDistributionRule(); CoordinatorStats stats = rule.run( null, @@ -408,14 +406,14 @@ public void testBroadcastToAllServers() smallSegment ); - Assert.assertEquals(6L, stats.getGlobalStat(LoadRule.ASSIGNED_COUNT)); + Assert.assertEquals(5L, stats.getGlobalStat(LoadRule.ASSIGNED_COUNT)); Assert.assertFalse(stats.hasPerTierStats()); Assert.assertTrue( druidCluster .getAllServers() .stream() - .allMatch(holder -> holder.getPeon().getSegmentsToLoad().contains(smallSegment)) + .allMatch(holder -> holder.isLoadingSegment(smallSegment) || holder.isServingSegment(smallSegment)) ); } } diff --git a/services/src/main/java/org/apache/druid/cli/CliBroker.java b/services/src/main/java/org/apache/druid/cli/CliBroker.java index 5badcb0e0e3a..18d2813fa411 100644 --- a/services/src/main/java/org/apache/druid/cli/CliBroker.java +++ b/services/src/main/java/org/apache/druid/cli/CliBroker.java @@ -33,6 +33,7 @@ import org.apache.druid.client.selector.CustomTierSelectorStrategyConfig; import org.apache.druid.client.selector.ServerSelectorStrategy; import org.apache.druid.client.selector.TierSelectorStrategy; +import org.apache.druid.discovery.DataNodeService; import org.apache.druid.discovery.LookupNodeService; import org.apache.druid.discovery.NodeRole; import org.apache.druid.guice.CacheModule; @@ -42,9 +43,11 @@ import org.apache.druid.guice.JsonConfigProvider; import org.apache.druid.guice.LazySingleton; import org.apache.druid.guice.LifecycleModule; +import org.apache.druid.guice.ManageLifecycle; import org.apache.druid.guice.QueryRunnerFactoryModule; import org.apache.druid.guice.QueryableModule; import org.apache.druid.guice.SegmentWranglerModule; +import org.apache.druid.guice.ServerTypeConfig; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.query.QuerySegmentWalker; import org.apache.druid.query.RetryQueryRunnerConfig; @@ -52,7 +55,12 @@ import org.apache.druid.server.BrokerQueryResource; import org.apache.druid.server.ClientInfoResource; import org.apache.druid.server.ClientQuerySegmentWalker; +import org.apache.druid.server.SegmentManager; +import org.apache.druid.server.coordination.ServerType; +import org.apache.druid.server.coordination.ZkCoordinator; import org.apache.druid.server.http.BrokerResource; +import org.apache.druid.server.http.HistoricalResource; +import org.apache.druid.server.http.SegmentListerResource; import org.apache.druid.server.http.SelfDiscoveryResource; import org.apache.druid.server.initialization.jetty.JettyServerInitializer; import org.apache.druid.server.metrics.QueryCountStatsProvider; @@ -123,12 +131,19 @@ protected List getModules() Jerseys.addResource(binder, HttpServerInventoryViewResource.class); LifecycleModule.register(binder, Server.class); + binder.bind(SegmentManager.class).in(LazySingleton.class); + binder.bind(ZkCoordinator.class).in(ManageLifecycle.class); + binder.bind(ServerTypeConfig.class).toInstance(new ServerTypeConfig(ServerType.BROKER)); + Jerseys.addResource(binder, HistoricalResource.class); + Jerseys.addResource(binder, SegmentListerResource.class); + + LifecycleModule.register(binder, ZkCoordinator.class); bindNodeRoleAndAnnouncer( binder, DiscoverySideEffectsProvider .builder(NodeRole.BROKER) - .serviceClasses(ImmutableList.of(LookupNodeService.class)) + .serviceClasses(ImmutableList.of(DataNodeService.class, LookupNodeService.class)) .useLegacyAnnouncer(true) .build() ); diff --git a/services/src/main/java/org/apache/druid/cli/CliIndexer.java b/services/src/main/java/org/apache/druid/cli/CliIndexer.java index 7483fec93a93..d4a8df18606b 100644 --- a/services/src/main/java/org/apache/druid/cli/CliIndexer.java +++ b/services/src/main/java/org/apache/druid/cli/CliIndexer.java @@ -25,9 +25,9 @@ import com.google.inject.Module; import com.google.inject.Provides; import com.google.inject.name.Names; -import com.google.inject.util.Providers; import io.airlift.airline.Command; import org.apache.druid.client.DruidServer; +import org.apache.druid.client.DruidServerConfig; import org.apache.druid.discovery.DataNodeService; import org.apache.druid.discovery.LookupNodeService; import org.apache.druid.discovery.NodeRole; @@ -43,6 +43,7 @@ import org.apache.druid.guice.JsonConfigProvider; import org.apache.druid.guice.LazySingleton; import org.apache.druid.guice.LifecycleModule; +import org.apache.druid.guice.ManageLifecycle; import org.apache.druid.guice.QueryRunnerFactoryModule; import org.apache.druid.guice.QueryableModule; import org.apache.druid.guice.QueryablePeonModule; @@ -60,12 +61,13 @@ import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.query.QuerySegmentWalker; import org.apache.druid.query.lookup.LookupModule; -import org.apache.druid.segment.realtime.CliIndexerDataSegmentServerAnnouncerLifecycleHandler; import org.apache.druid.segment.realtime.appenderator.AppenderatorsManager; import org.apache.druid.segment.realtime.appenderator.UnifiedIndexerAppenderatorsManager; import org.apache.druid.server.DruidNode; -import org.apache.druid.server.coordination.SegmentLoadDropHandler; +import org.apache.druid.server.SegmentManager; import org.apache.druid.server.coordination.ServerType; +import org.apache.druid.server.coordination.ZkCoordinator; +import org.apache.druid.server.http.HistoricalResource; import org.apache.druid.server.http.SegmentListerResource; import org.apache.druid.server.initialization.jetty.CliIndexerServerModule; import org.apache.druid.server.initialization.jetty.JettyServerInitializer; @@ -138,14 +140,14 @@ public void configure(Binder binder) binder.bind(JettyServerInitializer.class).to(QueryJettyServerInitializer.class); Jerseys.addResource(binder, SegmentListerResource.class); - - LifecycleModule.register(binder, CliIndexerDataSegmentServerAnnouncerLifecycleHandler.class); - Jerseys.addResource(binder, ShuffleResource.class); LifecycleModule.register(binder, Server.class, RemoteChatHandler.class); - binder.bind(SegmentLoadDropHandler.class).toProvider(Providers.of(null)); + binder.bind(SegmentManager.class).in(LazySingleton.class); + binder.bind(ZkCoordinator.class).in(ManageLifecycle.class); + Jerseys.addResource(binder, HistoricalResource.class); + LifecycleModule.register(binder, ZkCoordinator.class); bindNodeRoleAndAnnouncer( binder, @@ -186,11 +188,11 @@ public WorkerNodeService getWorkerNodeService(WorkerConfig workerConfig) @Provides @LazySingleton - public DataNodeService getDataNodeService() + public DataNodeService getDataNodeService(DruidServerConfig serverConfig) { return new DataNodeService( DruidServer.DEFAULT_TIER, - 0L, + serverConfig.getMaxSize(), ServerType.INDEXER_EXECUTOR, DruidServer.DEFAULT_PRIORITY ); diff --git a/services/src/main/java/org/apache/druid/cli/CliPeon.java b/services/src/main/java/org/apache/druid/cli/CliPeon.java index 1160eb9240c1..59ef48aee96a 100644 --- a/services/src/main/java/org/apache/druid/cli/CliPeon.java +++ b/services/src/main/java/org/apache/druid/cli/CliPeon.java @@ -61,7 +61,6 @@ import org.apache.druid.guice.annotations.Json; import org.apache.druid.guice.annotations.Parent; import org.apache.druid.guice.annotations.Self; -import org.apache.druid.guice.annotations.Smile; import org.apache.druid.indexing.common.RetryPolicyConfig; import org.apache.druid.indexing.common.RetryPolicyFactory; import org.apache.druid.indexing.common.SingleFileTaskReportFileWriter; @@ -109,15 +108,16 @@ import org.apache.druid.segment.realtime.plumber.CoordinatorBasedSegmentHandoffNotifierFactory; import org.apache.druid.segment.realtime.plumber.SegmentHandoffNotifierFactory; import org.apache.druid.server.DruidNode; -import org.apache.druid.server.coordination.BatchDataSegmentAnnouncer; +import org.apache.druid.server.SegmentManager; import org.apache.druid.server.coordination.ServerType; +import org.apache.druid.server.coordination.ZkCoordinator; +import org.apache.druid.server.http.HistoricalResource; import org.apache.druid.server.http.SegmentListerResource; import org.apache.druid.server.initialization.jetty.ChatHandlerServerModule; import org.apache.druid.server.initialization.jetty.JettyServerInitializer; import org.apache.druid.server.metrics.DataSourceTaskIdHolder; import org.eclipse.jetty.server.Server; -import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import java.util.List; @@ -154,6 +154,14 @@ public class CliPeon extends GuiceRunnable @Option(name = "--nodeType", title = "nodeType", description = "Set the node type to expose on ZK") public String serverType = "indexer-executor"; + + /** + * If set to "true", the peon will bind classes necessary for loading broadcast segments. This is used for + * queryable tasks, such as streaming ingestion tasks. + */ + @Option(name = "--loadBroadcastSegments", title = "loadBroadcastSegments", description = "Enable loading of broadcast segments") + public String loadBroadcastSegments = "false"; + private static final Logger log = new Logger(CliPeon.class); @Inject @@ -174,6 +182,7 @@ protected List getModules() new JoinableFactoryModule(), new Module() { + @SuppressForbidden(reason = "System#out, System#err") @Override public void configure(Binder binder) { @@ -218,6 +227,13 @@ public void configure(Binder binder) Jerseys.addResource(binder, SegmentListerResource.class); binder.bind(ServerTypeConfig.class).toInstance(new ServerTypeConfig(ServerType.fromString(serverType))); LifecycleModule.register(binder, Server.class); + + if ("true".equals(loadBroadcastSegments)) { + binder.bind(SegmentManager.class).in(LazySingleton.class); + binder.bind(ZkCoordinator.class).in(ManageLifecycle.class); + Jerseys.addResource(binder, HistoricalResource.class); + LifecycleModule.register(binder, ZkCoordinator.class); + } } @Provides @@ -247,16 +263,6 @@ public String getTaskIDFromTask(final Task task) { return task.getId(); } - - @Provides - public SegmentListerResource getSegmentListerResource( - @Json ObjectMapper jsonMapper, - @Smile ObjectMapper smileMapper, - @Nullable BatchDataSegmentAnnouncer announcer - ) - { - return new SegmentListerResource(jsonMapper, smileMapper, announcer, null); - } }, new QueryablePeonModule(), new IndexingServiceFirehoseModule(), diff --git a/services/src/test/java/org/apache/druid/cli/MainTest.java b/services/src/test/java/org/apache/druid/cli/MainTest.java index 3e960f69e442..a1fbed581e5d 100644 --- a/services/src/test/java/org/apache/druid/cli/MainTest.java +++ b/services/src/test/java/org/apache/druid/cli/MainTest.java @@ -50,7 +50,9 @@ public static Iterable constructorFeeder() //new Object[]{new CliInternalHadoopIndexer()}, new Object[]{new CliMiddleManager()}, - new Object[]{new CliRouter()} + new Object[]{new CliRouter()}, + + new Object[]{new CliIndexer()} ); } diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java b/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java index 9f087eaf9ccd..54e7e5a8f961 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java @@ -355,7 +355,7 @@ void addSegment(final DruidServerMetadata server, final DataSegment segment) AvailableSegmentMetadata segmentMetadata = knownSegments != null ? knownSegments.get(segment.getId()) : null; if (segmentMetadata == null) { // segmentReplicatable is used to determine if segments are served by historical or realtime servers - long isRealtime = server.segmentReplicatable() ? 0 : 1; + long isRealtime = server.isSegmentReplicationTarget() ? 0 : 1; segmentMetadata = AvailableSegmentMetadata.builder( segment, isRealtime, @@ -366,7 +366,7 @@ void addSegment(final DruidServerMetadata server, final DataSegment segment) // Unknown segment. setAvailableSegmentMetadata(segment.getId(), segmentMetadata); segmentsNeedingRefresh.add(segment.getId()); - if (!server.segmentReplicatable()) { + if (!server.isSegmentReplicationTarget()) { log.debug("Added new mutable segment[%s].", segment.getId()); mutableSegments.add(segment.getId()); } else { @@ -384,7 +384,7 @@ void addSegment(final DruidServerMetadata server, final DataSegment segment) .withRealtime(recomputeIsRealtime(servers)) .build(); knownSegments.put(segment.getId(), metadataWithNumReplicas); - if (server.segmentReplicatable()) { + if (server.isSegmentReplicationTarget()) { // If a segment shows up on a replicatable (historical) server at any point, then it must be immutable, // even if it's also available on non-replicatable (realtime) servers. mutableSegments.remove(segment.getId()); diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index e3579aad6a94..083285870044 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -11887,7 +11887,7 @@ public void testTimeExtractWithTooFewArguments() throws Exception @Test @Parameters(source = QueryContextForJoinProvider.class) - public void testNestedGroupByOnInlineDataSourceWithFilterIsNotSupported(Map queryContext) throws Exception + public void testNestedGroupByOnInlineDataSourceWithFilter(Map queryContext) throws Exception { try { testQuery( From 4e660b61e6eee1407cfd260a317877925a9ea671 Mon Sep 17 00:00:00 2001 From: Lucas Capistrant Date: Tue, 9 Jun 2020 12:31:08 -0500 Subject: [PATCH 066/107] small fixes to configuration documentation (#9975) --- docs/configuration/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/configuration/index.md b/docs/configuration/index.md index 0f2175b3aa1c..34b458a49b2b 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -709,7 +709,7 @@ These Coordinator static configurations can be defined in the `coordinator/runti |`druid.manager.config.pollDuration`|How often the manager polls the config table for updates.|PT1M| |`druid.manager.segments.pollDuration`|The duration between polls the Coordinator does for updates to the set of active segments. Generally defines the amount of lag time it can take for the Coordinator to notice new segments.|PT1M| |`druid.manager.rules.pollDuration`|The duration between polls the Coordinator does for updates to the set of active rules. Generally defines the amount of lag time it can take for the Coordinator to notice rules.|PT1M| -|`druid.manager.rules.defaultTier`|The default tier from which default rules will be loaded from.|_default| +|`druid.manager.rules.defaultRule`|The default rule for the cluster|_default| |`druid.manager.rules.alertThreshold`|The duration after a failed poll upon which an alert should be emitted.|PT10M| #### Dynamic Configuration @@ -1204,7 +1204,7 @@ You can optionally configure caching to be enabled on the peons by setting cachi |`druid.realtime.cache.useCache`|true, false|Enable the cache on the realtime.|false| |`druid.realtime.cache.populateCache`|true, false|Populate the cache on the realtime.|false| |`druid.realtime.cache.unCacheable`|All druid query types|All query types to not cache.|`[]`| -|`druid.realtime.cache.maxEntrySize`|Maximum cache entry size in bytes.|1_000_000| +|`druid.realtime.cache.maxEntrySize`|positive integer|Maximum cache entry size in bytes.|1_000_000| See [cache configuration](#cache-configuration) for how to configure cache settings. @@ -1344,7 +1344,7 @@ You can optionally configure caching to be enabled on the Indexer by setting cac |`druid.realtime.cache.useCache`|true, false|Enable the cache on the realtime.|false| |`druid.realtime.cache.populateCache`|true, false|Populate the cache on the realtime.|false| |`druid.realtime.cache.unCacheable`|All druid query types|All query types to not cache.|`[]`| -|`druid.realtime.cache.maxEntrySize`|Maximum cache entry size in bytes.|1_000_000| +|`druid.realtime.cache.maxEntrySize`|positive integer|Maximum cache entry size in bytes.|1_000_000| See [cache configuration](#cache-configuration) for how to configure cache settings. @@ -1650,7 +1650,7 @@ You can optionally only configure caching to be enabled on the Broker by setting |`druid.broker.cache.resultLevelCacheLimit`|positive integer|Maximum size of query response that can be cached.|`Integer.MAX_VALUE`| |`druid.broker.cache.unCacheable`|All druid query types|All query types to not cache.|`[]`| |`druid.broker.cache.cacheBulkMergeLimit`|positive integer or 0|Queries with more segments than this number will not attempt to fetch from cache at the broker level, leaving potential caching fetches (and cache result merging) to the Historicals|`Integer.MAX_VALUE`| -|`druid.broker.cache.maxEntrySize`|Maximum cache entry size in bytes.|1_000_000| +|`druid.broker.cache.maxEntrySize`|positive integer|Maximum cache entry size in bytes.|1_000_000| See [cache configuration](#cache-configuration) for how to configure cache settings. From cb0d866e74dd6de4398fc8c7d88157784a0c6242 Mon Sep 17 00:00:00 2001 From: Atul Mohan Date: Tue, 9 Jun 2020 14:55:20 -0500 Subject: [PATCH 067/107] Add Sql InputSource (#9449) * Add Sql InputSource * Add spelling * Use separate DruidModule * Change module name * Fix docs * Use sqltestutils for tests * Add additional tests * Fix inspection * Add module test * Fix md in docs * Remove annotation Co-authored-by: Atul Mohan --- .../input/impl/prefetch/JsonIterator.java | 4 +- .../common/parsers/CloseableIterator.java | 6 +- docs/ingestion/native-batch.md | 57 +++- .../SQLMetadataStorageActionHandler.java | 4 +- .../metadata/input/InputSourceModule.java | 51 ++++ .../druid/metadata/input/SqlEntity.java | 211 ++++++++++++++ .../druid/metadata/input/SqlInputFormat.java | 54 ++++ .../druid/metadata/input/SqlInputSource.java | 151 ++++++++++ .../druid/metadata/input/SqlReader.java | 95 +++++++ .../realtime/firehose/SqlFirehoseFactory.java | 89 +----- .../metadata/input/InputSourceModuleTest.java | 62 +++++ .../druid/metadata/input/SqlEntityTest.java | 134 +++++++++ .../metadata/input/SqlInputSourceTest.java | 262 ++++++++++++++++++ .../druid/metadata/input/SqlTestUtils.java | 118 ++++++++ .../firehose/SqlFirehoseFactoryTest.java | 78 ++---- .../java/org/apache/druid/cli/CliIndexer.java | 2 + .../apache/druid/cli/CliMiddleManager.java | 2 + .../org/apache/druid/cli/CliOverlord.java | 2 + .../java/org/apache/druid/cli/CliPeon.java | 2 + website/.spelling | 4 +- 20 files changed, 1235 insertions(+), 153 deletions(-) create mode 100644 server/src/main/java/org/apache/druid/metadata/input/InputSourceModule.java create mode 100644 server/src/main/java/org/apache/druid/metadata/input/SqlEntity.java create mode 100644 server/src/main/java/org/apache/druid/metadata/input/SqlInputFormat.java create mode 100644 server/src/main/java/org/apache/druid/metadata/input/SqlInputSource.java create mode 100644 server/src/main/java/org/apache/druid/metadata/input/SqlReader.java create mode 100644 server/src/test/java/org/apache/druid/metadata/input/InputSourceModuleTest.java create mode 100644 server/src/test/java/org/apache/druid/metadata/input/SqlEntityTest.java create mode 100644 server/src/test/java/org/apache/druid/metadata/input/SqlInputSourceTest.java create mode 100644 server/src/test/java/org/apache/druid/metadata/input/SqlTestUtils.java diff --git a/core/src/main/java/org/apache/druid/data/input/impl/prefetch/JsonIterator.java b/core/src/main/java/org/apache/druid/data/input/impl/prefetch/JsonIterator.java index 2a241f66de7c..c03e5f6d8e84 100644 --- a/core/src/main/java/org/apache/druid/data/input/impl/prefetch/JsonIterator.java +++ b/core/src/main/java/org/apache/druid/data/input/impl/prefetch/JsonIterator.java @@ -27,11 +27,11 @@ import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.guava.CloseQuietly; import org.apache.druid.java.util.common.io.Closer; +import org.apache.druid.java.util.common.parsers.CloseableIterator; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; -import java.util.Iterator; import java.util.NoSuchElementException; /** @@ -39,7 +39,7 @@ * * @param the type of object returned by this iterator */ -public class JsonIterator implements Iterator, Closeable +public class JsonIterator implements CloseableIterator { private JsonParser jp; private ObjectCodec objectCodec; diff --git a/core/src/main/java/org/apache/druid/java/util/common/parsers/CloseableIterator.java b/core/src/main/java/org/apache/druid/java/util/common/parsers/CloseableIterator.java index d5915369eada..af1baafe4198 100644 --- a/core/src/main/java/org/apache/druid/java/util/common/parsers/CloseableIterator.java +++ b/core/src/main/java/org/apache/druid/java/util/common/parsers/CloseableIterator.java @@ -66,10 +66,10 @@ default CloseableIterator flatMap(Function> funct return new CloseableIterator() { - CloseableIterator iterator = findNextIeteratorIfNecessary(); + CloseableIterator iterator = findNextIteratorIfNecessary(); @Nullable - private CloseableIterator findNextIeteratorIfNecessary() + private CloseableIterator findNextIteratorIfNecessary() { while ((iterator == null || !iterator.hasNext()) && delegate.hasNext()) { if (iterator != null) { @@ -105,7 +105,7 @@ public R next() return iterator.next(); } finally { - findNextIeteratorIfNecessary(); + findNextIteratorIfNecessary(); } } diff --git a/docs/ingestion/native-batch.md b/docs/ingestion/native-batch.md index 02c8a321f984..981f9f700938 100644 --- a/docs/ingestion/native-batch.md +++ b/docs/ingestion/native-batch.md @@ -53,7 +53,7 @@ The detailed behavior of the Parallel task is different depending on the [`parti See each `partitionsSpec` for more details. To use this task, the [`inputSource`](#input-sources) in the `ioConfig` should be _splittable_ and `maxNumConcurrentSubTasks` should be set to larger than 1 in the `tuningConfig`. -Otherwise, this task runs sequentially; the `index_paralllel` task reads each input file one by one and creates segments by itself. +Otherwise, this task runs sequentially; the `index_parallel` task reads each input file one by one and creates segments by itself. The supported splittable input formats for now are: - [`s3`](#s3-input-source) reads data from AWS S3 storage. @@ -63,6 +63,7 @@ The supported splittable input formats for now are: - [`http`](#http-input-source) reads data from HTTP servers. - [`local`](#local-input-source) reads data from local storage. - [`druid`](#druid-input-source) reads data from a Druid datasource. +- [`sql`](#sql-input-source) reads data from a RDBMS source. Some other cloud storage types are supported with the legacy [`firehose`](#firehoses-deprecated). The below `firehose` types are also splittable. Note that only text formats are supported @@ -1310,6 +1311,59 @@ A spec that applies a filter and reads a subset of the original datasource's col This spec above will only return the `page`, `user` dimensions and `added` metric. Only rows where `page` = `Druid` will be returned. +### SQL Input Source + +The SQL input source is used to read data directly from RDBMS. +The SQL input source is _splittable_ and can be used by the [Parallel task](#parallel-task), where each worker task will read from one SQL query from the list of queries. +Since this input source has a fixed input format for reading events, no `inputFormat` field needs to be specified in the ingestion spec when using this input source. +Please refer to the Recommended practices section below before using this input source. + +|property|description|required?| +|--------|-----------|---------| +|type|This should be "sql".|Yes| +|database|Specifies the database connection details. The database type corresponds to the extension that supplies the `connectorConfig` support and this extension must be loaded into Druid. For database types `mysql` and `postgresql`, the `connectorConfig` support is provided by [mysql-metadata-storage](../development/extensions-core/mysql.md) and [postgresql-metadata-storage](../development/extensions-core/postgresql.md) extensions respectively.|Yes| +|foldCase|Toggle case folding of database column names. This may be enabled in cases where the database returns case insensitive column names in query results.|No| +|sqls|List of SQL queries where each SQL query would retrieve the data to be indexed.|Yes| + +An example SqlInputSource spec is shown below: + +```json +... + "ioConfig": { + "type": "index_parallel", + "inputSource": { + "type": "sql", + "database": { + "type": "mysql", + "connectorConfig": { + "connectURI": "jdbc:mysql://host:port/schema", + "user": "user", + "password": "password" + } + }, + "sqls": ["SELECT * FROM table1 WHERE timestamp BETWEEN '2013-01-01 00:00:00' AND '2013-01-01 11:59:59'", "SELECT * FROM table2 WHERE timestamp BETWEEN '2013-01-01 00:00:00' AND '2013-01-01 11:59:59'"] + }, +... +``` + +The spec above will read all events from two separate SQLs for the interval `2013-01-01/2013-01-02`. +Each of the SQL queries will be run in its own sub-task and thus for the above example, there would be two sub-tasks. + +**Recommended practices** + +Compared to the other native batch InputSources, SQL InputSource behaves differently in terms of reading the input data and so it would be helpful to consider the following points before using this InputSource in a production environment: + +* During indexing, each sub-task would execute one of the SQL queries and the results are stored locally on disk. The sub-tasks then proceed to read the data from these local input files and generate segments. Presently, there isn’t any restriction on the size of the generated files and this would require the MiddleManagers or Indexers to have sufficient disk capacity based on the volume of data being indexed. + +* Filtering the SQL queries based on the intervals specified in the `granularitySpec` can avoid unwanted data being retrieved and stored locally by the indexing sub-tasks. For example, if the `intervals` specified in the `granularitySpec` is `["2013-01-01/2013-01-02"]` and the SQL query is `SELECT * FROM table1`, `SqlInputSource` will read all the data for `table1` based on the query, even though only data between the intervals specified will be indexed into Druid. + +* Pagination may be used on the SQL queries to ensure that each query pulls a similar amount of data, thereby improving the efficiency of the sub-tasks. + +* Similar to file-based input formats, any updates to existing data will replace the data in segments specific to the intervals specified in the `granularitySpec`. + + +### + ## Firehoses (Deprecated) Firehoses are deprecated in 0.17.0. It's highly recommended to use the [Input source](#input-sources) instead. @@ -1544,6 +1598,7 @@ This firehose will accept any type of parser, but will only utilize the list of This Firehose can be used to ingest events residing in an RDBMS. The database connection information is provided as part of the ingestion spec. For each query, the results are fetched locally and indexed. If there are multiple queries from which data needs to be indexed, queries are prefetched in the background, up to `maxFetchCapacityBytes` bytes. +This Firehose is _splittable_ and can be used by [native parallel index tasks](native-batch.md#parallel-task). This firehose will accept any type of parser, but will only utilize the list of dimensions and the timestamp specification. See the extension documentation for more detailed ingestion examples. Requires one of the following extensions: diff --git a/server/src/main/java/org/apache/druid/metadata/SQLMetadataStorageActionHandler.java b/server/src/main/java/org/apache/druid/metadata/SQLMetadataStorageActionHandler.java index eb9e9916cac8..e6fdcfb45f1a 100644 --- a/server/src/main/java/org/apache/druid/metadata/SQLMetadataStorageActionHandler.java +++ b/server/src/main/java/org/apache/druid/metadata/SQLMetadataStorageActionHandler.java @@ -22,7 +22,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.base.Throwables; import com.google.common.collect.Maps; @@ -173,8 +172,7 @@ public void insert( } } - @VisibleForTesting - protected static boolean isStatementException(Throwable e) + public static boolean isStatementException(Throwable e) { return e instanceof StatementException || (e instanceof CallbackFailedException && e.getCause() instanceof StatementException); diff --git a/server/src/main/java/org/apache/druid/metadata/input/InputSourceModule.java b/server/src/main/java/org/apache/druid/metadata/input/InputSourceModule.java new file mode 100644 index 000000000000..0423af4f9a03 --- /dev/null +++ b/server/src/main/java/org/apache/druid/metadata/input/InputSourceModule.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.metadata.input; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.google.common.collect.ImmutableList; +import com.google.inject.Binder; +import org.apache.druid.initialization.DruidModule; + +import java.util.List; + +/** + * Module that installs {@link org.apache.druid.data.input.InputSource} implementations + */ +public class InputSourceModule implements DruidModule +{ + @Override + public List getJacksonModules() + { + return ImmutableList.of( + new SimpleModule("InputSourceModule") + .registerSubtypes( + new NamedType(SqlInputSource.class, "sql") + ) + ); + } + + @Override + public void configure(Binder binder) + { + } +} diff --git a/server/src/main/java/org/apache/druid/metadata/input/SqlEntity.java b/server/src/main/java/org/apache/druid/metadata/input/SqlEntity.java new file mode 100644 index 000000000000..724077a4c0d2 --- /dev/null +++ b/server/src/main/java/org/apache/druid/metadata/input/SqlEntity.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.metadata.input; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Preconditions; +import org.apache.druid.data.input.InputEntity; +import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.java.util.common.logger.Logger; +import org.apache.druid.metadata.SQLFirehoseDatabaseConnector; +import org.apache.druid.metadata.SQLMetadataStorageActionHandler; +import org.skife.jdbi.v2.ResultIterator; +import org.skife.jdbi.v2.exceptions.ResultSetException; + +import javax.annotation.Nullable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +/** + * Represents a rdbms based input resource and knows how to read query results from the resource using SQL queries. + */ +public class SqlEntity implements InputEntity +{ + private static final Logger LOG = new Logger(SqlEntity.class); + + private final String sql; + private final ObjectMapper objectMapper; + private final SQLFirehoseDatabaseConnector sqlFirehoseDatabaseConnector; + private final boolean foldCase; + + public SqlEntity( + String sql, + SQLFirehoseDatabaseConnector sqlFirehoseDatabaseConnector, + boolean foldCase, + ObjectMapper objectMapper + ) + { + this.sql = sql; + this.sqlFirehoseDatabaseConnector = Preconditions.checkNotNull( + sqlFirehoseDatabaseConnector, + "SQL Metadata Connector not configured!" + ); + this.foldCase = foldCase; + this.objectMapper = objectMapper; + } + + public String getSql() + { + return sql; + } + + @Nullable + @Override + public URI getUri() + { + return null; + } + + @Override + public InputStream open() + { + throw new UnsupportedOperationException("Please use fetch() instead"); + } + + @Override + public CleanableFile fetch(File temporaryDirectory, byte[] fetchBuffer) throws IOException + { + final File tempFile = File.createTempFile("druid-sql-entity", ".tmp", temporaryDirectory); + return openCleanableFile(sql, sqlFirehoseDatabaseConnector, objectMapper, foldCase, tempFile); + + } + + /** + * Executes a SQL query on the specified database and fetches the result into the given file. + * The result file is deleted if the query execution or the file write fails. + * + * @param sql The SQL query to be executed + * @param sqlFirehoseDatabaseConnector The database connector + * @param objectMapper An object mapper, used for deserialization + * @param foldCase A boolean flag used to enable or disabling case sensitivity while handling database column names + * + * @return A {@link InputEntity.CleanableFile} object that wraps the file containing the SQL results + */ + + public static CleanableFile openCleanableFile( + String sql, + SQLFirehoseDatabaseConnector sqlFirehoseDatabaseConnector, + ObjectMapper objectMapper, + boolean foldCase, + File tempFile + ) + throws IOException + { + try (FileOutputStream fos = new FileOutputStream(tempFile); + final JsonGenerator jg = objectMapper.getFactory().createGenerator(fos);) { + + // Execute the sql query and lazily retrieve the results into the file in json format. + // foldCase is useful to handle differences in case sensitivity behavior across databases. + sqlFirehoseDatabaseConnector.retryWithHandle( + (handle) -> { + ResultIterator> resultIterator = handle.createQuery( + sql + ).map( + (index, r, ctx) -> { + Map resultRow = foldCase ? new CaseFoldedMap() : new HashMap<>(); + ResultSetMetaData resultMetadata; + try { + resultMetadata = r.getMetaData(); + } + catch (SQLException e) { + throw new ResultSetException("Unable to obtain metadata from result set", e, ctx); + } + try { + for (int i = 1; i <= resultMetadata.getColumnCount(); i++) { + String key = resultMetadata.getColumnName(i); + String alias = resultMetadata.getColumnLabel(i); + Object value = r.getObject(i); + resultRow.put(alias != null ? alias : key, value); + } + } + catch (SQLException e) { + throw new ResultSetException("Unable to access specific metadata from " + + "result set metadata", e, ctx); + } + return resultRow; + } + ).iterator(); + jg.writeStartArray(); + while (resultIterator.hasNext()) { + jg.writeObject(resultIterator.next()); + } + jg.writeEndArray(); + jg.close(); + return null; + }, + (exception) -> sqlFirehoseDatabaseConnector.isTransientException(exception) + && !(SQLMetadataStorageActionHandler.isStatementException(exception)) + ); + return new CleanableFile() + { + @Override + public File file() + { + return tempFile; + } + + @Override + public void close() + { + if (!tempFile.delete()) { + LOG.warn("Failed to remove file[%s]", tempFile.getAbsolutePath()); + } + } + }; + } + catch (Exception e) { + if (!tempFile.delete()) { + LOG.warn("Failed to remove file[%s]", tempFile.getAbsolutePath()); + } + throw new IOException(e); + } + } + + private static class CaseFoldedMap extends HashMap + { + public static final long serialVersionUID = 1L; + + @Override + public Object get(Object obj) + { + return super.get(obj == null ? null : StringUtils.toLowerCase((String) obj)); + } + + @Override + public Object put(String key, Object value) + { + return super.put(key == null ? null : StringUtils.toLowerCase(key), value); + } + + @Override + public boolean containsKey(Object obj) + { + return super.containsKey(obj == null ? null : StringUtils.toLowerCase((String) obj)); + } + } +} diff --git a/server/src/main/java/org/apache/druid/metadata/input/SqlInputFormat.java b/server/src/main/java/org/apache/druid/metadata/input/SqlInputFormat.java new file mode 100644 index 000000000000..6d0aa59a20d2 --- /dev/null +++ b/server/src/main/java/org/apache/druid/metadata/input/SqlInputFormat.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.metadata.input; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.druid.data.input.InputEntity; +import org.apache.druid.data.input.InputEntityReader; +import org.apache.druid.data.input.InputFormat; +import org.apache.druid.data.input.InputRowSchema; + +import java.io.File; + +public class SqlInputFormat implements InputFormat +{ + private final ObjectMapper objectMapper; + + public SqlInputFormat(ObjectMapper objectMapper) + { + this.objectMapper = objectMapper; + } + + @Override + public boolean isSplittable() + { + return true; + } + + @Override + public InputEntityReader createReader( + InputRowSchema inputRowSchema, + InputEntity source, + File temporaryDirectory + ) + { + return new SqlReader(inputRowSchema, source, temporaryDirectory, objectMapper); + } +} diff --git a/server/src/main/java/org/apache/druid/metadata/input/SqlInputSource.java b/server/src/main/java/org/apache/druid/metadata/input/SqlInputSource.java new file mode 100644 index 000000000000..c7dfbb7fa365 --- /dev/null +++ b/server/src/main/java/org/apache/druid/metadata/input/SqlInputSource.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.metadata.input; + +import com.fasterxml.jackson.annotation.JacksonInject; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Preconditions; +import org.apache.druid.data.input.AbstractInputSource; +import org.apache.druid.data.input.InputFormat; +import org.apache.druid.data.input.InputRowSchema; +import org.apache.druid.data.input.InputSourceReader; +import org.apache.druid.data.input.InputSplit; +import org.apache.druid.data.input.SplitHintSpec; +import org.apache.druid.data.input.impl.InputEntityIteratingReader; +import org.apache.druid.data.input.impl.SplittableInputSource; +import org.apache.druid.guice.annotations.Smile; +import org.apache.druid.metadata.SQLFirehoseDatabaseConnector; + +import javax.annotation.Nullable; +import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +public class SqlInputSource extends AbstractInputSource implements SplittableInputSource +{ + private final List sqls; + private final SQLFirehoseDatabaseConnector sqlFirehoseDatabaseConnector; + private final ObjectMapper objectMapper; + private final boolean foldCase; + + @JsonCreator + public SqlInputSource( + @JsonProperty("sqls") List sqls, + @JsonProperty("foldCase") boolean foldCase, + @JsonProperty("database") SQLFirehoseDatabaseConnector sqlFirehoseDatabaseConnector, + @JacksonInject @Smile ObjectMapper objectMapper + ) + { + Preconditions.checkArgument(sqls.size() > 0, "No SQL queries provided"); + + this.sqls = sqls; + this.foldCase = foldCase; + this.sqlFirehoseDatabaseConnector = Preconditions.checkNotNull( + sqlFirehoseDatabaseConnector, + "SQL Metadata Connector not configured!" + ); + this.objectMapper = objectMapper; + } + + @JsonProperty + public List getSqls() + { + return sqls; + } + + @JsonProperty + public boolean isFoldCase() + { + return foldCase; + } + + @JsonProperty("database") + public SQLFirehoseDatabaseConnector getSQLFirehoseDatabaseConnector() + { + return sqlFirehoseDatabaseConnector; + } + + @Override + public Stream> createSplits(InputFormat inputFormat, @Nullable SplitHintSpec splitHintSpec) + { + return sqls.stream().map(InputSplit::new); + } + + @Override + public int estimateNumSplits(InputFormat inputFormat, @Nullable SplitHintSpec splitHintSpec) + { + return sqls.size(); + } + + @Override + public SplittableInputSource withSplit(InputSplit split) + { + return new SqlInputSource( + Collections.singletonList(split.get()), + foldCase, + sqlFirehoseDatabaseConnector, + objectMapper + ); + } + + @Override + protected InputSourceReader fixedFormatReader(InputRowSchema inputRowSchema, @Nullable File temporaryDirectory) + { + final SqlInputFormat inputFormat = new SqlInputFormat(objectMapper); + return new InputEntityIteratingReader( + inputRowSchema, + inputFormat, + createSplits(inputFormat, null) + .map(split -> new SqlEntity(split.get(), sqlFirehoseDatabaseConnector, foldCase, objectMapper)).iterator(), + temporaryDirectory + ); + } + + @Override + public boolean needsFormat() + { + return false; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SqlInputSource that = (SqlInputSource) o; + return foldCase == that.foldCase && + sqls.equals(that.sqls) && + sqlFirehoseDatabaseConnector.equals(that.sqlFirehoseDatabaseConnector); + } + + @Override + public int hashCode() + { + return Objects.hash(sqls, sqlFirehoseDatabaseConnector, foldCase); + } +} diff --git a/server/src/main/java/org/apache/druid/metadata/input/SqlReader.java b/server/src/main/java/org/apache/druid/metadata/input/SqlReader.java new file mode 100644 index 000000000000..4657158c0463 --- /dev/null +++ b/server/src/main/java/org/apache/druid/metadata/input/SqlReader.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.metadata.input; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.druid.data.input.InputEntity; +import org.apache.druid.data.input.InputRow; +import org.apache.druid.data.input.InputRowSchema; +import org.apache.druid.data.input.IntermediateRowParsingReader; +import org.apache.druid.data.input.impl.MapInputRowParser; +import org.apache.druid.data.input.impl.prefetch.JsonIterator; +import org.apache.druid.java.util.common.io.Closer; +import org.apache.druid.java.util.common.parsers.CloseableIterator; +import org.apache.druid.java.util.common.parsers.ParseException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Reader exclusively for {@link SqlEntity} + */ +public class SqlReader extends IntermediateRowParsingReader> +{ + private final InputRowSchema inputRowSchema; + private final SqlEntity source; + private final File temporaryDirectory; + private final ObjectMapper objectMapper; + + + SqlReader( + InputRowSchema inputRowSchema, + InputEntity source, + File temporaryDirectory, + ObjectMapper objectMapper + ) + { + this.inputRowSchema = inputRowSchema; + this.source = (SqlEntity) source; + this.temporaryDirectory = temporaryDirectory; + this.objectMapper = objectMapper; + } + + @Override + protected CloseableIterator> intermediateRowIterator() throws IOException + { + final Closer closer = Closer.create(); + //The results are fetched into local storage as this avoids having to keep a persistent database connection for a long time + final InputEntity.CleanableFile resultFile = closer.register(source.fetch(temporaryDirectory, null)); + FileInputStream inputStream = new FileInputStream(resultFile.file()); + JsonIterator> jsonIterator = new JsonIterator<>(new TypeReference>() + { + }, inputStream, closer, objectMapper); + return jsonIterator; + } + + @Override + protected List parseInputRows(Map intermediateRow) throws ParseException + { + return Collections.singletonList( + MapInputRowParser.parse( + inputRowSchema.getTimestampSpec(), + inputRowSchema.getDimensionsSpec(), + intermediateRow + ) + ); + } + + @Override + protected Map toMap(Map intermediateRow) + { + return intermediateRow; + } +} diff --git a/server/src/main/java/org/apache/druid/segment/realtime/firehose/SqlFirehoseFactory.java b/server/src/main/java/org/apache/druid/segment/realtime/firehose/SqlFirehoseFactory.java index 9daa231f9b7e..0b5863d671d6 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/firehose/SqlFirehoseFactory.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/firehose/SqlFirehoseFactory.java @@ -22,32 +22,23 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Preconditions; import org.apache.druid.data.input.FiniteFirehoseFactory; import org.apache.druid.data.input.InputSplit; import org.apache.druid.data.input.impl.InputRowParser; import org.apache.druid.guice.annotations.Smile; -import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.metadata.MetadataStorageConnectorConfig; import org.apache.druid.metadata.SQLFirehoseDatabaseConnector; -import org.skife.jdbi.v2.ResultIterator; -import org.skife.jdbi.v2.exceptions.CallbackFailedException; -import org.skife.jdbi.v2.exceptions.ResultSetException; -import org.skife.jdbi.v2.exceptions.StatementException; +import org.apache.druid.metadata.input.SqlEntity; import javax.annotation.Nullable; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -86,7 +77,10 @@ public SqlFirehoseFactory( this.sqls = sqls; this.objectMapper = objectMapper; - this.sqlFirehoseDatabaseConnector = sqlFirehoseDatabaseConnector; + this.sqlFirehoseDatabaseConnector = Preconditions.checkNotNull( + sqlFirehoseDatabaseConnector, + "SQL Metadata Connector not configured!" + ); this.foldCase = foldCase; this.connectorConfig = null; } @@ -94,79 +88,8 @@ public SqlFirehoseFactory( @Override protected InputStream openObjectStream(String sql, File fileName) throws IOException { - Preconditions.checkNotNull(sqlFirehoseDatabaseConnector, "SQL Metadata Connector not configured!"); - try (FileOutputStream fos = new FileOutputStream(fileName)) { - final JsonGenerator jg = objectMapper.getFactory().createGenerator(fos); - sqlFirehoseDatabaseConnector.retryWithHandle( - (handle) -> { - ResultIterator> resultIterator = handle.createQuery( - sql - ).map( - (index, r, ctx) -> { - Map resultRow = foldCase ? new CaseFoldedMap() : new HashMap<>(); - ResultSetMetaData resultMetadata; - try { - resultMetadata = r.getMetaData(); - } - catch (SQLException e) { - throw new ResultSetException("Unable to obtain metadata from result set", e, ctx); - } - try { - for (int i = 1; i <= resultMetadata.getColumnCount(); i++) { - String key = resultMetadata.getColumnName(i); - String alias = resultMetadata.getColumnLabel(i); - Object value = r.getObject(i); - resultRow.put(alias != null ? alias : key, value); - } - } - catch (SQLException e) { - throw new ResultSetException("Unable to access specific metadata from " + - "result set metadata", e, ctx); - } - return resultRow; - } - ).iterator(); - jg.writeStartArray(); - while (resultIterator.hasNext()) { - jg.writeObject(resultIterator.next()); - } - jg.writeEndArray(); - jg.close(); - return null; - }, - (exception) -> { - final boolean isStatementException = exception instanceof StatementException || - (exception instanceof CallbackFailedException - && exception.getCause() instanceof StatementException); - return sqlFirehoseDatabaseConnector.isTransientException(exception) && !(isStatementException); - } - ); - } + SqlEntity.openCleanableFile(sql, sqlFirehoseDatabaseConnector, objectMapper, foldCase, fileName); return new FileInputStream(fileName); - - } - - private static class CaseFoldedMap extends HashMap - { - public static final long serialVersionUID = 1L; - - @Override - public Object get(Object obj) - { - return super.get(StringUtils.toLowerCase((String) obj)); - } - - @Override - public Object put(String key, Object value) - { - return super.put(StringUtils.toLowerCase(key), value); - } - - @Override - public boolean containsKey(Object obj) - { - return super.containsKey(StringUtils.toLowerCase((String) obj)); - } } @Override diff --git a/server/src/test/java/org/apache/druid/metadata/input/InputSourceModuleTest.java b/server/src/test/java/org/apache/druid/metadata/input/InputSourceModuleTest.java new file mode 100644 index 000000000000..67126b0c7b2f --- /dev/null +++ b/server/src/test/java/org/apache/druid/metadata/input/InputSourceModuleTest.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.metadata.input; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.AnnotatedClass; +import com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.google.common.collect.Iterables; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; +import java.util.stream.Collectors; + +public class InputSourceModuleTest +{ + private final ObjectMapper mapper = new ObjectMapper(); + private final String SQL_NAMED_TYPE = "sql"; + + @Before + public void setUp() + { + for (Module jacksonModule : new InputSourceModule().getJacksonModules()) { + mapper.registerModule(jacksonModule); + } + } + + @Test + public void testSubTypeRegistration() + { + MapperConfig config = mapper.getDeserializationConfig(); + AnnotatedClass annotatedClass = AnnotatedClassResolver.resolveWithoutSuperTypes(config, SqlInputSource.class); + List subtypes = mapper.getSubtypeResolver() + .collectAndResolveSubtypesByClass(config, annotatedClass) + .stream() + .map(NamedType::getName) + .collect(Collectors.toList()); + Assert.assertNotNull(subtypes); + Assert.assertEquals(SQL_NAMED_TYPE, Iterables.getOnlyElement(subtypes)); + } +} diff --git a/server/src/test/java/org/apache/druid/metadata/input/SqlEntityTest.java b/server/src/test/java/org/apache/druid/metadata/input/SqlEntityTest.java new file mode 100644 index 000000000000..46a171b50c92 --- /dev/null +++ b/server/src/test/java/org/apache/druid/metadata/input/SqlEntityTest.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.metadata.input; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.io.IOUtils; +import org.apache.druid.data.input.InputEntity; +import org.apache.druid.metadata.TestDerbyConnector; +import org.apache.druid.segment.TestHelper; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public class SqlEntityTest +{ + @Rule + public final TestDerbyConnector.DerbyConnectorRule derbyConnectorRule = new TestDerbyConnector.DerbyConnectorRule(); + + private final ObjectMapper mapper = TestHelper.makeSmileMapper(); + private TestDerbyConnector derbyConnector; + String TABLE_NAME_1 = "FOOS_TABLE"; + + String VALID_SQL = "SELECT timestamp,a,b FROM FOOS_TABLE"; + String INVALID_SQL = "DONT SELECT timestamp,a,b FROM FOOS_TABLE"; + String resultJson = "[{\"a\":\"0\"," + + "\"b\":\"0\"," + + "\"timestamp\":\"2011-01-12T00:00:00.000Z\"" + + "}]"; + + @Before + public void setUp() + { + for (Module jacksonModule : new InputSourceModule().getJacksonModules()) { + mapper.registerModule(jacksonModule); + } + } + + @Test + public void testExecuteQuery() throws IOException + { + derbyConnector = derbyConnectorRule.getConnector(); + SqlTestUtils testUtils = new SqlTestUtils(derbyConnector); + testUtils.createAndUpdateTable(TABLE_NAME_1, 1); + File tmpFile = File.createTempFile( + "testQueryResults", + "" + ); + InputEntity.CleanableFile queryResult = SqlEntity.openCleanableFile( + VALID_SQL, + testUtils.getDerbyFirehoseConnector(), + mapper, + true, + tmpFile + ); + InputStream queryInputStream = new FileInputStream(queryResult.file()); + String actualJson = IOUtils.toString(queryInputStream, StandardCharsets.UTF_8); + + Assert.assertEquals(actualJson, resultJson); + testUtils.dropTable(TABLE_NAME_1); + } + + @Test(expected = IOException.class) + public void testFailOnInvalidQuery() throws IOException + { + derbyConnector = derbyConnectorRule.getConnector(); + SqlTestUtils testUtils = new SqlTestUtils(derbyConnector); + testUtils.createAndUpdateTable(TABLE_NAME_1, 1); + File tmpFile = File.createTempFile( + "testQueryResults", + "" + ); + InputEntity.CleanableFile queryResult = SqlEntity.openCleanableFile( + INVALID_SQL, + testUtils.getDerbyFirehoseConnector(), + mapper, + true, + tmpFile + ); + + Assert.assertTrue(tmpFile.exists()); + } + + @Test + public void testFileDeleteOnInvalidQuery() throws IOException + { + //The test parameters here are same as those used for testFailOnInvalidQuery(). + //The only difference is that this test checks if the temporary file is deleted upon failure. + derbyConnector = derbyConnectorRule.getConnector(); + SqlTestUtils testUtils = new SqlTestUtils(derbyConnector); + testUtils.createAndUpdateTable(TABLE_NAME_1, 1); + File tmpFile = File.createTempFile( + "testQueryResults", + "" + ); + try { + SqlEntity.openCleanableFile( + INVALID_SQL, + testUtils.getDerbyFirehoseConnector(), + mapper, + true, + tmpFile + ); + } + // Lets catch the exception so as to test temporary file deletion. + catch (IOException e) { + Assert.assertFalse(tmpFile.exists()); + } + } +} diff --git a/server/src/test/java/org/apache/druid/metadata/input/SqlInputSourceTest.java b/server/src/test/java/org/apache/druid/metadata/input/SqlInputSourceTest.java new file mode 100644 index 000000000000..7afa88894f26 --- /dev/null +++ b/server/src/test/java/org/apache/druid/metadata/input/SqlInputSourceTest.java @@ -0,0 +1,262 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.metadata.input; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.apache.commons.dbcp2.BasicDataSource; +import org.apache.commons.io.FileUtils; +import org.apache.druid.data.input.InputFormat; +import org.apache.druid.data.input.InputRow; +import org.apache.druid.data.input.InputRowSchema; +import org.apache.druid.data.input.InputSourceReader; +import org.apache.druid.data.input.InputSplit; +import org.apache.druid.data.input.Row; +import org.apache.druid.data.input.impl.DimensionsSpec; +import org.apache.druid.data.input.impl.TimestampSpec; +import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.java.util.common.parsers.CloseableIterator; +import org.apache.druid.metadata.MetadataStorageConnectorConfig; +import org.apache.druid.metadata.SQLFirehoseDatabaseConnector; +import org.apache.druid.metadata.TestDerbyConnector; +import org.apache.druid.segment.TestHelper; +import org.easymock.EasyMock; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.skife.jdbi.v2.DBI; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class SqlInputSourceTest +{ + private static final List FIREHOSE_TMP_DIRS = new ArrayList<>(); + private final String TABLE_NAME_1 = "FOOS_TABLE_1"; + private final String TABLE_NAME_2 = "FOOS_TABLE_2"; + + private final List SQLLIST1 = ImmutableList.of("SELECT timestamp,a,b FROM FOOS_TABLE_1"); + private final List SQLLIST2 = ImmutableList.of( + "SELECT timestamp,a,b FROM FOOS_TABLE_1", + "SELECT timestamp,a,b FROM FOOS_TABLE_2" + ); + + private static final InputRowSchema INPUT_ROW_SCHEMA = new InputRowSchema( + new TimestampSpec("timestamp", "auto", null), + new DimensionsSpec( + DimensionsSpec.getDefaultSchemas(Arrays.asList("timestamp", "a", "b")), + new ArrayList<>(), + new ArrayList<>() + ), + Collections.emptyList() + ); + @Rule + public final TestDerbyConnector.DerbyConnectorRule derbyConnectorRule = new TestDerbyConnector.DerbyConnectorRule(); + private final ObjectMapper mapper = TestHelper.makeSmileMapper(); + private TestDerbyConnector derbyConnector; + + @Before + public void setUp() + { + for (Module jacksonModule : new InputSourceModule().getJacksonModules()) { + mapper.registerModule(jacksonModule); + } + } + + @AfterClass + public static void teardown() throws IOException + { + for (File dir : FIREHOSE_TMP_DIRS) { + FileUtils.forceDelete(dir); + } + } + + private void assertResult(List rows, List sqls) + { + Assert.assertEquals(10 * sqls.size(), rows.size()); + rows.sort(Comparator.comparing(Row::getTimestamp) + .thenComparingInt(r -> Integer.valueOf(r.getDimension("a").get(0))) + .thenComparingInt(r -> Integer.valueOf(r.getDimension("b").get(0)))); + int rowCount = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < sqls.size(); j++) { + final Row row = rows.get(rowCount); + String timestampSt = StringUtils.format("2011-01-12T00:0%s:00.000Z", i); + Assert.assertEquals(timestampSt, row.getTimestamp().toString()); + Assert.assertEquals(i, Integer.valueOf(row.getDimension("a").get(0)).intValue()); + Assert.assertEquals(i, Integer.valueOf(row.getDimension("b").get(0)).intValue()); + rowCount++; + } + } + } + + private File createFirehoseTmpDir(String dirSuffix) throws IOException + { + final File firehoseTempDir = File.createTempFile( + SqlInputSourceTest.class.getSimpleName(), + dirSuffix + ); + FileUtils.forceDelete(firehoseTempDir); + FileUtils.forceMkdir(firehoseTempDir); + FIREHOSE_TMP_DIRS.add(firehoseTempDir); + return firehoseTempDir; + } + + @Test + public void testSerde() throws IOException + { + mapper.registerSubtypes(TestSerdeFirehoseConnector.class); + final SqlInputSourceTest.TestSerdeFirehoseConnector testSerdeFirehoseConnector = new SqlInputSourceTest.TestSerdeFirehoseConnector( + new MetadataStorageConnectorConfig()); + final SqlInputSource sqlInputSource = new SqlInputSource(SQLLIST1, true, testSerdeFirehoseConnector, mapper); + final String valueString = mapper.writeValueAsString(sqlInputSource); + final SqlInputSource inputSourceFromJson = mapper.readValue(valueString, SqlInputSource.class); + Assert.assertEquals(sqlInputSource, inputSourceFromJson); + } + + @Test + public void testSingleSplit() throws Exception + { + derbyConnector = derbyConnectorRule.getConnector(); + SqlTestUtils testUtils = new SqlTestUtils(derbyConnector); + testUtils.createAndUpdateTable(TABLE_NAME_1, 10); + final File tempDir = createFirehoseTmpDir("testSingleSplit"); + SqlInputSource sqlInputSource = new SqlInputSource(SQLLIST1, true, testUtils.getDerbyFirehoseConnector(), mapper); + InputSourceReader sqlReader = sqlInputSource.fixedFormatReader(INPUT_ROW_SCHEMA, tempDir); + CloseableIterator resultIterator = sqlReader.read(); + final List rows = new ArrayList<>(); + while (resultIterator.hasNext()) { + rows.add(resultIterator.next()); + } + assertResult(rows, SQLLIST1); + testUtils.dropTable(TABLE_NAME_1); + } + + + @Test + public void testMultipleSplits() throws Exception + { + derbyConnector = derbyConnectorRule.getConnector(); + SqlTestUtils testUtils = new SqlTestUtils(derbyConnector); + testUtils.createAndUpdateTable(TABLE_NAME_1, 10); + testUtils.createAndUpdateTable(TABLE_NAME_2, 10); + final File tempDir = createFirehoseTmpDir("testMultipleSplit"); + SqlInputSource sqlInputSource = new SqlInputSource(SQLLIST2, true, testUtils.getDerbyFirehoseConnector(), mapper); + InputSourceReader sqlReader = sqlInputSource.fixedFormatReader(INPUT_ROW_SCHEMA, tempDir); + CloseableIterator resultIterator = sqlReader.read(); + final List rows = new ArrayList<>(); + while (resultIterator.hasNext()) { + rows.add(resultIterator.next()); + } + assertResult(rows, SQLLIST2); + testUtils.dropTable(TABLE_NAME_1); + testUtils.dropTable(TABLE_NAME_2); + } + + @Test + public void testNumSplits() + { + derbyConnector = derbyConnectorRule.getConnector(); + SqlTestUtils testUtils = new SqlTestUtils(derbyConnector); + SqlInputSource sqlInputSource = new SqlInputSource(SQLLIST2, true, testUtils.getDerbyFirehoseConnector(), mapper); + InputFormat inputFormat = EasyMock.createMock(InputFormat.class); + Stream> sqlSplits = sqlInputSource.createSplits(inputFormat, null); + Assert.assertEquals(SQLLIST2, sqlSplits.map(InputSplit::get).collect(Collectors.toList())); + Assert.assertEquals(2, sqlInputSource.estimateNumSplits(inputFormat, null)); + } + + @Test + public void testEquals() + { + EqualsVerifier.forClass(SqlInputSource.class) + .withPrefabValues( + ObjectMapper.class, + new ObjectMapper(), + new ObjectMapper() + ) + .withIgnoredFields("objectMapper") + .withNonnullFields("sqls", "sqlFirehoseDatabaseConnector") + .usingGetClass() + .verify(); + } + + @JsonTypeName("test") + private static class TestSerdeFirehoseConnector extends SQLFirehoseDatabaseConnector + { + private final DBI dbi; + private final MetadataStorageConnectorConfig metadataStorageConnectorConfig; + + private TestSerdeFirehoseConnector( + @JsonProperty("connectorConfig") MetadataStorageConnectorConfig metadataStorageConnectorConfig + ) + { + final BasicDataSource datasource = getDatasource(metadataStorageConnectorConfig); + datasource.setDriverClassLoader(getClass().getClassLoader()); + datasource.setDriverClassName("org.apache.derby.jdbc.ClientDriver"); + this.dbi = new DBI(datasource); + this.metadataStorageConnectorConfig = metadataStorageConnectorConfig; + } + + @JsonProperty("connectorConfig") + public MetadataStorageConnectorConfig getConnectorConfig() + { + return metadataStorageConnectorConfig; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TestSerdeFirehoseConnector that = (TestSerdeFirehoseConnector) o; + return metadataStorageConnectorConfig.equals(that.metadataStorageConnectorConfig); + } + + @Override + public int hashCode() + { + return Objects.hash(metadataStorageConnectorConfig); + } + + @Override + public DBI getDBI() + { + return dbi; + } + } +} diff --git a/server/src/test/java/org/apache/druid/metadata/input/SqlTestUtils.java b/server/src/test/java/org/apache/druid/metadata/input/SqlTestUtils.java new file mode 100644 index 000000000000..60e7c73e4397 --- /dev/null +++ b/server/src/test/java/org/apache/druid/metadata/input/SqlTestUtils.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.metadata.input; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; +import org.apache.commons.dbcp2.BasicDataSource; +import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.metadata.MetadataStorageConnectorConfig; +import org.apache.druid.metadata.SQLFirehoseDatabaseConnector; +import org.apache.druid.metadata.TestDerbyConnector; +import org.junit.Rule; +import org.skife.jdbi.v2.Batch; +import org.skife.jdbi.v2.DBI; +import org.skife.jdbi.v2.tweak.HandleCallback; + +public class SqlTestUtils +{ + @Rule + public final TestDerbyConnector.DerbyConnectorRule derbyConnectorRule = new TestDerbyConnector.DerbyConnectorRule(); + private final TestDerbyFirehoseConnector derbyFirehoseConnector; + private final TestDerbyConnector derbyConnector; + + public SqlTestUtils(TestDerbyConnector derbyConnector) + { + this.derbyConnector = derbyConnector; + this.derbyFirehoseConnector = new SqlTestUtils.TestDerbyFirehoseConnector( + new MetadataStorageConnectorConfig(), + derbyConnector.getDBI() + ); + } + + private static class TestDerbyFirehoseConnector extends SQLFirehoseDatabaseConnector + { + private final DBI dbi; + + private TestDerbyFirehoseConnector( + @JsonProperty("connectorConfig") MetadataStorageConnectorConfig metadataStorageConnectorConfig, DBI dbi + ) + { + final BasicDataSource datasource = getDatasource(metadataStorageConnectorConfig); + datasource.setDriverClassLoader(getClass().getClassLoader()); + datasource.setDriverClassName("org.apache.derby.jdbc.ClientDriver"); + this.dbi = dbi; + } + + @Override + public DBI getDBI() + { + return dbi; + } + } + + public void createAndUpdateTable(final String tableName, int numEntries) + { + derbyConnector.createTable( + tableName, + ImmutableList.of( + StringUtils.format( + "CREATE TABLE %1$s (\n" + + " timestamp varchar(255) NOT NULL,\n" + + " a VARCHAR(255) NOT NULL,\n" + + " b VARCHAR(255) NOT NULL\n" + + ")", + tableName + ) + ) + ); + + derbyConnector.getDBI().withHandle( + (handle) -> { + Batch batch = handle.createBatch(); + for (int i = 0; i < numEntries; i++) { + String timestampSt = StringUtils.format("2011-01-12T00:0%s:00.000Z", i); + batch.add(StringUtils.format("INSERT INTO %1$s (timestamp, a, b) VALUES ('%2$s', '%3$s', '%4$s')", + tableName, timestampSt, + i, i + )); + } + batch.execute(); + return null; + } + ); + } + + public void dropTable(final String tableName) + { + derbyConnector.getDBI().withHandle( + (HandleCallback) handle -> { + handle.createStatement(StringUtils.format("DROP TABLE %s", tableName)) + .execute(); + return null; + } + ); + } + + public TestDerbyFirehoseConnector getDerbyFirehoseConnector() + { + return derbyFirehoseConnector; + } +} diff --git a/server/src/test/java/org/apache/druid/segment/realtime/firehose/SqlFirehoseFactoryTest.java b/server/src/test/java/org/apache/druid/segment/realtime/firehose/SqlFirehoseFactoryTest.java index 34fa763613fe..189aa4984aac 100644 --- a/server/src/test/java/org/apache/druid/segment/realtime/firehose/SqlFirehoseFactoryTest.java +++ b/server/src/test/java/org/apache/druid/segment/realtime/firehose/SqlFirehoseFactoryTest.java @@ -34,6 +34,7 @@ import org.apache.druid.metadata.MetadataStorageConnectorConfig; import org.apache.druid.metadata.SQLFirehoseDatabaseConnector; import org.apache.druid.metadata.TestDerbyConnector; +import org.apache.druid.metadata.input.SqlTestUtils; import org.apache.druid.segment.TestHelper; import org.apache.druid.segment.transform.TransformSpec; import org.junit.AfterClass; @@ -41,9 +42,7 @@ import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; -import org.skife.jdbi.v2.Batch; import org.skife.jdbi.v2.DBI; -import org.skife.jdbi.v2.tweak.HandleCallback; import java.io.File; import java.io.IOException; @@ -82,7 +81,6 @@ public class SqlFirehoseFactoryTest ) ); private TestDerbyConnector derbyConnector; - private TestDerbyFirehoseConnector derbyFirehoseConnector; @BeforeClass public static void setup() throws IOException @@ -139,56 +137,12 @@ private File createFirehoseTmpDir(String dirSuffix) throws IOException return firehoseTempDir; } - private void dropTable(final String tableName) - { - derbyConnector.getDBI().withHandle( - (HandleCallback) handle -> { - handle.createStatement(StringUtils.format("DROP TABLE %s", tableName)) - .execute(); - return null; - } - ); - } - - private void createAndUpdateTable(final String tableName) - { - derbyConnector = derbyConnectorRule.getConnector(); - derbyFirehoseConnector = new TestDerbyFirehoseConnector(new MetadataStorageConnectorConfig(), - derbyConnector.getDBI()); - derbyConnector.createTable( - tableName, - ImmutableList.of( - StringUtils.format( - "CREATE TABLE %1$s (\n" - + " timestamp varchar(255) NOT NULL,\n" - + " a VARCHAR(255) NOT NULL,\n" - + " b VARCHAR(255) NOT NULL\n" - + ")", - tableName - ) - ) - ); - - derbyConnector.getDBI().withHandle( - (handle) -> { - Batch batch = handle.createBatch(); - for (int i = 0; i < 10; i++) { - String timestampSt = StringUtils.format("2011-01-12T00:0%s:00.000Z", i); - batch.add(StringUtils.format("INSERT INTO %1$s (timestamp, a, b) VALUES ('%2$s', '%3$s', '%4$s')", - tableName, timestampSt, - i, i - )); - } - batch.execute(); - return null; - } - ); - } - @Test public void testWithoutCacheAndFetch() throws Exception { - createAndUpdateTable(TABLE_NAME_1); + derbyConnector = derbyConnectorRule.getConnector(); + SqlTestUtils testUtils = new SqlTestUtils(derbyConnector); + testUtils.createAndUpdateTable(TABLE_NAME_1, 10); final SqlFirehoseFactory factory = new SqlFirehoseFactory( SQLLIST1, @@ -197,7 +151,7 @@ public void testWithoutCacheAndFetch() throws Exception 0L, 0L, true, - derbyFirehoseConnector, + testUtils.getDerbyFirehoseConnector(), mapper ); @@ -211,14 +165,16 @@ public void testWithoutCacheAndFetch() throws Exception assertResult(rows, SQLLIST1); assertNumRemainingCacheFiles(firehoseTmpDir, 0); - dropTable(TABLE_NAME_1); + testUtils.dropTable(TABLE_NAME_1); } @Test public void testWithoutCache() throws IOException { - createAndUpdateTable(TABLE_NAME_1); + derbyConnector = derbyConnectorRule.getConnector(); + SqlTestUtils testUtils = new SqlTestUtils(derbyConnector); + testUtils.createAndUpdateTable(TABLE_NAME_1, 10); final SqlFirehoseFactory factory = new SqlFirehoseFactory( SQLLIST1, @@ -227,7 +183,7 @@ public void testWithoutCache() throws IOException null, null, true, - derbyFirehoseConnector, + testUtils.getDerbyFirehoseConnector(), mapper ); @@ -242,15 +198,17 @@ public void testWithoutCache() throws IOException assertResult(rows, SQLLIST1); assertNumRemainingCacheFiles(firehoseTmpDir, 0); - dropTable(TABLE_NAME_1); + testUtils.dropTable(TABLE_NAME_1); } @Test public void testWithCacheAndFetch() throws IOException { - createAndUpdateTable(TABLE_NAME_1); - createAndUpdateTable(TABLE_NAME_2); + derbyConnector = derbyConnectorRule.getConnector(); + SqlTestUtils testUtils = new SqlTestUtils(derbyConnector); + testUtils.createAndUpdateTable(TABLE_NAME_1, 10); + testUtils.createAndUpdateTable(TABLE_NAME_2, 10); final SqlFirehoseFactory factory = new SqlFirehoseFactory( @@ -260,7 +218,7 @@ public void testWithCacheAndFetch() throws IOException 0L, null, true, - derbyFirehoseConnector, + testUtils.getDerbyFirehoseConnector(), mapper ); @@ -274,8 +232,8 @@ public void testWithCacheAndFetch() throws IOException assertResult(rows, SQLLIST2); assertNumRemainingCacheFiles(firehoseTmpDir, 2); - dropTable(TABLE_NAME_1); - dropTable(TABLE_NAME_2); + testUtils.dropTable(TABLE_NAME_1); + testUtils.dropTable(TABLE_NAME_2); } private static class TestDerbyFirehoseConnector extends SQLFirehoseDatabaseConnector diff --git a/services/src/main/java/org/apache/druid/cli/CliIndexer.java b/services/src/main/java/org/apache/druid/cli/CliIndexer.java index d4a8df18606b..7029d6f039bc 100644 --- a/services/src/main/java/org/apache/druid/cli/CliIndexer.java +++ b/services/src/main/java/org/apache/druid/cli/CliIndexer.java @@ -59,6 +59,7 @@ import org.apache.druid.indexing.worker.config.WorkerConfig; import org.apache.druid.indexing.worker.http.ShuffleResource; import org.apache.druid.java.util.common.logger.Logger; +import org.apache.druid.metadata.input.InputSourceModule; import org.apache.druid.query.QuerySegmentWalker; import org.apache.druid.query.lookup.LookupModule; import org.apache.druid.segment.realtime.appenderator.AppenderatorsManager; @@ -202,6 +203,7 @@ public DataNodeService getDataNodeService(DruidServerConfig serverConfig) new IndexingServiceInputSourceModule(), new IndexingServiceTaskLogsModule(), new IndexingServiceTuningConfigModule(), + new InputSourceModule(), new QueryablePeonModule(), new CliIndexerServerModule(properties), new LookupModule() diff --git a/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java b/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java index d273c16ad331..8b3d12f286eb 100644 --- a/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java +++ b/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java @@ -61,6 +61,7 @@ import org.apache.druid.indexing.worker.http.TaskManagementResource; import org.apache.druid.indexing.worker.http.WorkerResource; import org.apache.druid.java.util.common.logger.Logger; +import org.apache.druid.metadata.input.InputSourceModule; import org.apache.druid.query.lookup.LookupSerdeModule; import org.apache.druid.segment.realtime.appenderator.AppenderatorsManager; import org.apache.druid.segment.realtime.appenderator.DummyForInjectionAppenderatorsManager; @@ -186,6 +187,7 @@ public WorkerNodeService getWorkerNodeService(WorkerConfig workerConfig) new IndexingServiceInputSourceModule(), new IndexingServiceTaskLogsModule(), new IndexingServiceTuningConfigModule(), + new InputSourceModule(), new LookupSerdeModule() ); } diff --git a/services/src/main/java/org/apache/druid/cli/CliOverlord.java b/services/src/main/java/org/apache/druid/cli/CliOverlord.java index 21ffa9a8bff2..4baf155d229c 100644 --- a/services/src/main/java/org/apache/druid/cli/CliOverlord.java +++ b/services/src/main/java/org/apache/druid/cli/CliOverlord.java @@ -98,6 +98,7 @@ import org.apache.druid.indexing.overlord.supervisor.SupervisorResource; import org.apache.druid.indexing.worker.config.WorkerConfig; import org.apache.druid.java.util.common.logger.Logger; +import org.apache.druid.metadata.input.InputSourceModule; import org.apache.druid.query.lookup.LookupSerdeModule; import org.apache.druid.segment.realtime.appenderator.AppenderatorsManager; import org.apache.druid.segment.realtime.appenderator.DummyForInjectionAppenderatorsManager; @@ -347,6 +348,7 @@ private void configureOverlordHelpers(Binder binder) new IndexingServiceInputSourceModule(), new IndexingServiceTaskLogsModule(), new IndexingServiceTuningConfigModule(), + new InputSourceModule(), new SupervisorModule(), new LookupSerdeModule(), new SamplerModule() diff --git a/services/src/main/java/org/apache/druid/cli/CliPeon.java b/services/src/main/java/org/apache/druid/cli/CliPeon.java index 59ef48aee96a..2eb2c68521fc 100644 --- a/services/src/main/java/org/apache/druid/cli/CliPeon.java +++ b/services/src/main/java/org/apache/druid/cli/CliPeon.java @@ -91,6 +91,7 @@ import org.apache.druid.java.util.common.lifecycle.Lifecycle; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.metadata.IndexerSQLMetadataStorageCoordinator; +import org.apache.druid.metadata.input.InputSourceModule; import org.apache.druid.query.QuerySegmentWalker; import org.apache.druid.query.lookup.LookupModule; import org.apache.druid.segment.loading.DataSegmentArchiver; @@ -268,6 +269,7 @@ public String getTaskIDFromTask(final Task task) new IndexingServiceFirehoseModule(), new IndexingServiceInputSourceModule(), new IndexingServiceTuningConfigModule(), + new InputSourceModule(), new ChatHandlerServerModule(properties), new LookupModule() ); diff --git a/website/.spelling b/website/.spelling index 8d41dc98a478..f96b49dc6cfc 100644 --- a/website/.spelling +++ b/website/.spelling @@ -98,6 +98,7 @@ IndexTask InfluxDB InputFormat InputSource +InputSources Integer.MAX_VALUE JBOD JDBC @@ -151,6 +152,7 @@ S3 SDK SIGAR SPNEGO +SqlInputSource SQLServer SSD SSDs @@ -1756,4 +1758,4 @@ UserGroupInformation CVE-2019-17571 CVE-2019-12399 CVE-2018-17196 -bin.tar.gz \ No newline at end of file +bin.tar.gz From c8067d7f3b46cc6776bfa0eecf580b86cd11e5e3 Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Tue, 9 Jun 2020 19:31:04 -0700 Subject: [PATCH 068/107] add a GeneratorInputSource to fill up a cluster with generated data for testing (#9946) * move benchmark data generator into druid-processing, add a GeneratorInputSource to fill up a cluster with data * newlines * make test coverage not fail maybe * remove useless test * Update pom.xml * Update GeneratorInputSourceTest.java * less passive aggressive test names --- .../ExpressionAggregationBenchmark.java | 10 +- .../benchmark/ExpressionFilterBenchmark.java | 10 +- .../ExpressionSelectorBenchmark.java | 10 +- .../benchmark/FilterPartitionBenchmark.java | 12 +- .../FilteredAggregatorBenchmark.java | 14 +- ...loatCompressionBenchmarkFileGenerator.java | 30 +- .../GroupByTypeInterfaceBenchmark.java | 14 +- ...LongCompressionBenchmarkFileGenerator.java | 30 +- .../benchmark/TopNTypeInterfaceBenchmark.java | 14 +- .../benchmark/datagen/BenchmarkSchemas.java | 297 ------------- .../benchmark/datagen/SegmentGenerator.java | 6 +- .../IncrementalIndexReadBenchmark.java | 12 +- .../indexing/IndexIngestionBenchmark.java | 12 +- .../indexing/IndexMergeBenchmark.java | 12 +- .../indexing/IndexPersistBenchmark.java | 12 +- .../CachingClusteredClientBenchmark.java | 8 +- .../benchmark/query/GroupByBenchmark.java | 22 +- .../druid/benchmark/query/ScanBenchmark.java | 24 +- .../benchmark/query/SearchBenchmark.java | 24 +- .../druid/benchmark/query/SqlBenchmark.java | 6 +- .../benchmark/query/SqlVsNativeBenchmark.java | 6 +- .../benchmark/query/TimeseriesBenchmark.java | 14 +- .../druid/benchmark/query/TopNBenchmark.java | 14 +- .../timecompare/TimeCompareBenchmark.java | 14 +- .../IndexingServiceInputSourceModule.java | 4 +- .../indexing/input/GeneratorInputSource.java | 292 ++++++++++++ .../input/GeneratorInputSourceTest.java | 165 +++++++ processing/pom.xml | 2 +- .../generator/ColumnValueGenerator.java | 24 +- .../segment/generator/DataGenerator.java | 26 +- .../EnumeratedTreeDistribution.java | 4 +- .../generator/GeneratorBasicSchemas.java | 420 ++++++++++++++++++ .../generator/GeneratorColumnSchema.java | 227 ++++++++-- .../generator/GeneratorSchemaInfo.java | 14 +- .../generator}/RealRoundingDistribution.java | 2 +- .../generator}/SequentialDistribution.java | 2 +- .../segment/generator/DataGeneratorTest.java | 99 +++-- .../generator/GeneratorColumnSchemaTest.java | 63 +++ 38 files changed, 1405 insertions(+), 566 deletions(-) delete mode 100644 benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkSchemas.java create mode 100644 indexing-service/src/main/java/org/apache/druid/indexing/input/GeneratorInputSource.java create mode 100644 indexing-service/src/test/java/org/apache/druid/indexing/input/GeneratorInputSourceTest.java rename benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkColumnValueGenerator.java => processing/src/main/java/org/apache/druid/segment/generator/ColumnValueGenerator.java (90%) rename benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkDataGenerator.java => processing/src/main/java/org/apache/druid/segment/generator/DataGenerator.java (83%) rename {benchmarks/src/test/java/org/apache/druid/benchmark/datagen => processing/src/main/java/org/apache/druid/segment/generator}/EnumeratedTreeDistribution.java (95%) create mode 100644 processing/src/main/java/org/apache/druid/segment/generator/GeneratorBasicSchemas.java rename benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkColumnSchema.java => processing/src/main/java/org/apache/druid/segment/generator/GeneratorColumnSchema.java (58%) rename benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkSchemaInfo.java => processing/src/main/java/org/apache/druid/segment/generator/GeneratorSchemaInfo.java (86%) rename {benchmarks/src/test/java/org/apache/druid/benchmark/datagen => processing/src/main/java/org/apache/druid/segment/generator}/RealRoundingDistribution.java (97%) rename {benchmarks/src/test/java/org/apache/druid/benchmark/datagen => processing/src/main/java/org/apache/druid/segment/generator}/SequentialDistribution.java (97%) rename benchmarks/src/test/java/org/apache/druid/benchmark/BenchmarkDataGeneratorTest.java => processing/src/test/java/org/apache/druid/segment/generator/DataGeneratorTest.java (72%) create mode 100644 processing/src/test/java/org/apache/druid/segment/generator/GeneratorColumnSchemaTest.java diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionAggregationBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionAggregationBenchmark.java index 7d7c8c45f81a..83a13e9a0a67 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionAggregationBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionAggregationBenchmark.java @@ -21,8 +21,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -import org.apache.druid.benchmark.datagen.BenchmarkColumnSchema; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; import org.apache.druid.benchmark.datagen.SegmentGenerator; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.Intervals; @@ -42,6 +40,8 @@ import org.apache.druid.segment.QueryableIndexStorageAdapter; import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.column.ValueType; +import org.apache.druid.segment.generator.GeneratorColumnSchema; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.partition.LinearShardSpec; import org.openjdk.jmh.annotations.Benchmark; @@ -90,10 +90,10 @@ public void setup() { this.closer = Closer.create(); - final BenchmarkSchemaInfo schemaInfo = new BenchmarkSchemaInfo( + final GeneratorSchemaInfo schemaInfo = new GeneratorSchemaInfo( ImmutableList.of( - BenchmarkColumnSchema.makeNormal("x", ValueType.FLOAT, false, 1, 0d, 0d, 10000d, false), - BenchmarkColumnSchema.makeNormal("y", ValueType.FLOAT, false, 1, 0d, 0d, 10000d, false) + GeneratorColumnSchema.makeNormal("x", ValueType.FLOAT, false, 1, 0d, 0d, 10000d, false), + GeneratorColumnSchema.makeNormal("y", ValueType.FLOAT, false, 1, 0d, 0d, 10000d, false) ), ImmutableList.of(), Intervals.of("2000/P1D"), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionFilterBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionFilterBenchmark.java index 6b96d6137357..ea7ac3bdac8f 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionFilterBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionFilterBenchmark.java @@ -20,8 +20,6 @@ package org.apache.druid.benchmark; import com.google.common.collect.ImmutableList; -import org.apache.druid.benchmark.datagen.BenchmarkColumnSchema; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; import org.apache.druid.benchmark.datagen.SegmentGenerator; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.Intervals; @@ -39,6 +37,8 @@ import org.apache.druid.segment.QueryableIndexStorageAdapter; import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.column.ValueType; +import org.apache.druid.segment.generator.GeneratorColumnSchema; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.partition.LinearShardSpec; import org.openjdk.jmh.annotations.Benchmark; @@ -86,9 +86,9 @@ public void setup() { this.closer = Closer.create(); - final BenchmarkSchemaInfo schemaInfo = new BenchmarkSchemaInfo( + final GeneratorSchemaInfo schemaInfo = new GeneratorSchemaInfo( ImmutableList.of( - BenchmarkColumnSchema.makeEnumerated( + GeneratorColumnSchema.makeEnumerated( "x", ValueType.STRING, false, @@ -97,7 +97,7 @@ public void setup() Arrays.asList("Apple", "Orange", "Xylophone", "Corundum", null), Arrays.asList(0.2, 0.25, 0.15, 0.10, 0.3) ), - BenchmarkColumnSchema.makeEnumerated( + GeneratorColumnSchema.makeEnumerated( "y", ValueType.STRING, false, diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionSelectorBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionSelectorBenchmark.java index d3406a469051..0ee1db3669c7 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionSelectorBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/ExpressionSelectorBenchmark.java @@ -20,8 +20,6 @@ package org.apache.druid.benchmark; import com.google.common.collect.ImmutableList; -import org.apache.druid.benchmark.datagen.BenchmarkColumnSchema; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; import org.apache.druid.benchmark.datagen.SegmentGenerator; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.Intervals; @@ -41,6 +39,8 @@ import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.column.ColumnHolder; import org.apache.druid.segment.column.ValueType; +import org.apache.druid.segment.generator.GeneratorColumnSchema; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.partition.LinearShardSpec; @@ -86,9 +86,9 @@ public void setup() { this.closer = Closer.create(); - final BenchmarkSchemaInfo schemaInfo = new BenchmarkSchemaInfo( + final GeneratorSchemaInfo schemaInfo = new GeneratorSchemaInfo( ImmutableList.of( - BenchmarkColumnSchema.makeZipf( + GeneratorColumnSchema.makeZipf( "n", ValueType.LONG, false, @@ -98,7 +98,7 @@ public void setup() 10000, 3d ), - BenchmarkColumnSchema.makeZipf( + GeneratorColumnSchema.makeZipf( "s", ValueType.STRING, false, diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/FilterPartitionBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/FilterPartitionBenchmark.java index 512f1eb932db..5efe00e5b6aa 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/FilterPartitionBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/FilterPartitionBenchmark.java @@ -22,9 +22,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.common.config.NullHandling; import org.apache.druid.data.input.InputRow; import org.apache.druid.jackson.DefaultObjectMapper; @@ -69,6 +66,9 @@ import org.apache.druid.segment.filter.Filters; import org.apache.druid.segment.filter.OrFilter; import org.apache.druid.segment.filter.SelectorFilter; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.serde.ComplexMetrics; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; @@ -125,7 +125,7 @@ public class FilterPartitionBenchmark private Filter timeFilterHalf; private Filter timeFilterAll; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; private static String JS_FN = "function(str) { return 'super-' + str; }"; private static ExtractionFn JS_EXTRACTION_FN = new JavaScriptExtractionFn(JS_FN, false, JavaScriptConfig.getEnabledInstance()); @@ -153,9 +153,9 @@ public void setup() throws IOException ComplexMetrics.registerSerde("hyperUnique", new HyperUniquesSerde()); - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schema); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schema); - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED, schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/FilteredAggregatorBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/FilteredAggregatorBenchmark.java index f60708596a8c..ef4ca7bc1925 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/FilteredAggregatorBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/FilteredAggregatorBenchmark.java @@ -21,9 +21,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.benchmark.query.QueryBenchmarkUtil; import org.apache.druid.common.config.NullHandling; import org.apache.druid.data.input.InputRow; @@ -67,6 +64,9 @@ import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.QueryableIndexSegment; import org.apache.druid.segment.column.ColumnConfig; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.serde.ComplexMetrics; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; @@ -125,7 +125,7 @@ public class FilteredAggregatorBenchmark private DimFilter filter; private List inputRows; private QueryRunnerFactory factory; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; private TimeseriesQuery query; private File tmpDir; @@ -152,9 +152,9 @@ public void setup() throws IOException ComplexMetrics.registerSerde("hyperUnique", new HyperUniquesSerde()); - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schema); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schema); - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED, schemaInfo.getDataInterval(), @@ -202,7 +202,7 @@ public void setup() throws IOException QueryBenchmarkUtil.NOOP_QUERYWATCHER ); - BenchmarkSchemaInfo basicSchema = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + GeneratorSchemaInfo basicSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); List queryAggs = new ArrayList<>(); queryAggs.add(filteredMetrics[0]); diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/FloatCompressionBenchmarkFileGenerator.java b/benchmarks/src/test/java/org/apache/druid/benchmark/FloatCompressionBenchmarkFileGenerator.java index 1570619b9029..424f2977104c 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/FloatCompressionBenchmarkFileGenerator.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/FloatCompressionBenchmarkFileGenerator.java @@ -20,14 +20,14 @@ package org.apache.druid.benchmark; import com.google.common.collect.ImmutableList; -import org.apache.druid.benchmark.datagen.BenchmarkColumnSchema; -import org.apache.druid.benchmark.datagen.BenchmarkColumnValueGenerator; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.data.ColumnarFloatsSerializer; import org.apache.druid.segment.data.CompressionFactory; import org.apache.druid.segment.data.CompressionStrategy; +import org.apache.druid.segment.generator.ColumnValueGenerator; +import org.apache.druid.segment.generator.GeneratorColumnSchema; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMedium; import java.io.BufferedReader; @@ -65,7 +65,7 @@ public static void main(String[] args) throws IOException dirPath = args[0]; } - BenchmarkColumnSchema enumeratedSchema = BenchmarkColumnSchema.makeEnumerated( + GeneratorColumnSchema enumeratedSchema = GeneratorColumnSchema.makeEnumerated( "", ValueType.FLOAT, true, @@ -86,7 +86,7 @@ public static void main(String[] args) throws IOException 0.0001 ) ); - BenchmarkColumnSchema zipfLowSchema = BenchmarkColumnSchema.makeZipf( + GeneratorColumnSchema zipfLowSchema = GeneratorColumnSchema.makeZipf( "", ValueType.FLOAT, true, @@ -96,7 +96,7 @@ public static void main(String[] args) throws IOException 1000, 1d ); - BenchmarkColumnSchema zipfHighSchema = BenchmarkColumnSchema.makeZipf( + GeneratorColumnSchema zipfHighSchema = GeneratorColumnSchema.makeZipf( "", ValueType.FLOAT, true, @@ -106,7 +106,7 @@ public static void main(String[] args) throws IOException 1000, 3d ); - BenchmarkColumnSchema sequentialSchema = BenchmarkColumnSchema.makeSequential( + GeneratorColumnSchema sequentialSchema = GeneratorColumnSchema.makeSequential( "", ValueType.FLOAT, true, @@ -115,7 +115,7 @@ public static void main(String[] args) throws IOException 1470187671, 2000000000 ); - BenchmarkColumnSchema uniformSchema = BenchmarkColumnSchema.makeContinuousUniform( + GeneratorColumnSchema uniformSchema = GeneratorColumnSchema.makeContinuousUniform( "", ValueType.FLOAT, true, @@ -125,18 +125,18 @@ public static void main(String[] args) throws IOException 1000 ); - Map generators = new HashMap<>(); - generators.put("enumerate", new BenchmarkColumnValueGenerator(enumeratedSchema, 1)); - generators.put("zipfLow", new BenchmarkColumnValueGenerator(zipfLowSchema, 1)); - generators.put("zipfHigh", new BenchmarkColumnValueGenerator(zipfHighSchema, 1)); - generators.put("sequential", new BenchmarkColumnValueGenerator(sequentialSchema, 1)); - generators.put("uniform", new BenchmarkColumnValueGenerator(uniformSchema, 1)); + Map generators = new HashMap<>(); + generators.put("enumerate", new ColumnValueGenerator(enumeratedSchema, 1)); + generators.put("zipfLow", new ColumnValueGenerator(zipfLowSchema, 1)); + generators.put("zipfHigh", new ColumnValueGenerator(zipfHighSchema, 1)); + generators.put("sequential", new ColumnValueGenerator(sequentialSchema, 1)); + generators.put("uniform", new ColumnValueGenerator(uniformSchema, 1)); File dir = new File(dirPath); dir.mkdir(); // create data files using BenchmarkColunValueGenerator - for (Map.Entry entry : generators.entrySet()) { + for (Map.Entry entry : generators.entrySet()) { final File dataFile = new File(dir, entry.getKey()); dataFile.delete(); try (Writer writer = Files.newBufferedWriter(dataFile.toPath(), StandardCharsets.UTF_8)) { @@ -147,7 +147,7 @@ public static void main(String[] args) throws IOException } // create compressed files using all combinations of CompressionStrategy and FloatEncoding provided - for (Map.Entry entry : generators.entrySet()) { + for (Map.Entry entry : generators.entrySet()) { for (CompressionStrategy compression : COMPRESSIONS) { String name = entry.getKey() + "-" + compression; log.info("%s: ", name); diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/GroupByTypeInterfaceBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/GroupByTypeInterfaceBenchmark.java index 647a787f67b6..d12ff8784f2e 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/GroupByTypeInterfaceBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/GroupByTypeInterfaceBenchmark.java @@ -23,9 +23,6 @@ import com.fasterxml.jackson.dataformat.smile.SmileFactory; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.benchmark.query.QueryBenchmarkUtil; import org.apache.druid.collections.BlockingPool; import org.apache.druid.collections.DefaultBlockingPool; @@ -70,6 +67,9 @@ import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.QueryableIndexSegment; import org.apache.druid.segment.column.ColumnConfig; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.serde.ComplexMetrics; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; @@ -144,7 +144,7 @@ public class GroupByTypeInterfaceBenchmark private QueryRunnerFactory factory; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; private GroupByQuery stringQuery; private GroupByQuery longFloatQuery; private GroupByQuery floatQuery; @@ -172,7 +172,7 @@ private void setupQueries() { // queries for the basic schema Map basicQueries = new LinkedHashMap<>(); - BenchmarkSchemaInfo basicSchema = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + GeneratorSchemaInfo basicSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); { // basic.A QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -277,13 +277,13 @@ public void setup() throws IOException String schemaName = "basic"; - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schemaName); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schemaName); stringQuery = SCHEMA_QUERY_MAP.get(schemaName).get("string"); longFloatQuery = SCHEMA_QUERY_MAP.get(schemaName).get("longFloat"); longQuery = SCHEMA_QUERY_MAP.get(schemaName).get("long"); floatQuery = SCHEMA_QUERY_MAP.get(schemaName).get("float"); - final BenchmarkDataGenerator dataGenerator = new BenchmarkDataGenerator( + final DataGenerator dataGenerator = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED + 1, schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/LongCompressionBenchmarkFileGenerator.java b/benchmarks/src/test/java/org/apache/druid/benchmark/LongCompressionBenchmarkFileGenerator.java index ead3dd0e5cc7..b9bca954de45 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/LongCompressionBenchmarkFileGenerator.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/LongCompressionBenchmarkFileGenerator.java @@ -20,14 +20,14 @@ package org.apache.druid.benchmark; import com.google.common.collect.ImmutableList; -import org.apache.druid.benchmark.datagen.BenchmarkColumnSchema; -import org.apache.druid.benchmark.datagen.BenchmarkColumnValueGenerator; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.data.ColumnarLongsSerializer; import org.apache.druid.segment.data.CompressionFactory; import org.apache.druid.segment.data.CompressionStrategy; +import org.apache.druid.segment.generator.ColumnValueGenerator; +import org.apache.druid.segment.generator.GeneratorColumnSchema; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMedium; import java.io.BufferedReader; @@ -66,7 +66,7 @@ public static void main(String[] args) throws IOException dirPath = args[0]; } - BenchmarkColumnSchema enumeratedSchema = BenchmarkColumnSchema.makeEnumerated( + GeneratorColumnSchema enumeratedSchema = GeneratorColumnSchema.makeEnumerated( "", ValueType.LONG, true, @@ -87,8 +87,8 @@ public static void main(String[] args) throws IOException 0.0001 ) ); - BenchmarkColumnSchema zipfLowSchema = BenchmarkColumnSchema.makeZipf("", ValueType.LONG, true, 1, 0d, -1, 1000, 1d); - BenchmarkColumnSchema zipfHighSchema = BenchmarkColumnSchema.makeZipf( + GeneratorColumnSchema zipfLowSchema = GeneratorColumnSchema.makeZipf("", ValueType.LONG, true, 1, 0d, -1, 1000, 1d); + GeneratorColumnSchema zipfHighSchema = GeneratorColumnSchema.makeZipf( "", ValueType.LONG, true, @@ -98,7 +98,7 @@ public static void main(String[] args) throws IOException 1000, 3d ); - BenchmarkColumnSchema sequentialSchema = BenchmarkColumnSchema.makeSequential( + GeneratorColumnSchema sequentialSchema = GeneratorColumnSchema.makeSequential( "", ValueType.LONG, true, @@ -107,7 +107,7 @@ public static void main(String[] args) throws IOException 1470187671, 2000000000 ); - BenchmarkColumnSchema uniformSchema = BenchmarkColumnSchema.makeDiscreteUniform( + GeneratorColumnSchema uniformSchema = GeneratorColumnSchema.makeDiscreteUniform( "", ValueType.LONG, true, @@ -117,18 +117,18 @@ public static void main(String[] args) throws IOException 1000 ); - Map generators = new HashMap<>(); - generators.put("enumerate", new BenchmarkColumnValueGenerator(enumeratedSchema, 1)); - generators.put("zipfLow", new BenchmarkColumnValueGenerator(zipfLowSchema, 1)); - generators.put("zipfHigh", new BenchmarkColumnValueGenerator(zipfHighSchema, 1)); - generators.put("sequential", new BenchmarkColumnValueGenerator(sequentialSchema, 1)); - generators.put("uniform", new BenchmarkColumnValueGenerator(uniformSchema, 1)); + Map generators = new HashMap<>(); + generators.put("enumerate", new ColumnValueGenerator(enumeratedSchema, 1)); + generators.put("zipfLow", new ColumnValueGenerator(zipfLowSchema, 1)); + generators.put("zipfHigh", new ColumnValueGenerator(zipfHighSchema, 1)); + generators.put("sequential", new ColumnValueGenerator(sequentialSchema, 1)); + generators.put("uniform", new ColumnValueGenerator(uniformSchema, 1)); File dir = new File(dirPath); dir.mkdir(); // create data files using BenchmarkColunValueGenerator - for (Map.Entry entry : generators.entrySet()) { + for (Map.Entry entry : generators.entrySet()) { final File dataFile = new File(dir, entry.getKey()); dataFile.delete(); try (Writer writer = Files.newBufferedWriter(dataFile.toPath(), StandardCharsets.UTF_8)) { @@ -139,7 +139,7 @@ public static void main(String[] args) throws IOException } // create compressed files using all combinations of CompressionStrategy and LongEncoding provided - for (Map.Entry entry : generators.entrySet()) { + for (Map.Entry entry : generators.entrySet()) { for (CompressionStrategy compression : COMPRESSIONS) { for (CompressionFactory.LongEncodingStrategy encoding : ENCODINGS) { String name = entry.getKey() + "-" + compression + "-" + encoding; diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/TopNTypeInterfaceBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/TopNTypeInterfaceBenchmark.java index 69f48664555f..241bd80ff721 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/TopNTypeInterfaceBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/TopNTypeInterfaceBenchmark.java @@ -20,9 +20,6 @@ package org.apache.druid.benchmark; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.benchmark.query.QueryBenchmarkUtil; import org.apache.druid.collections.StupidPool; import org.apache.druid.common.config.NullHandling; @@ -66,6 +63,9 @@ import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.QueryableIndexSegment; import org.apache.druid.segment.column.ColumnConfig; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.serde.ComplexMetrics; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; @@ -125,7 +125,7 @@ public class TopNTypeInterfaceBenchmark private List qIndexes; private QueryRunnerFactory factory; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; private TopNQueryBuilder queryBuilder; private TopNQuery stringQuery; private TopNQuery longQuery; @@ -153,7 +153,7 @@ private void setupQueries() { // queries for the basic schema Map basicQueries = new LinkedHashMap<>(); - BenchmarkSchemaInfo basicSchema = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + GeneratorSchemaInfo basicSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); { // basic.A QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -241,7 +241,7 @@ public void setup() throws IOException setupQueries(); - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); queryBuilder = SCHEMA_QUERY_MAP.get("basic").get("string"); queryBuilder.threshold(threshold); stringQuery = queryBuilder.build(); @@ -258,7 +258,7 @@ public void setup() throws IOException for (int i = 0; i < numSegments; i++) { log.info("Generating rows for segment " + i); - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED + i, schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkSchemas.java b/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkSchemas.java deleted file mode 100644 index 142843753fdc..000000000000 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkSchemas.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.benchmark.datagen; - -import com.google.common.collect.ImmutableList; -import org.apache.druid.java.util.common.Intervals; -import org.apache.druid.math.expr.ExprMacroTable; -import org.apache.druid.query.aggregation.AggregatorFactory; -import org.apache.druid.query.aggregation.CountAggregatorFactory; -import org.apache.druid.query.aggregation.DoubleMinAggregatorFactory; -import org.apache.druid.query.aggregation.DoubleSumAggregatorFactory; -import org.apache.druid.query.aggregation.LongMaxAggregatorFactory; -import org.apache.druid.query.aggregation.LongSumAggregatorFactory; -import org.apache.druid.query.aggregation.hyperloglog.HyperUniquesAggregatorFactory; -import org.apache.druid.segment.column.ValueType; -import org.joda.time.Interval; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -public class BenchmarkSchemas -{ - public static final Map SCHEMA_MAP = new LinkedHashMap<>(); - - static { // basic schema - List basicSchemaColumns = ImmutableList.of( - // dims - BenchmarkColumnSchema.makeSequential("dimSequential", ValueType.STRING, false, 1, null, 0, 1000), - BenchmarkColumnSchema.makeZipf("dimZipf", ValueType.STRING, false, 1, null, 1, 101, 1.0), - BenchmarkColumnSchema.makeDiscreteUniform("dimUniform", ValueType.STRING, false, 1, null, 1, 100000), - BenchmarkColumnSchema.makeSequential("dimSequentialHalfNull", ValueType.STRING, false, 1, 0.5, 0, 1000), - BenchmarkColumnSchema.makeEnumerated( - "dimMultivalEnumerated", - ValueType.STRING, - false, - 4, - null, - Arrays.asList("Hello", "World", "Foo", "Bar", "Baz"), - Arrays.asList(0.2, 0.25, 0.15, 0.10, 0.3) - ), - BenchmarkColumnSchema.makeEnumerated( - "dimMultivalEnumerated2", - ValueType.STRING, - false, - 3, - null, - Arrays.asList("Apple", "Orange", "Xylophone", "Corundum", null), - Arrays.asList(0.2, 0.25, 0.15, 0.10, 0.3) - ), - BenchmarkColumnSchema.makeSequential("dimMultivalSequentialWithNulls", ValueType.STRING, false, 8, 0.15, 1, 11), - BenchmarkColumnSchema.makeSequential("dimHyperUnique", ValueType.STRING, false, 1, null, 0, 100000), - BenchmarkColumnSchema.makeSequential("dimNull", ValueType.STRING, false, 1, 1.0, 0, 1), - - // metrics - BenchmarkColumnSchema.makeSequential("metLongSequential", ValueType.LONG, true, 1, null, 0, 10000), - BenchmarkColumnSchema.makeDiscreteUniform("metLongUniform", ValueType.LONG, true, 1, null, 0, 500), - BenchmarkColumnSchema.makeNormal("metFloatNormal", ValueType.FLOAT, true, 1, null, 5000.0, 1.0, true), - BenchmarkColumnSchema.makeZipf("metFloatZipf", ValueType.FLOAT, true, 1, null, 0, 1000, 1.0) - ); - - List basicSchemaIngestAggs = new ArrayList<>(); - basicSchemaIngestAggs.add(new CountAggregatorFactory("rows")); - basicSchemaIngestAggs.add(new LongSumAggregatorFactory("sumLongSequential", "metLongSequential")); - basicSchemaIngestAggs.add(new LongMaxAggregatorFactory("maxLongUniform", "metLongUniform")); - basicSchemaIngestAggs.add(new DoubleSumAggregatorFactory("sumFloatNormal", "metFloatNormal")); - basicSchemaIngestAggs.add(new DoubleMinAggregatorFactory("minFloatZipf", "metFloatZipf")); - basicSchemaIngestAggs.add(new HyperUniquesAggregatorFactory("hyper", "dimHyperUnique")); - - List basicSchemaIngestAggsExpression = new ArrayList<>(); - basicSchemaIngestAggsExpression.add(new CountAggregatorFactory("rows")); - basicSchemaIngestAggsExpression.add(new LongSumAggregatorFactory("sumLongSequential", null, "if(sumLongSequential>0 && dimSequential>100 || dimSequential<10 || metLongSequential>3000,sumLongSequential,0)", ExprMacroTable.nil())); - basicSchemaIngestAggsExpression.add(new LongMaxAggregatorFactory("maxLongUniform", "metLongUniform")); - basicSchemaIngestAggsExpression.add(new DoubleSumAggregatorFactory("sumFloatNormal", null, "if(sumFloatNormal>0 && dimSequential>100 || dimSequential<10 || metLongSequential>3000,sumFloatNormal,0)", ExprMacroTable.nil())); - basicSchemaIngestAggsExpression.add(new DoubleMinAggregatorFactory("minFloatZipf", "metFloatZipf")); - basicSchemaIngestAggsExpression.add(new HyperUniquesAggregatorFactory("hyper", "dimHyperUnique")); - - Interval basicSchemaDataInterval = Intervals.of("2000-01-01/P1D"); - - BenchmarkSchemaInfo basicSchema = new BenchmarkSchemaInfo( - basicSchemaColumns, - basicSchemaIngestAggs, - basicSchemaDataInterval, - true - ); - - BenchmarkSchemaInfo basicSchemaExpression = new BenchmarkSchemaInfo( - basicSchemaColumns, - basicSchemaIngestAggsExpression, - basicSchemaDataInterval, - true - ); - - SCHEMA_MAP.put("basic", basicSchema); - SCHEMA_MAP.put("expression", basicSchemaExpression); - } - - static { // simple single string column and count agg schema, no rollup - List basicSchemaColumns = ImmutableList.of( - // dims - BenchmarkColumnSchema.makeSequential("dimSequential", ValueType.STRING, false, 1, null, 0, 1000000) - ); - - List basicSchemaIngestAggs = new ArrayList<>(); - basicSchemaIngestAggs.add(new CountAggregatorFactory("rows")); - - Interval basicSchemaDataInterval = Intervals.utc(0, 1000000); - - BenchmarkSchemaInfo basicSchema = new BenchmarkSchemaInfo( - basicSchemaColumns, - basicSchemaIngestAggs, - basicSchemaDataInterval, - false - ); - SCHEMA_MAP.put("simple", basicSchema); - } - - static { // simple single long column and count agg schema, no rollup - List basicSchemaColumns = ImmutableList.of( - // dims, ingest as a metric for now with rollup off, until numeric dims at ingestion are supported - BenchmarkColumnSchema.makeSequential("dimSequential", ValueType.LONG, true, 1, null, 0, 1000000) - ); - - List basicSchemaIngestAggs = new ArrayList<>(); - basicSchemaIngestAggs.add(new LongSumAggregatorFactory("dimSequential", "dimSequential")); - basicSchemaIngestAggs.add(new CountAggregatorFactory("rows")); - - Interval basicSchemaDataInterval = Intervals.utc(0, 1000000); - - BenchmarkSchemaInfo basicSchema = new BenchmarkSchemaInfo( - basicSchemaColumns, - basicSchemaIngestAggs, - basicSchemaDataInterval, - false - ); - SCHEMA_MAP.put("simpleLong", basicSchema); - } - - static { // simple single float column and count agg schema, no rollup - List basicSchemaColumns = ImmutableList.of( - // dims, ingest as a metric for now with rollup off, until numeric dims at ingestion are supported - BenchmarkColumnSchema.makeSequential("dimSequential", ValueType.FLOAT, true, 1, null, 0, 1000000) - ); - - List basicSchemaIngestAggs = new ArrayList<>(); - basicSchemaIngestAggs.add(new DoubleSumAggregatorFactory("dimSequential", "dimSequential")); - basicSchemaIngestAggs.add(new CountAggregatorFactory("rows")); - - Interval basicSchemaDataInterval = Intervals.utc(0, 1000000); - - BenchmarkSchemaInfo basicSchema = new BenchmarkSchemaInfo( - basicSchemaColumns, - basicSchemaIngestAggs, - basicSchemaDataInterval, - false - ); - SCHEMA_MAP.put("simpleFloat", basicSchema); - } - - static { // schema with high opportunity for rollup - List rolloColumns = ImmutableList.of( - // dims - BenchmarkColumnSchema.makeEnumerated( - "dimEnumerated", - ValueType.STRING, - false, - 1, - null, - Arrays.asList("Hello", "World", "Foo", "Bar", "Baz"), - Arrays.asList(0.2, 0.25, 0.15, 0.10, 0.3) - ), - BenchmarkColumnSchema.makeEnumerated( - "dimEnumerated2", - ValueType.STRING, - false, - 1, - null, - Arrays.asList("Apple", "Orange", "Xylophone", "Corundum", null), - Arrays.asList(0.2, 0.25, 0.15, 0.10, 0.3) - ), - BenchmarkColumnSchema.makeZipf("dimZipf", ValueType.STRING, false, 1, null, 1, 100, 2.0), - BenchmarkColumnSchema.makeDiscreteUniform("dimUniform", ValueType.STRING, false, 1, null, 1, 100), - - // metrics - BenchmarkColumnSchema.makeZipf("metLongZipf", ValueType.LONG, true, 1, null, 0, 10000, 2.0), - BenchmarkColumnSchema.makeDiscreteUniform("metLongUniform", ValueType.LONG, true, 1, null, 0, 500), - BenchmarkColumnSchema.makeNormal("metFloatNormal", ValueType.FLOAT, true, 1, null, 5000.0, 1.0, true), - BenchmarkColumnSchema.makeZipf("metFloatZipf", ValueType.FLOAT, true, 1, null, 0, 1000, 1.5) - ); - - List rolloSchemaIngestAggs = new ArrayList<>(); - rolloSchemaIngestAggs.add(new CountAggregatorFactory("rows")); - rolloSchemaIngestAggs.add(new LongSumAggregatorFactory("sumLongSequential", "metLongSequential")); - rolloSchemaIngestAggs.add(new LongMaxAggregatorFactory("maxLongUniform", "metLongUniform")); - rolloSchemaIngestAggs.add(new DoubleSumAggregatorFactory("sumFloatNormal", "metFloatNormal")); - rolloSchemaIngestAggs.add(new DoubleMinAggregatorFactory("minFloatZipf", "metFloatZipf")); - rolloSchemaIngestAggs.add(new HyperUniquesAggregatorFactory("hyper", "dimHyperUnique")); - - Interval basicSchemaDataInterval = Intervals.utc(0, 1000000); - - BenchmarkSchemaInfo rolloSchema = new BenchmarkSchemaInfo( - rolloColumns, - rolloSchemaIngestAggs, - basicSchemaDataInterval, - true - ); - SCHEMA_MAP.put("rollo", rolloSchema); - } - - static { // simple schema with null valued rows, no aggs on numeric columns - List nullsSchemaColumns = ImmutableList.of( - // string dims with nulls - BenchmarkColumnSchema.makeZipf("stringZipf", ValueType.STRING, false, 1, 0.8, 1, 101, 1.5), - BenchmarkColumnSchema.makeDiscreteUniform("stringUniform", ValueType.STRING, false, 1, 0.3, 1, 100000), - BenchmarkColumnSchema.makeSequential("stringSequential", ValueType.STRING, false, 1, 0.5, 0, 1000), - - // numeric dims with nulls - BenchmarkColumnSchema.makeSequential("longSequential", ValueType.LONG, false, 1, 0.45, 0, 10000), - BenchmarkColumnSchema.makeDiscreteUniform("longUniform", ValueType.LONG, false, 1, 0.25, 0, 500), - BenchmarkColumnSchema.makeZipf("doubleZipf", ValueType.DOUBLE, false, 1, 0.1, 0, 1000, 2.0), - BenchmarkColumnSchema.makeZipf("floatZipf", ValueType.FLOAT, false, 1, 0.1, 0, 1000, 2.0) - ); - - List simpleNullsSchemaIngestAggs = new ArrayList<>(); - simpleNullsSchemaIngestAggs.add(new CountAggregatorFactory("rows")); - - Interval nullsSchemaDataInterval = Intervals.of("2000-01-01/P1D"); - - BenchmarkSchemaInfo nullsSchema = new BenchmarkSchemaInfo( - nullsSchemaColumns, - simpleNullsSchemaIngestAggs, - nullsSchemaDataInterval, - false - ); - - SCHEMA_MAP.put("nulls", nullsSchema); - } - - static { // simple schema with null valued rows, no aggs on numeric columns - List nullsSchemaColumns = ImmutableList.of( - // string dims - BenchmarkColumnSchema.makeZipf("stringZipf", ValueType.STRING, false, 1, null, 1, 101, 1.5), - BenchmarkColumnSchema.makeDiscreteUniform("stringUniform", ValueType.STRING, false, 1, null, 1, 100000), - BenchmarkColumnSchema.makeSequential("stringSequential", ValueType.STRING, false, 1, null, 0, 1000), - - // numeric dims - BenchmarkColumnSchema.makeSequential("longSequential", ValueType.LONG, false, 1, null, 0, 10000), - BenchmarkColumnSchema.makeDiscreteUniform("longUniform", ValueType.LONG, false, 1, null, 0, 500), - BenchmarkColumnSchema.makeZipf("doubleZipf", ValueType.DOUBLE, false, 1, null, 0, 1000, 2.0), - BenchmarkColumnSchema.makeZipf("floatZipf", ValueType.FLOAT, false, 1, null, 0, 1000, 2.0), - - // string dims with nulls - BenchmarkColumnSchema.makeZipf("stringZipfWithNulls", ValueType.STRING, false, 1, 0.8, 1, 101, 1.5), - BenchmarkColumnSchema.makeDiscreteUniform("stringUniformWithNulls", ValueType.STRING, false, 1, 0.3, 1, 100000), - BenchmarkColumnSchema.makeSequential("stringSequentialWithNulls", ValueType.STRING, false, 1, 0.5, 0, 1000), - - // numeric dims with nulls - BenchmarkColumnSchema.makeSequential("longSequentialWithNulls", ValueType.LONG, false, 1, 0.45, 0, 10000), - BenchmarkColumnSchema.makeDiscreteUniform("longUniformWithNulls", ValueType.LONG, false, 1, 0.25, 0, 500), - BenchmarkColumnSchema.makeZipf("doubleZipfWithNulls", ValueType.DOUBLE, false, 1, 0.1, 0, 1000, 2.0), - BenchmarkColumnSchema.makeZipf("floatZipfWithNulls", ValueType.FLOAT, false, 1, 0.1, 0, 1000, 2.0) - ); - - List simpleNullsSchemaIngestAggs = new ArrayList<>(); - simpleNullsSchemaIngestAggs.add(new CountAggregatorFactory("rows")); - - Interval nullsSchemaDataInterval = Intervals.of("2000-01-01/P1D"); - - BenchmarkSchemaInfo nullsSchema = new BenchmarkSchemaInfo( - nullsSchemaColumns, - simpleNullsSchemaIngestAggs, - nullsSchemaDataInterval, - false - ); - - SCHEMA_MAP.put("nulls-and-non-nulls", nullsSchema); - } -} diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/SegmentGenerator.java b/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/SegmentGenerator.java index 95a42d5ffd88..f6d018d9bb82 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/SegmentGenerator.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/SegmentGenerator.java @@ -35,6 +35,8 @@ import org.apache.druid.segment.QueryableIndexIndexableAdapter; import org.apache.druid.segment.TestHelper; import org.apache.druid.segment.data.RoaringBitmapSerdeFactory; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndexSchema; import org.apache.druid.segment.serde.ComplexMetrics; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; @@ -100,7 +102,7 @@ public File getCacheDir() public QueryableIndex generate( final DataSegment dataSegment, - final BenchmarkSchemaInfo schemaInfo, + final GeneratorSchemaInfo schemaInfo, final Granularity granularity, final int numRows ) @@ -131,7 +133,7 @@ public QueryableIndex generate( log.info("Writing segment with hash[%s] to directory[%s].", dataHash, outDir); - final BenchmarkDataGenerator dataGenerator = new BenchmarkDataGenerator( + final DataGenerator dataGenerator = new DataGenerator( schemaInfo.getColumnSchemas(), dataSegment.getId().hashCode(), /* Use segment identifier hashCode as seed */ schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IncrementalIndexReadBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IncrementalIndexReadBenchmark.java index f7d1e91b5236..1a9ac01cf412 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IncrementalIndexReadBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IncrementalIndexReadBenchmark.java @@ -19,9 +19,6 @@ package org.apache.druid.benchmark.indexing; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.common.config.NullHandling; import org.apache.druid.data.input.InputRow; import org.apache.druid.java.util.common.granularity.Granularities; @@ -43,6 +40,9 @@ import org.apache.druid.segment.DimensionSelector; import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.data.IndexedInts; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.incremental.IncrementalIndexSchema; import org.apache.druid.segment.incremental.IncrementalIndexStorageAdapter; @@ -91,7 +91,7 @@ public class IncrementalIndexReadBenchmark private IncrementalIndex incIndex; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; @Setup public void setup() throws IOException @@ -100,9 +100,9 @@ public void setup() throws IOException ComplexMetrics.registerSerde("hyperUnique", new HyperUniquesSerde()); - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schema); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schema); - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED, schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexIngestionBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexIngestionBenchmark.java index 19862f27c948..02c6efd89569 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexIngestionBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexIngestionBenchmark.java @@ -19,13 +19,13 @@ package org.apache.druid.benchmark.indexing; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.common.config.NullHandling; import org.apache.druid.data.input.InputRow; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.query.aggregation.hyperloglog.HyperUniquesSerde; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.incremental.IncrementalIndexSchema; import org.apache.druid.segment.serde.ComplexMetrics; @@ -70,7 +70,7 @@ public class IndexIngestionBenchmark private IncrementalIndex incIndex; private ArrayList rows; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; @Setup public void setup() @@ -78,9 +78,9 @@ public void setup() ComplexMetrics.registerSerde("hyperUnique", new HyperUniquesSerde()); rows = new ArrayList(); - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schema); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schema); - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED, schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexMergeBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexMergeBenchmark.java index 6b3b902c3ad4..80e964362e44 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexMergeBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexMergeBenchmark.java @@ -21,9 +21,6 @@ import com.fasterxml.jackson.databind.InjectableValues; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.common.config.NullHandling; import org.apache.druid.data.input.InputRow; import org.apache.druid.jackson.DefaultObjectMapper; @@ -35,6 +32,9 @@ import org.apache.druid.segment.IndexMergerV9; import org.apache.druid.segment.IndexSpec; import org.apache.druid.segment.QueryableIndex; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.incremental.IncrementalIndexSchema; import org.apache.druid.segment.serde.ComplexMetrics; @@ -96,7 +96,7 @@ public class IndexMergeBenchmark } private List indexesToMerge; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; private File tmpDir; private IndexMergerV9 indexMergerV9; @@ -121,10 +121,10 @@ public void setup() throws IOException indexesToMerge = new ArrayList<>(); - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schema); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schema); for (int i = 0; i < numSegments; i++) { - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED + i, schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexPersistBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexPersistBenchmark.java index d826fe8fdc73..5b1a0ca6d6f4 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexPersistBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/indexing/IndexPersistBenchmark.java @@ -20,9 +20,6 @@ package org.apache.druid.benchmark.indexing; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.common.config.NullHandling; import org.apache.druid.data.input.InputRow; import org.apache.druid.jackson.DefaultObjectMapper; @@ -32,6 +29,9 @@ import org.apache.druid.segment.IndexIO; import org.apache.druid.segment.IndexMergerV9; import org.apache.druid.segment.IndexSpec; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.incremental.IncrementalIndexSchema; import org.apache.druid.segment.serde.ComplexMetrics; @@ -92,7 +92,7 @@ public class IndexPersistBenchmark private IncrementalIndex incIndex; private ArrayList rows; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; @Setup public void setup() @@ -102,7 +102,7 @@ public void setup() ComplexMetrics.registerSerde("hyperUnique", new HyperUniquesSerde()); rows = new ArrayList(); - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schema); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schema); int valuesPerTimestamp = 1; switch (rollupOpportunity) { @@ -115,7 +115,7 @@ public void setup() } - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED, schemaInfo.getDataInterval().getStartMillis(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/query/CachingClusteredClientBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/query/CachingClusteredClientBenchmark.java index 94b560e8f4df..daa614ff4ddd 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/query/CachingClusteredClientBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/query/CachingClusteredClientBenchmark.java @@ -27,8 +27,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.benchmark.datagen.SegmentGenerator; import org.apache.druid.client.CachingClusteredClient; import org.apache.druid.client.DruidServer; @@ -104,6 +102,8 @@ import org.apache.druid.query.topn.TopNResultValue; import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.QueryableIndexSegment; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.server.QueryStackTests; import org.apache.druid.server.coordination.ServerType; import org.apache.druid.timeline.DataSegment; @@ -180,7 +180,7 @@ public class CachingClusteredClientBenchmark private final Closer closer = Closer.create(); - private final BenchmarkSchemaInfo basicSchema = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + private final GeneratorSchemaInfo basicSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); private final QuerySegmentSpec basicSchemaIntervalSpec = new MultipleIntervalSegmentSpec( Collections.singletonList(basicSchema.getDataInterval()) ); @@ -204,7 +204,7 @@ public void setup() parallelCombine = parallelism > 0; - BenchmarkSchemaInfo schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schemaName); + GeneratorSchemaInfo schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schemaName); Map queryableIndexes = Maps.newHashMapWithExpectedSize(numServers); diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/query/GroupByBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/query/GroupByBenchmark.java index 02107f09af48..57214154cd2c 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/query/GroupByBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/query/GroupByBenchmark.java @@ -26,9 +26,6 @@ import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.collections.BlockingPool; import org.apache.druid.collections.DefaultBlockingPool; import org.apache.druid.collections.NonBlockingPool; @@ -84,6 +81,9 @@ import org.apache.druid.segment.QueryableIndexSegment; import org.apache.druid.segment.column.ColumnConfig; import org.apache.druid.segment.column.ValueType; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.incremental.IncrementalIndexSchema; import org.apache.druid.segment.serde.ComplexMetrics; @@ -161,7 +161,7 @@ public class GroupByBenchmark private QueryRunnerFactory factory; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; private GroupByQuery query; private ExecutorService executorService; @@ -192,7 +192,7 @@ private void setupQueries() { // queries for the basic schema Map basicQueries = new LinkedHashMap<>(); - BenchmarkSchemaInfo basicSchema = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + GeneratorSchemaInfo basicSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); { // basic.A QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -324,7 +324,7 @@ private void setupQueries() // simple one column schema, for testing performance difference between querying on numeric values as Strings and // directly as longs Map simpleQueries = new LinkedHashMap<>(); - BenchmarkSchemaInfo simpleSchema = BenchmarkSchemas.SCHEMA_MAP.get("simple"); + GeneratorSchemaInfo simpleSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("simple"); { // simple.A QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(simpleSchema.getDataInterval())); @@ -351,7 +351,7 @@ private void setupQueries() Map simpleLongQueries = new LinkedHashMap<>(); - BenchmarkSchemaInfo simpleLongSchema = BenchmarkSchemas.SCHEMA_MAP.get("simpleLong"); + GeneratorSchemaInfo simpleLongSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("simpleLong"); { // simpleLong.A QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(simpleLongSchema.getDataInterval())); List queryAggs = new ArrayList<>(); @@ -377,7 +377,7 @@ private void setupQueries() Map simpleFloatQueries = new LinkedHashMap<>(); - BenchmarkSchemaInfo simpleFloatSchema = BenchmarkSchemas.SCHEMA_MAP.get("simpleFloat"); + GeneratorSchemaInfo simpleFloatSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("simpleFloat"); { // simpleFloat.A QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(simpleFloatSchema.getDataInterval())); List queryAggs = new ArrayList<>(); @@ -402,7 +402,7 @@ private void setupQueries() // simple one column schema, for testing performance difference between querying on numeric values as Strings and // directly as longs Map nullQueries = new LinkedHashMap<>(); - BenchmarkSchemaInfo nullSchema = BenchmarkSchemas.SCHEMA_MAP.get("nulls"); + GeneratorSchemaInfo nullSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("nulls"); { // simple-null QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(nullSchema.getDataInterval())); @@ -443,10 +443,10 @@ public void setup() throws IOException String schemaName = schemaQuery[0]; String queryName = schemaQuery[1]; - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schemaName); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schemaName); query = SCHEMA_QUERY_MAP.get(schemaName).get(queryName); - final BenchmarkDataGenerator dataGenerator = new BenchmarkDataGenerator( + final DataGenerator dataGenerator = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED + 1, schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/query/ScanBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/query/ScanBenchmark.java index a2b0e98fb980..410b13e4661c 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/query/ScanBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/query/ScanBenchmark.java @@ -22,9 +22,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.common.config.NullHandling; import org.apache.druid.data.input.InputRow; import org.apache.druid.data.input.Row; @@ -67,6 +64,9 @@ import org.apache.druid.segment.IndexSpec; import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.QueryableIndexSegment; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.serde.ComplexMetrics; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; @@ -133,7 +133,7 @@ public class ScanBenchmark private List qIndexes; private QueryRunnerFactory factory; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; private Druids.ScanQueryBuilder queryBuilder; private ScanQuery query; private File tmpDir; @@ -155,7 +155,7 @@ private void setupQueries() { // queries for the basic schema final Map basicQueries = new LinkedHashMap<>(); - final BenchmarkSchemaInfo basicSchema = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + final GeneratorSchemaInfo basicSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); final List queryTypes = ImmutableList.of("A", "B", "C", "D"); for (final String eachType : queryTypes) { @@ -165,7 +165,7 @@ private void setupQueries() SCHEMA_QUERY_MAP.put("basic", basicQueries); } - private static Druids.ScanQueryBuilder makeQuery(final String name, final BenchmarkSchemaInfo basicSchema) + private static Druids.ScanQueryBuilder makeQuery(final String name, final GeneratorSchemaInfo basicSchema) { switch (name) { case "A": @@ -182,7 +182,7 @@ private static Druids.ScanQueryBuilder makeQuery(final String name, final Benchm } /* Just get everything */ - private static Druids.ScanQueryBuilder basicA(final BenchmarkSchemaInfo basicSchema) + private static Druids.ScanQueryBuilder basicA(final GeneratorSchemaInfo basicSchema) { final QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -193,7 +193,7 @@ private static Druids.ScanQueryBuilder basicA(final BenchmarkSchemaInfo basicSch .order(ordering); } - private static Druids.ScanQueryBuilder basicB(final BenchmarkSchemaInfo basicSchema) + private static Druids.ScanQueryBuilder basicB(final GeneratorSchemaInfo basicSchema) { final QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -214,7 +214,7 @@ private static Druids.ScanQueryBuilder basicB(final BenchmarkSchemaInfo basicSch .order(ordering); } - private static Druids.ScanQueryBuilder basicC(final BenchmarkSchemaInfo basicSchema) + private static Druids.ScanQueryBuilder basicC(final GeneratorSchemaInfo basicSchema) { final QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -227,7 +227,7 @@ private static Druids.ScanQueryBuilder basicC(final BenchmarkSchemaInfo basicSch .order(ordering); } - private static Druids.ScanQueryBuilder basicD(final BenchmarkSchemaInfo basicSchema) + private static Druids.ScanQueryBuilder basicD(final GeneratorSchemaInfo basicSchema) { final QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec( Collections.singletonList(basicSchema.getDataInterval()) @@ -257,7 +257,7 @@ public void setup() throws IOException String schemaName = schemaQuery[0]; String queryName = schemaQuery[1]; - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schemaName); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schemaName); queryBuilder = SCHEMA_QUERY_MAP.get(schemaName).get(queryName); queryBuilder.limit(limit); query = queryBuilder.build(); @@ -265,7 +265,7 @@ public void setup() throws IOException incIndexes = new ArrayList<>(); for (int i = 0; i < numSegments; i++) { log.info("Generating rows for segment " + i); - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), System.currentTimeMillis(), schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SearchBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SearchBenchmark.java index 06320c331101..d8ae55adc412 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SearchBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SearchBenchmark.java @@ -24,9 +24,6 @@ import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.common.config.NullHandling; import org.apache.druid.data.input.InputRow; import org.apache.druid.data.input.Row; @@ -74,6 +71,9 @@ import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.QueryableIndexSegment; import org.apache.druid.segment.column.ColumnConfig; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.serde.ComplexMetrics; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; @@ -133,7 +133,7 @@ public class SearchBenchmark private List qIndexes; private QueryRunnerFactory factory; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; private Druids.SearchQueryBuilder queryBuilder; private SearchQuery query; private File tmpDir; @@ -162,7 +162,7 @@ private void setupQueries() { // queries for the basic schema final Map basicQueries = new LinkedHashMap<>(); - final BenchmarkSchemaInfo basicSchema = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + final GeneratorSchemaInfo basicSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); final List queryTypes = ImmutableList.of("A", "B", "C", "D"); for (final String eachType : queryTypes) { @@ -172,7 +172,7 @@ private void setupQueries() SCHEMA_QUERY_MAP.put("basic", basicQueries); } - private static SearchQueryBuilder makeQuery(final String name, final BenchmarkSchemaInfo basicSchema) + private static SearchQueryBuilder makeQuery(final String name, final GeneratorSchemaInfo basicSchema) { switch (name) { case "A": @@ -188,7 +188,7 @@ private static SearchQueryBuilder makeQuery(final String name, final BenchmarkSc } } - private static SearchQueryBuilder basicA(final BenchmarkSchemaInfo basicSchema) + private static SearchQueryBuilder basicA(final GeneratorSchemaInfo basicSchema) { final QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -199,7 +199,7 @@ private static SearchQueryBuilder basicA(final BenchmarkSchemaInfo basicSchema) .query("123"); } - private static SearchQueryBuilder basicB(final BenchmarkSchemaInfo basicSchema) + private static SearchQueryBuilder basicB(final GeneratorSchemaInfo basicSchema) { final QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -230,7 +230,7 @@ private static SearchQueryBuilder basicB(final BenchmarkSchemaInfo basicSchema) .filters(new AndDimFilter(dimFilters)); } - private static SearchQueryBuilder basicC(final BenchmarkSchemaInfo basicSchema) + private static SearchQueryBuilder basicC(final GeneratorSchemaInfo basicSchema) { final QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -284,7 +284,7 @@ public ExtractionType getExtractionType() .filters(new AndDimFilter(dimFilters)); } - private static SearchQueryBuilder basicD(final BenchmarkSchemaInfo basicSchema) + private static SearchQueryBuilder basicD(final GeneratorSchemaInfo basicSchema) { final QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec( Collections.singletonList(basicSchema.getDataInterval()) @@ -327,7 +327,7 @@ public void setup() throws IOException String schemaName = schemaQuery[0]; String queryName = schemaQuery[1]; - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schemaName); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schemaName); queryBuilder = SCHEMA_QUERY_MAP.get(schemaName).get(queryName); queryBuilder.limit(limit); query = queryBuilder.build(); @@ -335,7 +335,7 @@ public void setup() throws IOException incIndexes = new ArrayList<>(); for (int i = 0; i < numSegments; i++) { log.info("Generating rows for segment " + i); - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), System.currentTimeMillis(), schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlBenchmark.java index 6c2de09cce55..bd63a1a20e9d 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlBenchmark.java @@ -22,8 +22,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.apache.calcite.schema.SchemaPlus; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.benchmark.datagen.SegmentGenerator; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.granularity.Granularities; @@ -32,6 +30,8 @@ import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.query.QueryRunnerFactoryConglomerate; import org.apache.druid.segment.QueryableIndex; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.server.QueryStackTests; import org.apache.druid.server.security.AuthTestUtils; import org.apache.druid.server.security.AuthenticationResult; @@ -385,7 +385,7 @@ public class SqlBenchmark @Setup(Level.Trial) public void setup() { - final BenchmarkSchemaInfo schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + final GeneratorSchemaInfo schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); final DataSegment dataSegment = DataSegment.builder() .dataSource("foo") diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlVsNativeBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlVsNativeBenchmark.java index dc75b6d47d1f..2e89c4dda48e 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlVsNativeBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/query/SqlVsNativeBenchmark.java @@ -21,8 +21,6 @@ import com.google.common.collect.ImmutableList; import org.apache.calcite.schema.SchemaPlus; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.benchmark.datagen.SegmentGenerator; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.Intervals; @@ -38,6 +36,8 @@ import org.apache.druid.query.groupby.GroupByQuery; import org.apache.druid.query.groupby.ResultRow; import org.apache.druid.segment.QueryableIndex; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.server.QueryStackTests; import org.apache.druid.server.security.AuthTestUtils; import org.apache.druid.server.security.AuthenticationResult; @@ -96,7 +96,7 @@ public void setup() { this.closer = Closer.create(); - final BenchmarkSchemaInfo schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + final GeneratorSchemaInfo schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); final DataSegment dataSegment = DataSegment.builder() .dataSource("foo") diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/query/TimeseriesBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/query/TimeseriesBenchmark.java index bf72eea3774e..931af6cf5f7b 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/query/TimeseriesBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/query/TimeseriesBenchmark.java @@ -20,9 +20,6 @@ package org.apache.druid.benchmark.query; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.common.config.NullHandling; import org.apache.druid.data.input.InputRow; import org.apache.druid.jackson.DefaultObjectMapper; @@ -68,6 +65,9 @@ import org.apache.druid.segment.QueryableIndexSegment; import org.apache.druid.segment.column.ColumnConfig; import org.apache.druid.segment.column.ColumnHolder; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.serde.ComplexMetrics; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; @@ -126,7 +126,7 @@ public class TimeseriesBenchmark private File tmpDir; private QueryRunnerFactory factory; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; private TimeseriesQuery query; private ExecutorService executorService; @@ -153,7 +153,7 @@ private void setupQueries() { // queries for the basic schema Map basicQueries = new LinkedHashMap<>(); - BenchmarkSchemaInfo basicSchema = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + GeneratorSchemaInfo basicSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); { // basic.A QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -253,13 +253,13 @@ public void setup() throws IOException String schemaName = schemaQuery[0]; String queryName = schemaQuery[1]; - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schemaName); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schemaName); query = SCHEMA_QUERY_MAP.get(schemaName).get(queryName); incIndexes = new ArrayList<>(); for (int i = 0; i < numSegments; i++) { log.info("Generating rows for segment " + i); - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED + i, schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/query/TopNBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/query/TopNBenchmark.java index c9ccfd7828e2..bbafeff1507f 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/query/TopNBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/query/TopNBenchmark.java @@ -20,9 +20,6 @@ package org.apache.druid.benchmark.query; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.collections.StupidPool; import org.apache.druid.common.config.NullHandling; import org.apache.druid.data.input.InputRow; @@ -65,6 +62,9 @@ import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.QueryableIndexSegment; import org.apache.druid.segment.column.ColumnConfig; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.serde.ComplexMetrics; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; @@ -125,7 +125,7 @@ public class TopNBenchmark private List qIndexes; private QueryRunnerFactory factory; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; private TopNQueryBuilder queryBuilder; private TopNQuery query; private File tmpDir; @@ -154,7 +154,7 @@ private void setupQueries() { // queries for the basic schema Map basicQueries = new LinkedHashMap<>(); - BenchmarkSchemaInfo basicSchema = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + GeneratorSchemaInfo basicSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); { // basic.A QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -228,7 +228,7 @@ public void setup() throws IOException String schemaName = schemaQuery[0]; String queryName = schemaQuery[1]; - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schemaName); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schemaName); queryBuilder = SCHEMA_QUERY_MAP.get(schemaName).get(queryName); queryBuilder.threshold(threshold); query = queryBuilder.build(); @@ -237,7 +237,7 @@ public void setup() throws IOException for (int i = 0; i < numSegments; i++) { log.info("Generating rows for segment " + i); - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED + i, schemaInfo.getDataInterval(), diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/query/timecompare/TimeCompareBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/query/timecompare/TimeCompareBenchmark.java index f87773d36f1e..3272ecd2623d 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/query/timecompare/TimeCompareBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/query/timecompare/TimeCompareBenchmark.java @@ -21,9 +21,6 @@ import com.fasterxml.jackson.databind.InjectableValues; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; -import org.apache.druid.benchmark.datagen.BenchmarkSchemaInfo; -import org.apache.druid.benchmark.datagen.BenchmarkSchemas; import org.apache.druid.benchmark.query.QueryBenchmarkUtil; import org.apache.druid.collections.StupidPool; import org.apache.druid.common.config.NullHandling; @@ -72,6 +69,9 @@ import org.apache.druid.segment.QueryableIndexSegment; import org.apache.druid.segment.column.ColumnConfig; import org.apache.druid.segment.column.ColumnHolder; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorSchemaInfo; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.segment.serde.ComplexMetrics; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; @@ -143,7 +143,7 @@ public class TimeCompareBenchmark private Query timeseriesQuery; private QueryRunner timeseriesRunner; - private BenchmarkSchemaInfo schemaInfo; + private GeneratorSchemaInfo schemaInfo; private File tmpDir; private Interval[] segmentIntervals; @@ -172,7 +172,7 @@ public int columnCacheSizeBytes() private void setupQueries() { // queries for the basic schema - BenchmarkSchemaInfo basicSchema = BenchmarkSchemas.SCHEMA_MAP.get("basic"); + GeneratorSchemaInfo basicSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); QuerySegmentSpec intervalSpec = new MultipleIntervalSegmentSpec(Collections.singletonList(basicSchema.getDataInterval())); @@ -291,7 +291,7 @@ public void setup() throws IOException setupQueries(); String schemaName = "basic"; - schemaInfo = BenchmarkSchemas.SCHEMA_MAP.get(schemaName); + schemaInfo = GeneratorBasicSchemas.SCHEMA_MAP.get(schemaName); segmentIntervals = new Interval[numSegments]; long startMillis = schemaInfo.getDataInterval().getStartMillis(); @@ -308,7 +308,7 @@ public void setup() throws IOException for (int i = 0; i < numSegments; i++) { log.info("Generating rows for segment " + i); - BenchmarkDataGenerator gen = new BenchmarkDataGenerator( + DataGenerator gen = new DataGenerator( schemaInfo.getColumnSchemas(), RNG_SEED + i, segmentIntervals[i], diff --git a/indexing-service/src/main/java/org/apache/druid/guice/IndexingServiceInputSourceModule.java b/indexing-service/src/main/java/org/apache/druid/guice/IndexingServiceInputSourceModule.java index 56159e1c1000..6c37d97943b1 100644 --- a/indexing-service/src/main/java/org/apache/druid/guice/IndexingServiceInputSourceModule.java +++ b/indexing-service/src/main/java/org/apache/druid/guice/IndexingServiceInputSourceModule.java @@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableList; import com.google.inject.Binder; import org.apache.druid.indexing.input.DruidInputSource; +import org.apache.druid.indexing.input.GeneratorInputSource; import org.apache.druid.initialization.DruidModule; import java.util.List; @@ -37,7 +38,8 @@ public List getJacksonModules() return ImmutableList.of( new SimpleModule("IndexingServiceInputSourceModule") .registerSubtypes( - new NamedType(DruidInputSource.class, "druid") + new NamedType(DruidInputSource.class, "druid"), + new NamedType(GeneratorInputSource.class, "generator") ) ); } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/input/GeneratorInputSource.java b/indexing-service/src/main/java/org/apache/druid/indexing/input/GeneratorInputSource.java new file mode 100644 index 000000000000..208666d15b93 --- /dev/null +++ b/indexing-service/src/main/java/org/apache/druid/indexing/input/GeneratorInputSource.java @@ -0,0 +1,292 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.indexing.input; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Preconditions; +import org.apache.druid.data.input.AbstractInputSource; +import org.apache.druid.data.input.InputFormat; +import org.apache.druid.data.input.InputRow; +import org.apache.druid.data.input.InputRowListPlusRawValues; +import org.apache.druid.data.input.InputRowSchema; +import org.apache.druid.data.input.InputSource; +import org.apache.druid.data.input.InputSourceReader; +import org.apache.druid.data.input.InputSplit; +import org.apache.druid.data.input.MapBasedInputRow; +import org.apache.druid.data.input.SplitHintSpec; +import org.apache.druid.data.input.impl.SplittableInputSource; +import org.apache.druid.java.util.common.CloseableIterators; +import org.apache.druid.java.util.common.DateTimes; +import org.apache.druid.java.util.common.parsers.CloseableIterator; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorColumnSchema; + +import javax.annotation.Nullable; +import java.io.File; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Random; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +/** + * {@link InputSource} that can be used to seed a Druid cluster with test data, using either the built-in schemas + * defined in {@link GeneratorBasicSchemas}, or by directly supplying a list of {@link GeneratorColumnSchema}, to + * construct a {@link DataGenerator}. To produce a stable set of data, a random {@link #seed} may be supplied which + * will be used for all data generated by the columns. When {@link #numSplits} is greater than 1, the {@link #seed} + * will be instead used to pick a new seed for each split, allowing the splits to produce a different set of data, + * but still in a stable manner. + */ +public class GeneratorInputSource extends AbstractInputSource implements SplittableInputSource +{ + private static final int DEFAULT_NUM_ROWS = 1000; + private static final int DEFAULT_NUM_SPLITS = 1; + private static final long DEFAULT_SEED = 1024L; + private static final long DEFAULT_START_TIME = DateTimes.nowUtc().minusDays(1).getMillis(); + private static final int DEFAULT_CONSECUTIVE_TIMESTAMPS = 100; + private static final double DEFAULT_TIMESTAMP_INCREMENT = 1.0; + + private final String schemaName; + private final List schema; + private final int numRows; + private final Integer numSplits; + private final Long seed; + private final Long startTime; + private final Integer numConsecutiveTimestamps; + private final Double timestampIncrement; + + @JsonCreator + public GeneratorInputSource( + @JsonProperty("schemaName") @Nullable String schemaName, + @JsonProperty("schema") @Nullable List schema, + @JsonProperty("numRows") Integer numRows, + @JsonProperty("numSplits") Integer numSplits, + @JsonProperty("seed") Long seed, + @JsonProperty("startTime") Long startTime, + @JsonProperty("numConsecutiveTimestamps") Integer numConsecutiveTimestamps, + @JsonProperty("timestampIncrement") Double timestampIncrement + ) + { + Preconditions.checkArgument( + schemaName != null || schema != null, + "Must specify either 'schemaName' or 'schema'" + ); + this.schemaName = schemaName; + this.schema = schema != null + ? schema + : GeneratorBasicSchemas.SCHEMA_MAP.get(schemaName).getColumnSchemas(); + this.numRows = numRows != null ? numRows : DEFAULT_NUM_ROWS; + this.numSplits = numSplits != null ? numSplits : DEFAULT_NUM_SPLITS; + this.seed = seed != null ? seed : DEFAULT_SEED; + this.startTime = startTime != null ? startTime : DEFAULT_START_TIME; + this.numConsecutiveTimestamps = numConsecutiveTimestamps != null + ? numConsecutiveTimestamps + : DEFAULT_CONSECUTIVE_TIMESTAMPS; + this.timestampIncrement = timestampIncrement != null ? timestampIncrement : DEFAULT_TIMESTAMP_INCREMENT; + } + + @Override + public Stream> createSplits( + InputFormat inputFormat, + @Nullable SplitHintSpec splitHintSpec + ) + { + Random r = new Random(seed); + return LongStream.range(0, numSplits).mapToObj(i -> new InputSplit<>(r.nextLong())); + } + + @Override + public int estimateNumSplits(InputFormat inputFormat, @Nullable SplitHintSpec splitHintSpec) + { + return numSplits; + } + + @Override + public InputSource withSplit(InputSplit split) + { + return new GeneratorInputSource( + schemaName, + schema, + numRows, + 1, + split.get(), + startTime, + numConsecutiveTimestamps, + timestampIncrement + ); + } + + @Override + public boolean needsFormat() + { + return false; + } + + @Override + protected InputSourceReader fixedFormatReader(InputRowSchema inputRowSchema, @Nullable File temporaryDirectory) + { + return new InputSourceReader() + { + @Override + public CloseableIterator read() + { + return CloseableIterators.withEmptyBaggage(new Iterator() + { + int rowCount = 0; + private final DataGenerator generator = makeGenerator(); + + @Override + public boolean hasNext() + { + return rowCount < numRows; + } + + @Override + public InputRow next() + { + rowCount++; + return generator.nextRow(); + } + }); + } + + @Override + public CloseableIterator sample() + { + return CloseableIterators.withEmptyBaggage(new Iterator() + { + int rowCount = 0; + private final DataGenerator generator = makeGenerator(); + + @Override + public boolean hasNext() + { + return rowCount < numRows; + } + + @Override + public InputRowListPlusRawValues next() + { + rowCount++; + InputRow row = generator.nextRow(); + return InputRowListPlusRawValues.of(row, ((MapBasedInputRow) row).getEvent()); + } + }); + } + }; + } + + @JsonProperty + public String getSchemaName() + { + return schemaName; + } + + @JsonProperty + public List getSchema() + { + return schemaName == null ? schema : null; + } + + @JsonProperty + public int getNumRows() + { + return numRows; + } + + @JsonProperty + public Integer getNumSplits() + { + return numSplits; + } + + @JsonProperty + public Long getSeed() + { + return seed; + } + + @JsonProperty + public Long getStartTime() + { + return startTime; + } + + @JsonProperty + public Integer getNumConsecutiveTimestamps() + { + return numConsecutiveTimestamps; + } + + @JsonProperty + public Double getTimestampIncrement() + { + return timestampIncrement; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GeneratorInputSource that = (GeneratorInputSource) o; + return numRows == that.numRows && + Objects.equals(schemaName, that.schemaName) && + Objects.equals(schema, that.schema) && + Objects.equals(numSplits, that.numSplits) && + Objects.equals(seed, that.seed) && + Objects.equals(startTime, that.startTime) && + Objects.equals(numConsecutiveTimestamps, that.numConsecutiveTimestamps) && + Objects.equals(timestampIncrement, that.timestampIncrement); + } + + @Override + public int hashCode() + { + return Objects.hash( + schemaName, + schema, + numRows, + numSplits, + seed, + startTime, + numConsecutiveTimestamps, + timestampIncrement + ); + } + + private DataGenerator makeGenerator() + { + return new DataGenerator( + schema, + seed, + startTime, + numConsecutiveTimestamps, + timestampIncrement + ); + } +} diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/input/GeneratorInputSourceTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/input/GeneratorInputSourceTest.java new file mode 100644 index 000000000000..9b6ae0d3e36f --- /dev/null +++ b/indexing-service/src/test/java/org/apache/druid/indexing/input/GeneratorInputSourceTest.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.indexing.input; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.apache.druid.data.input.InputRow; +import org.apache.druid.data.input.InputSourceReader; +import org.apache.druid.data.input.InputSplit; +import org.apache.druid.jackson.DefaultObjectMapper; +import org.apache.druid.java.util.common.DateTimes; +import org.apache.druid.java.util.common.parsers.CloseableIterator; +import org.apache.druid.segment.column.ValueType; +import org.apache.druid.segment.generator.DataGenerator; +import org.apache.druid.segment.generator.GeneratorBasicSchemas; +import org.apache.druid.segment.generator.GeneratorColumnSchema; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; + +public class GeneratorInputSourceTest +{ + private static final ObjectMapper MAPPER = new DefaultObjectMapper(); + + @Test + public void testSerde() throws JsonProcessingException + { + GeneratorInputSource inputSource = new GeneratorInputSource( + "basic", + null, + 1000, + 2, + 1024L, + DateTimes.nowUtc().getMillis(), + 1000, + 1.0 + ); + + String serialized = MAPPER.writeValueAsString(inputSource); + GeneratorInputSource sauce = MAPPER.readValue(serialized, GeneratorInputSource.class); + + Assert.assertEquals(inputSource, sauce); + } + + @Test + public void testSerdeWithSchema() throws JsonProcessingException + { + GeneratorInputSource inputSource = new GeneratorInputSource( + null, + ImmutableList.of( + GeneratorColumnSchema.makeLazyZipf( + "test", + ValueType.LONG, + false, + 1, + 0.0, + 0, + 1000, + 1.3 + ) + ), + 1000, + 2, + 1024L, + DateTimes.nowUtc().getMillis(), + 1000, + 1.0 + ); + + String serialized = MAPPER.writeValueAsString(inputSource); + GeneratorInputSource sauce = MAPPER.readValue(serialized, GeneratorInputSource.class); + + Assert.assertEquals(inputSource, sauce); + } + + @Test + public void testEquals() + { + EqualsVerifier.forClass(GeneratorInputSource.class).usingGetClass().verify(); + } + + @Test + public void testReader() throws IOException + { + final long seed = 1024L; + final long millis = DateTimes.nowUtc().getMillis(); + final int numConsecutiveTimestamps = 1000; + final double timestampIncrement = 1.0; + final int numRows = 1000; + GeneratorInputSource inputSource = new GeneratorInputSource( + "basic", + null, + numRows, + 2, + seed, + millis, + numConsecutiveTimestamps, + timestampIncrement + ); + + DataGenerator generator = new DataGenerator( + GeneratorBasicSchemas.SCHEMA_MAP.get("basic").getColumnSchemas(), + seed, + millis, + numConsecutiveTimestamps, + timestampIncrement + ); + + InputSourceReader reader = inputSource.fixedFormatReader(null, null); + CloseableIterator iterator = reader.read(); + + InputRow first = iterator.next(); + InputRow generatorFirst = generator.nextRow(); + Assert.assertEquals(generatorFirst, first); + Assert.assertTrue(iterator.hasNext()); + int i; + for (i = 1; iterator.hasNext(); i++) { + iterator.next(); + } + Assert.assertEquals(numRows, i); + } + + @Test + public void testSplits() + { + GeneratorInputSource inputSource = new GeneratorInputSource( + "basic", + null, + 1000, + 2, + 1024L, + DateTimes.nowUtc().getMillis(), + 1000, + 1.0 + ); + + Assert.assertEquals(2, inputSource.estimateNumSplits(null, null)); + Assert.assertEquals(false, inputSource.needsFormat()); + Assert.assertEquals(2, inputSource.createSplits(null, null).count()); + Assert.assertEquals( + new Long(2048L), + ((GeneratorInputSource) inputSource.withSplit(new InputSplit<>(2048L))).getSeed() + ); + } +} diff --git a/processing/pom.xml b/processing/pom.xml index f0af5c30fda7..5355bf1a16a7 100644 --- a/processing/pom.xml +++ b/processing/pom.xml @@ -93,7 +93,7 @@ org.apache.commons - commons-lang3 + commons-math3 commons-net diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkColumnValueGenerator.java b/processing/src/main/java/org/apache/druid/segment/generator/ColumnValueGenerator.java similarity index 90% rename from benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkColumnValueGenerator.java rename to processing/src/main/java/org/apache/druid/segment/generator/ColumnValueGenerator.java index 5fe89d8a9fb5..fbfc1a9f3c15 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkColumnValueGenerator.java +++ b/processing/src/main/java/org/apache/druid/segment/generator/ColumnValueGenerator.java @@ -17,12 +17,13 @@ * under the License. */ -package org.apache.druid.benchmark.datagen; +package org.apache.druid.segment.generator; import org.apache.commons.math3.distribution.AbstractIntegerDistribution; import org.apache.commons.math3.distribution.AbstractRealDistribution; import org.apache.commons.math3.distribution.EnumeratedDistribution; import org.apache.commons.math3.distribution.NormalDistribution; +import org.apache.commons.math3.distribution.UniformIntegerDistribution; import org.apache.commons.math3.distribution.UniformRealDistribution; import org.apache.commons.math3.distribution.ZipfDistribution; import org.apache.commons.math3.util.Pair; @@ -33,16 +34,16 @@ import java.util.List; import java.util.Random; -public class BenchmarkColumnValueGenerator +public class ColumnValueGenerator { - private final BenchmarkColumnSchema schema; + private final GeneratorColumnSchema schema; private final long seed; private Serializable distribution; private Random simpleRng; - public BenchmarkColumnValueGenerator( - BenchmarkColumnSchema schema, + public ColumnValueGenerator( + GeneratorColumnSchema schema, long seed ) { @@ -76,7 +77,7 @@ public Object generateRowValue() } } - public BenchmarkColumnSchema getSchema() + public GeneratorColumnSchema getSchema() { return schema; } @@ -138,7 +139,7 @@ private Object convertType(Object input, ValueType type) private void initDistribution() { - BenchmarkColumnSchema.ValueDistribution distributionType = schema.getDistributionType(); + GeneratorColumnSchema.ValueDistribution distributionType = schema.getDistributionType(); ValueType type = schema.getType(); List enumeratedValues = schema.getEnumeratedValues(); List enumeratedProbabilities = schema.getEnumeratedProbabilities(); @@ -195,6 +196,15 @@ private void initDistribution() } distribution = new EnumeratedTreeDistribution<>(probabilities); break; + case LAZY_ZIPF: + int lazyCardinality; + Integer startInt = schema.getStartInt(); + lazyCardinality = schema.getEndInt() - startInt; + distribution = new ZipfDistribution(lazyCardinality, schema.getZipfExponent()); + break; + case LAZY_DISCRETE_UNIFORM: + distribution = new UniformIntegerDistribution(schema.getStartInt(), schema.getEndInt()); + break; case ENUMERATED: for (int i = 0; i < enumeratedValues.size(); i++) { probabilities.add(new Pair<>(enumeratedValues.get(i), enumeratedProbabilities.get(i))); diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkDataGenerator.java b/processing/src/main/java/org/apache/druid/segment/generator/DataGenerator.java similarity index 83% rename from benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkDataGenerator.java rename to processing/src/main/java/org/apache/druid/segment/generator/DataGenerator.java index 49d01a4c12c5..f2fac21d536e 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkDataGenerator.java +++ b/processing/src/main/java/org/apache/druid/segment/generator/DataGenerator.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.druid.benchmark.datagen; +package org.apache.druid.segment.generator; import com.google.common.base.Function; import com.google.common.base.Preconditions; @@ -31,12 +31,12 @@ import java.util.List; import java.util.Map; -public class BenchmarkDataGenerator +public class DataGenerator { - private final List columnSchemas; + private final List columnSchemas; private final long seed; - private List columnGenerators; + private List columnGenerators; private final long startTime; private final long endTime; private final int numConsecutiveTimestamps; @@ -46,8 +46,8 @@ public class BenchmarkDataGenerator private int timeCounter; private List dimensionNames; - public BenchmarkDataGenerator( - List columnSchemas, + public DataGenerator( + List columnSchemas, final long seed, long startTime, int numConsecutiveTimestamps, @@ -66,8 +66,8 @@ public BenchmarkDataGenerator( init(); } - public BenchmarkDataGenerator( - List columnSchemas, + public DataGenerator( + List columnSchemas, final long seed, Interval interval, int numRows @@ -91,7 +91,7 @@ public BenchmarkDataGenerator( public InputRow nextRow() { Map event = new HashMap<>(); - for (BenchmarkColumnValueGenerator generator : columnGenerators) { + for (ColumnValueGenerator generator : columnGenerators) { event.put(generator.getSchema().getName(), generator.generateRowValue()); } MapBasedInputRow row = new MapBasedInputRow(nextTimestamp(), dimensionNames, event); @@ -104,7 +104,7 @@ private void init() this.currentTime = startTime; dimensionNames = new ArrayList<>(); - for (BenchmarkColumnSchema schema : columnSchemas) { + for (GeneratorColumnSchema schema : columnSchemas) { if (!schema.isMetric()) { dimensionNames.add(schema.getName()); } @@ -114,11 +114,11 @@ private void init() columnGenerators.addAll( Lists.transform( columnSchemas, - new Function() + new Function() { @Override - public BenchmarkColumnValueGenerator apply( - BenchmarkColumnSchema input + public ColumnValueGenerator apply( + GeneratorColumnSchema input ) { return input.makeGenerator(seed); diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/EnumeratedTreeDistribution.java b/processing/src/main/java/org/apache/druid/segment/generator/EnumeratedTreeDistribution.java similarity index 95% rename from benchmarks/src/test/java/org/apache/druid/benchmark/datagen/EnumeratedTreeDistribution.java rename to processing/src/main/java/org/apache/druid/segment/generator/EnumeratedTreeDistribution.java index 1b44e5c3216e..057af0775cfc 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/EnumeratedTreeDistribution.java +++ b/processing/src/main/java/org/apache/druid/segment/generator/EnumeratedTreeDistribution.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.druid.benchmark.datagen; +package org.apache.druid.segment.generator; import org.apache.commons.math3.distribution.EnumeratedDistribution; import org.apache.commons.math3.util.Pair; @@ -41,7 +41,7 @@ public EnumeratedTreeDistribution(final List> pmf) super(pmf); // build the interval tree - probabilityRanges = new TreeMap(); + probabilityRanges = new TreeMap<>(); normalizedPmf = this.getPmf(); double cumulativep = 0.0; for (int i = 0; i < normalizedPmf.size(); i++) { diff --git a/processing/src/main/java/org/apache/druid/segment/generator/GeneratorBasicSchemas.java b/processing/src/main/java/org/apache/druid/segment/generator/GeneratorBasicSchemas.java new file mode 100644 index 000000000000..a7683963e68c --- /dev/null +++ b/processing/src/main/java/org/apache/druid/segment/generator/GeneratorBasicSchemas.java @@ -0,0 +1,420 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.generator; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.druid.java.util.common.Intervals; +import org.apache.druid.math.expr.ExprMacroTable; +import org.apache.druid.query.aggregation.AggregatorFactory; +import org.apache.druid.query.aggregation.CountAggregatorFactory; +import org.apache.druid.query.aggregation.DoubleMinAggregatorFactory; +import org.apache.druid.query.aggregation.DoubleSumAggregatorFactory; +import org.apache.druid.query.aggregation.LongMaxAggregatorFactory; +import org.apache.druid.query.aggregation.LongSumAggregatorFactory; +import org.apache.druid.query.aggregation.hyperloglog.HyperUniquesAggregatorFactory; +import org.apache.druid.segment.column.ValueType; +import org.joda.time.Interval; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class GeneratorBasicSchemas +{ + private static final ImmutableMap.Builder SCHEMA_INFO_BUILDER = ImmutableMap.builder(); + + static { + // basic schema + List basicSchemaColumns = ImmutableList.of( + // dims + GeneratorColumnSchema.makeSequential("dimSequential", ValueType.STRING, false, 1, null, 0, 1000), + GeneratorColumnSchema.makeZipf("dimZipf", ValueType.STRING, false, 1, null, 1, 101, 1.0), + GeneratorColumnSchema.makeDiscreteUniform("dimUniform", ValueType.STRING, false, 1, null, 1, 100000), + GeneratorColumnSchema.makeSequential("dimSequentialHalfNull", ValueType.STRING, false, 1, 0.5, 0, 1000), + GeneratorColumnSchema.makeEnumerated( + "dimMultivalEnumerated", + ValueType.STRING, + false, + 4, + null, + Arrays.asList("Hello", "World", "Foo", "Bar", "Baz"), + Arrays.asList(0.2, 0.25, 0.15, 0.10, 0.3) + ), + GeneratorColumnSchema.makeEnumerated( + "dimMultivalEnumerated2", + ValueType.STRING, + false, + 3, + null, + Arrays.asList("Apple", "Orange", "Xylophone", "Corundum", null), + Arrays.asList(0.2, 0.25, 0.15, 0.10, 0.3) + ), + GeneratorColumnSchema.makeSequential("dimMultivalSequentialWithNulls", ValueType.STRING, false, 8, 0.15, 1, 11), + GeneratorColumnSchema.makeSequential("dimHyperUnique", ValueType.STRING, false, 1, null, 0, 100000), + GeneratorColumnSchema.makeSequential("dimNull", ValueType.STRING, false, 1, 1.0, 0, 1), + + // metrics + GeneratorColumnSchema.makeSequential("metLongSequential", ValueType.LONG, true, 1, null, 0, 10000), + GeneratorColumnSchema.makeDiscreteUniform("metLongUniform", ValueType.LONG, true, 1, null, 0, 500), + GeneratorColumnSchema.makeNormal("metFloatNormal", ValueType.FLOAT, true, 1, null, 5000.0, 1.0, true), + GeneratorColumnSchema.makeZipf("metFloatZipf", ValueType.FLOAT, true, 1, null, 0, 1000, 1.0) + ); + + List basicSchemaIngestAggs = new ArrayList<>(); + basicSchemaIngestAggs.add(new CountAggregatorFactory("rows")); + basicSchemaIngestAggs.add(new LongSumAggregatorFactory("sumLongSequential", "metLongSequential")); + basicSchemaIngestAggs.add(new LongMaxAggregatorFactory("maxLongUniform", "metLongUniform")); + basicSchemaIngestAggs.add(new DoubleSumAggregatorFactory("sumFloatNormal", "metFloatNormal")); + basicSchemaIngestAggs.add(new DoubleMinAggregatorFactory("minFloatZipf", "metFloatZipf")); + basicSchemaIngestAggs.add(new HyperUniquesAggregatorFactory("hyper", "dimHyperUnique")); + + List basicSchemaIngestAggsExpression = new ArrayList<>(); + basicSchemaIngestAggsExpression.add(new CountAggregatorFactory("rows")); + basicSchemaIngestAggsExpression.add(new LongSumAggregatorFactory( + "sumLongSequential", + null, + "if(sumLongSequential>0 && dimSequential>100 || dimSequential<10 || metLongSequential>3000,sumLongSequential,0)", + ExprMacroTable.nil() + )); + basicSchemaIngestAggsExpression.add(new LongMaxAggregatorFactory("maxLongUniform", "metLongUniform")); + basicSchemaIngestAggsExpression.add(new DoubleSumAggregatorFactory( + "sumFloatNormal", + null, + "if(sumFloatNormal>0 && dimSequential>100 || dimSequential<10 || metLongSequential>3000,sumFloatNormal,0)", + ExprMacroTable.nil() + )); + basicSchemaIngestAggsExpression.add(new DoubleMinAggregatorFactory("minFloatZipf", "metFloatZipf")); + basicSchemaIngestAggsExpression.add(new HyperUniquesAggregatorFactory("hyper", "dimHyperUnique")); + + Interval basicSchemaDataInterval = Intervals.of("2000-01-01/P1D"); + + GeneratorSchemaInfo basicSchema = new GeneratorSchemaInfo( + basicSchemaColumns, + basicSchemaIngestAggs, + basicSchemaDataInterval, + true + ); + + GeneratorSchemaInfo basicSchemaExpression = new GeneratorSchemaInfo( + basicSchemaColumns, + basicSchemaIngestAggsExpression, + basicSchemaDataInterval, + true + ); + + SCHEMA_INFO_BUILDER.put("basic", basicSchema); + SCHEMA_INFO_BUILDER.put("expression", basicSchemaExpression); + } + + static { // simple single string column and count agg schema, no rollup + List basicSchemaColumns = ImmutableList.of( + // dims + GeneratorColumnSchema.makeSequential("dimSequential", ValueType.STRING, false, 1, null, 0, 1000000) + ); + + List basicSchemaIngestAggs = new ArrayList<>(); + basicSchemaIngestAggs.add(new CountAggregatorFactory("rows")); + + Interval basicSchemaDataInterval = Intervals.utc(0, 1000000); + + GeneratorSchemaInfo basicSchema = new GeneratorSchemaInfo( + basicSchemaColumns, + basicSchemaIngestAggs, + basicSchemaDataInterval, + false + ); + SCHEMA_INFO_BUILDER.put("simple", basicSchema); + } + + static { + // simple single long column and count agg schema, no rollup + List basicSchemaColumns = ImmutableList.of( + // dims, ingest as a metric for now with rollup off, until numeric dims at ingestion are supported + GeneratorColumnSchema.makeSequential("dimSequential", ValueType.LONG, true, 1, null, 0, 1000000) + ); + + List basicSchemaIngestAggs = new ArrayList<>(); + basicSchemaIngestAggs.add(new LongSumAggregatorFactory("dimSequential", "dimSequential")); + basicSchemaIngestAggs.add(new CountAggregatorFactory("rows")); + + Interval basicSchemaDataInterval = Intervals.utc(0, 1000000); + + GeneratorSchemaInfo basicSchema = new GeneratorSchemaInfo( + basicSchemaColumns, + basicSchemaIngestAggs, + basicSchemaDataInterval, + false + ); + SCHEMA_INFO_BUILDER.put("simpleLong", basicSchema); + } + + static { + // simple single float column and count agg schema, no rollup + List basicSchemaColumns = ImmutableList.of( + // dims, ingest as a metric for now with rollup off, until numeric dims at ingestion are supported + GeneratorColumnSchema.makeSequential("dimSequential", ValueType.FLOAT, true, 1, null, 0, 1000000) + ); + + List basicSchemaIngestAggs = new ArrayList<>(); + basicSchemaIngestAggs.add(new DoubleSumAggregatorFactory("dimSequential", "dimSequential")); + basicSchemaIngestAggs.add(new CountAggregatorFactory("rows")); + + Interval basicSchemaDataInterval = Intervals.utc(0, 1000000); + + GeneratorSchemaInfo basicSchema = new GeneratorSchemaInfo( + basicSchemaColumns, + basicSchemaIngestAggs, + basicSchemaDataInterval, + false + ); + SCHEMA_INFO_BUILDER.put("simpleFloat", basicSchema); + } + + static { + // schema with high opportunity for rollup + List rolloColumns = ImmutableList.of( + // dims + GeneratorColumnSchema.makeEnumerated( + "dimEnumerated", + ValueType.STRING, + false, + 1, + null, + Arrays.asList("Hello", "World", "Foo", "Bar", "Baz"), + Arrays.asList(0.2, 0.25, 0.15, 0.10, 0.3) + ), + GeneratorColumnSchema.makeEnumerated( + "dimEnumerated2", + ValueType.STRING, + false, + 1, + null, + Arrays.asList("Apple", "Orange", "Xylophone", "Corundum", null), + Arrays.asList(0.2, 0.25, 0.15, 0.10, 0.3) + ), + GeneratorColumnSchema.makeZipf("dimZipf", ValueType.STRING, false, 1, null, 1, 100, 2.0), + GeneratorColumnSchema.makeDiscreteUniform("dimUniform", ValueType.STRING, false, 1, null, 1, 100), + + // metrics + GeneratorColumnSchema.makeZipf("metLongZipf", ValueType.LONG, true, 1, null, 0, 10000, 2.0), + GeneratorColumnSchema.makeDiscreteUniform("metLongUniform", ValueType.LONG, true, 1, null, 0, 500), + GeneratorColumnSchema.makeNormal("metFloatNormal", ValueType.FLOAT, true, 1, null, 5000.0, 1.0, true), + GeneratorColumnSchema.makeZipf("metFloatZipf", ValueType.FLOAT, true, 1, null, 0, 1000, 1.5) + ); + + List rolloSchemaIngestAggs = new ArrayList<>(); + rolloSchemaIngestAggs.add(new CountAggregatorFactory("rows")); + rolloSchemaIngestAggs.add(new LongSumAggregatorFactory("sumLongSequential", "metLongSequential")); + rolloSchemaIngestAggs.add(new LongMaxAggregatorFactory("maxLongUniform", "metLongUniform")); + rolloSchemaIngestAggs.add(new DoubleSumAggregatorFactory("sumFloatNormal", "metFloatNormal")); + rolloSchemaIngestAggs.add(new DoubleMinAggregatorFactory("minFloatZipf", "metFloatZipf")); + rolloSchemaIngestAggs.add(new HyperUniquesAggregatorFactory("hyper", "dimHyperUnique")); + + Interval basicSchemaDataInterval = Intervals.utc(0, 1000000); + + GeneratorSchemaInfo rolloSchema = new GeneratorSchemaInfo( + rolloColumns, + rolloSchemaIngestAggs, + basicSchemaDataInterval, + true + ); + SCHEMA_INFO_BUILDER.put("rollo", rolloSchema); + } + + static { + // simple schema with null valued rows, no aggs on numeric columns + List nullsSchemaColumns = ImmutableList.of( + // string dims with nulls + GeneratorColumnSchema.makeZipf("stringZipf", ValueType.STRING, false, 1, 0.8, 1, 101, 1.5), + GeneratorColumnSchema.makeDiscreteUniform("stringUniform", ValueType.STRING, false, 1, 0.3, 1, 100000), + GeneratorColumnSchema.makeSequential("stringSequential", ValueType.STRING, false, 1, 0.5, 0, 1000), + + // numeric dims with nulls + GeneratorColumnSchema.makeSequential("longSequential", ValueType.LONG, false, 1, 0.45, 0, 10000), + GeneratorColumnSchema.makeDiscreteUniform("longUniform", ValueType.LONG, false, 1, 0.25, 0, 500), + GeneratorColumnSchema.makeZipf("doubleZipf", ValueType.DOUBLE, false, 1, 0.1, 0, 1000, 2.0), + GeneratorColumnSchema.makeZipf("floatZipf", ValueType.FLOAT, false, 1, 0.1, 0, 1000, 2.0) + ); + + List simpleNullsSchemaIngestAggs = new ArrayList<>(); + simpleNullsSchemaIngestAggs.add(new CountAggregatorFactory("rows")); + + Interval nullsSchemaDataInterval = Intervals.of("2000-01-01/P1D"); + + GeneratorSchemaInfo nullsSchema = new GeneratorSchemaInfo( + nullsSchemaColumns, + simpleNullsSchemaIngestAggs, + nullsSchemaDataInterval, + false + ); + + SCHEMA_INFO_BUILDER.put("nulls", nullsSchema); + } + + static { + // simple schema with null valued rows, no aggs on numeric columns + List nullsSchemaColumns = ImmutableList.of( + // string dims + GeneratorColumnSchema.makeZipf("stringZipf", ValueType.STRING, false, 1, null, 1, 101, 1.5), + GeneratorColumnSchema.makeDiscreteUniform("stringUniform", ValueType.STRING, false, 1, null, 1, 100000), + GeneratorColumnSchema.makeSequential("stringSequential", ValueType.STRING, false, 1, null, 0, 1000), + + // string dims with nulls + GeneratorColumnSchema.makeZipf("stringZipfWithNulls", ValueType.STRING, false, 1, 0.8, 1, 101, 1.5), + GeneratorColumnSchema.makeDiscreteUniform("stringUniformWithNulls", ValueType.STRING, false, 1, 0.3, 1, 100000), + GeneratorColumnSchema.makeSequential("stringSequentialWithNulls", ValueType.STRING, false, 1, 0.5, 0, 1000), + + // numeric dims + GeneratorColumnSchema.makeSequential("longSequential", ValueType.LONG, false, 1, null, 0, 10000), + GeneratorColumnSchema.makeDiscreteUniform("longUniform", ValueType.LONG, false, 1, null, 0, 500), + GeneratorColumnSchema.makeZipf("doubleZipf", ValueType.DOUBLE, false, 1, null, 0, 1000, 2.0), + GeneratorColumnSchema.makeZipf("floatZipf", ValueType.FLOAT, false, 1, null, 0, 1000, 2.0), + + // numeric dims with nulls + GeneratorColumnSchema.makeSequential("longSequentialWithNulls", ValueType.LONG, false, 1, 0.45, 0, 10000), + GeneratorColumnSchema.makeDiscreteUniform("longUniformWithNulls", ValueType.LONG, false, 1, 0.25, 0, 500), + GeneratorColumnSchema.makeZipf("doubleZipfWithNulls", ValueType.DOUBLE, false, 1, 0.1, 0, 1000, 2.0), + GeneratorColumnSchema.makeZipf("floatZipfWithNulls", ValueType.FLOAT, false, 1, 0.1, 0, 1000, 2.0) + ); + + List simpleNullsSchemaIngestAggs = new ArrayList<>(); + simpleNullsSchemaIngestAggs.add(new CountAggregatorFactory("rows")); + + Interval nullsSchemaDataInterval = Intervals.of("2000-01-01/P1D"); + + GeneratorSchemaInfo nullsSchema = new GeneratorSchemaInfo( + nullsSchemaColumns, + simpleNullsSchemaIngestAggs, + nullsSchemaDataInterval, + false + ); + + SCHEMA_INFO_BUILDER.put("nulls-and-non-nulls", nullsSchema); + } + + static { + // simple 'wide' schema with null valued rows, high cardinality columns, no aggs on numeric columns + // essentially 'nulls-and-non-nulls' with a ton of extra zipf columns of each type with a variety of value + // distributions + List nullsSchemaColumns = ImmutableList.of( + // string dims + GeneratorColumnSchema.makeLazyZipf("stringZipf", ValueType.STRING, false, 1, null, 1, 101, 1.5), + GeneratorColumnSchema.makeLazyDiscreteUniform("stringUniform", ValueType.STRING, false, 1, null, 1, 100000), + GeneratorColumnSchema.makeSequential("stringSequential", ValueType.STRING, false, 1, null, 0, 1000), + + // string dims with nulls + GeneratorColumnSchema.makeLazyZipf("stringZipfWithNulls", ValueType.STRING, false, 1, 0.8, 1, 101, 1.5), + GeneratorColumnSchema.makeLazyDiscreteUniform( + "stringUniformWithNulls", + ValueType.STRING, + false, + 1, + 0.3, + 1, + 100000 + ), + GeneratorColumnSchema.makeSequential("stringSequentialWithNulls", ValueType.STRING, false, 1, 0.5, 0, 1000), + + // additional string columns + GeneratorColumnSchema.makeLazyZipf("string1", ValueType.STRING, false, 1, 0.1, 1, 100000, 2.0), + GeneratorColumnSchema.makeLazyZipf("string2", ValueType.STRING, false, 1, 0.3, 1, 1000000, 1.5), + GeneratorColumnSchema.makeLazyZipf("string3", ValueType.STRING, false, 1, 0.12, 1, 1000, 1.25), + GeneratorColumnSchema.makeLazyZipf("string4", ValueType.STRING, false, 1, 0.22, 1, 12000, 3.0), + GeneratorColumnSchema.makeLazyZipf("string5", ValueType.STRING, false, 1, 0.05, 1, 33333, 1.8), + GeneratorColumnSchema.makeLazyZipf("string6", ValueType.STRING, false, 1, 0.09, 1, 25000000, 2.0), + GeneratorColumnSchema.makeLazyZipf("string7", ValueType.STRING, false, 1, 0.8, 1, 100, 1.5), + GeneratorColumnSchema.makeLazyZipf("string8", ValueType.STRING, false, 1, 0.5, 1, 10, 1.2), + GeneratorColumnSchema.makeLazyZipf("string9", ValueType.STRING, false, 1, 0.05, 1, 1000000, 1.3), + GeneratorColumnSchema.makeLazyZipf("string10", ValueType.STRING, false, 1, 0.4, 1, 888888, 1.4), + GeneratorColumnSchema.makeLazyZipf("string11", ValueType.STRING, false, 1, 0.7, 1, 999, 1.8), + GeneratorColumnSchema.makeLazyZipf("string12", ValueType.STRING, false, 1, 0.2, 1, 123456, 1.0), + GeneratorColumnSchema.makeLazyZipf("string13", ValueType.STRING, false, 1, 0.33, 1, 52, 1.9), + GeneratorColumnSchema.makeLazyZipf("string14", ValueType.STRING, false, 1, 0.42, 1, 90001, 1.75), + + // numeric dims + GeneratorColumnSchema.makeSequential("longSequential", ValueType.LONG, false, 1, null, 0, 10000), + GeneratorColumnSchema.makeLazyDiscreteUniform("longUniform", ValueType.LONG, false, 1, null, 0, 500), + + GeneratorColumnSchema.makeLazyZipf("longZipf", ValueType.LONG, false, 1, null, 0, 1000, 2.0), + GeneratorColumnSchema.makeLazyZipf("doubleZipf", ValueType.DOUBLE, false, 1, null, 0, 1000, 2.0), + GeneratorColumnSchema.makeLazyZipf("floatZipf", ValueType.FLOAT, false, 1, null, 0, 1000, 2.0), + + // numeric dims with nulls + GeneratorColumnSchema.makeSequential("longSequentialWithNulls", ValueType.LONG, false, 1, 0.45, 0, 10000), + GeneratorColumnSchema.makeLazyDiscreteUniform("longUniformWithNulls", ValueType.LONG, false, 1, 0.25, 0, 500), + + GeneratorColumnSchema.makeLazyZipf("longZipfWithNulls", ValueType.LONG, false, 1, 0.1, 0, 1000, 2.0), + GeneratorColumnSchema.makeLazyZipf("doubleZipfWithNulls", ValueType.DOUBLE, false, 1, 0.1, 0, 1000, 2.0), + GeneratorColumnSchema.makeLazyZipf("floatZipfWithNulls", ValueType.FLOAT, false, 1, 0.1, 0, 1000, 2.0), + + // additional numeric columns + GeneratorColumnSchema.makeLazyZipf("long1", ValueType.LONG, false, 1, 0.1, 0, 1001, 2.0), + GeneratorColumnSchema.makeLazyZipf("long2", ValueType.LONG, false, 1, 0.01, 0, 666666, 2.2), + GeneratorColumnSchema.makeLazyZipf("long3", ValueType.LONG, false, 1, 0.12, 0, 1000000, 2.5), + GeneratorColumnSchema.makeLazyZipf("long4", ValueType.LONG, false, 1, 0.4, 0, 23, 1.2), + GeneratorColumnSchema.makeLazyZipf("long5", ValueType.LONG, false, 1, 0.33, 0, 9999, 1.5), + GeneratorColumnSchema.makeLazyZipf("long6", ValueType.LONG, false, 1, 0.8, 0, 12345, 1.8), + GeneratorColumnSchema.makeLazyZipf("long7", ValueType.LONG, false, 1, 0.6, 0, 543210, 2.3), + GeneratorColumnSchema.makeLazyZipf("long8", ValueType.LONG, false, 1, 0.5, 0, 11223344, 2.4), + GeneratorColumnSchema.makeLazyZipf("long9", ValueType.LONG, false, 1, 0.111, 0, 80, 4.0), + GeneratorColumnSchema.makeLazyZipf("long10", ValueType.LONG, false, 1, 0.21, 0, 111, 3.3), + + GeneratorColumnSchema.makeLazyZipf("double1", ValueType.DOUBLE, false, 1, 0.1, 0, 333, 2.2), + GeneratorColumnSchema.makeLazyZipf("double2", ValueType.DOUBLE, false, 1, 0.01, 0, 4021, 2.5), + GeneratorColumnSchema.makeLazyZipf("double3", ValueType.DOUBLE, false, 1, 0.41, 0, 90210, 4.0), + GeneratorColumnSchema.makeLazyZipf("double4", ValueType.DOUBLE, false, 1, 0.5, 0, 5555555, 1.2), + GeneratorColumnSchema.makeLazyZipf("double5", ValueType.DOUBLE, false, 1, 0.23, 0, 80, 1.8), + GeneratorColumnSchema.makeLazyZipf("double6", ValueType.DOUBLE, false, 1, 0.33, 0, 99999, 3.0), + GeneratorColumnSchema.makeLazyZipf("double7", ValueType.DOUBLE, false, 1, 0.8, 0, 12345678, 1.4), + GeneratorColumnSchema.makeLazyZipf("double8", ValueType.DOUBLE, false, 1, 0.4, 0, 8080, 4.2), + GeneratorColumnSchema.makeLazyZipf("double9", ValueType.DOUBLE, false, 1, 0.111, 0, 9999, 2.3), + GeneratorColumnSchema.makeLazyZipf("double10", ValueType.DOUBLE, false, 1, 0.2, 0, 19, 0.6), + + GeneratorColumnSchema.makeLazyZipf("float1", ValueType.FLOAT, false, 1, 0.11, 0, 1000000, 1.7), + GeneratorColumnSchema.makeLazyZipf("float2", ValueType.FLOAT, false, 1, 0.4, 0, 10, 1.5), + GeneratorColumnSchema.makeLazyZipf("float3", ValueType.FLOAT, false, 1, 0.8, 0, 5000, 2.3), + GeneratorColumnSchema.makeLazyZipf("float4", ValueType.FLOAT, false, 1, 0.999, 0, 14440, 2.0), + GeneratorColumnSchema.makeLazyZipf("float5", ValueType.FLOAT, false, 1, 0.001, 0, 1029, 1.5), + GeneratorColumnSchema.makeLazyZipf("float6", ValueType.FLOAT, false, 1, 0.01, 0, 4445555, 1.8), + GeneratorColumnSchema.makeLazyZipf("float7", ValueType.FLOAT, false, 1, 0.44, 0, 1000000, 1.1), + GeneratorColumnSchema.makeLazyZipf("float8", ValueType.FLOAT, false, 1, 0.55, 0, 33, 4.5), + GeneratorColumnSchema.makeLazyZipf("float9", ValueType.FLOAT, false, 1, 0.12, 0, 5000, 2.2), + GeneratorColumnSchema.makeLazyZipf("float10", ValueType.FLOAT, false, 1, 0.11, 0, 101, 1.3) + ); + + List simpleNullsSchemaIngestAggs = new ArrayList<>(); + simpleNullsSchemaIngestAggs.add(new CountAggregatorFactory("rows")); + + Interval nullsSchemaDataInterval = Intervals.of("2000-01-01/P1D"); + + GeneratorSchemaInfo nullsSchema = new GeneratorSchemaInfo( + nullsSchemaColumns, + simpleNullsSchemaIngestAggs, + nullsSchemaDataInterval, + false + ); + + SCHEMA_INFO_BUILDER.put("wide", nullsSchema); + } + + public static final Map SCHEMA_MAP = SCHEMA_INFO_BUILDER.build(); +} diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkColumnSchema.java b/processing/src/main/java/org/apache/druid/segment/generator/GeneratorColumnSchema.java similarity index 58% rename from benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkColumnSchema.java rename to processing/src/main/java/org/apache/druid/segment/generator/GeneratorColumnSchema.java index 8636941dd28d..2c5e42d6651d 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkColumnSchema.java +++ b/processing/src/main/java/org/apache/druid/segment/generator/GeneratorColumnSchema.java @@ -17,8 +17,11 @@ * under the License. */ -package org.apache.druid.benchmark.datagen; +package org.apache.druid.segment.generator; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.druid.data.input.impl.DimensionSchema; import org.apache.druid.data.input.impl.DoubleDimensionSchema; import org.apache.druid.data.input.impl.FloatDimensionSchema; @@ -28,26 +31,32 @@ import org.apache.druid.segment.column.ValueType; import java.util.List; +import java.util.Objects; -public class BenchmarkColumnSchema +public class GeneratorColumnSchema { + /** - * SEQUENTIAL: Generate integer or enumerated values in sequence. Not random. + * SEQUENTIAL: Generate integer or enumerated values in sequence. Not random. + * + * DISCRETE_UNIFORM: Discrete uniform distribution, generates integers or enumerated values. + * + * ROUNDED_NORMAL: Discrete distribution that rounds sample values from an underlying normal + * distribution * - * DISCRETE_UNIFORM: Discrete uniform distribution, generates integers or enumerated values. + * ZIPF: Discrete Zipf distribution. + * Lower numbers have higher probability. + * Can also generate Zipf distribution from a list of enumerated values. * - * ROUNDED_NORMAL: Discrete distribution that rounds sample values from an underlying normal - * distribution + * LAZY_ZIPF: ZIPF but lazy evaluated for large cardinalities * - * ZIPF: Discrete Zipf distribution. - * Lower numbers have higher probability. - * Can also generate Zipf distribution from a list of enumerated values. + * LAZY_DISCRETE_UNIFORM: DISCRETE_UNIFORM but lazy evaluated for large cardinalities * - * ENUMERATED: Discrete distribution, generated from lists of values and associated probabilities. + * ENUMERATED: Discrete distribution, generated from lists of values and associated probabilities. * - * NORMAL: Continuous normal distribution. + * NORMAL: Continuous normal distribution. * - * UNIFORM: Continuous uniform distribution. + * UNIFORM: Continuous uniform distribution. */ public enum ValueDistribution { @@ -57,6 +66,8 @@ public enum ValueDistribution ROUNDED_NORMAL, ZIPF, ENUMERATED, + LAZY_ZIPF, + LAZY_DISCRETE_UNIFORM, // continuous distributions UNIFORM, @@ -128,7 +139,43 @@ public enum ValueDistribution private Double mean; private Double standardDeviation; - private BenchmarkColumnSchema( + @JsonCreator + public GeneratorColumnSchema( + @JsonProperty("name") String name, + @JsonProperty("type") ValueType type, + @JsonProperty("isMetric") boolean isMetric, + @JsonProperty("rowSize") int rowSize, + @JsonProperty("nullProbability") Double nullProbability, + @JsonProperty("distributionType") ValueDistribution distributionType, + @JsonProperty("enumeratedValues") List enumeratedValues, + @JsonProperty("enumeratedProbabilities") List enumeratedProbabilities, + @JsonProperty("startInt") Integer startInt, + @JsonProperty("endInt") Integer endInt, + @JsonProperty("startDouble") Double startDouble, + @JsonProperty("endDouble") Double endDouble, + @JsonProperty("zipfExponent") Double zipfExponent, + @JsonProperty("mean") Double mean, + @JsonProperty("standardDeviation") Double standardDeviation + ) + { + this.name = name; + this.type = type; + this.isMetric = isMetric; + this.distributionType = distributionType; + this.rowSize = rowSize; + this.nullProbability = nullProbability; + this.enumeratedValues = enumeratedValues; + this.enumeratedProbabilities = enumeratedProbabilities; + this.startInt = startInt; + this.endInt = endInt; + this.startDouble = startDouble; + this.endDouble = endDouble; + this.zipfExponent = zipfExponent; + this.mean = mean; + this.standardDeviation = standardDeviation; + } + + private GeneratorColumnSchema( String name, ValueType type, boolean isMetric, @@ -145,11 +192,12 @@ private BenchmarkColumnSchema( this.nullProbability = nullProbability; } - public BenchmarkColumnValueGenerator makeGenerator(long seed) + public ColumnValueGenerator makeGenerator(long seed) { - return new BenchmarkColumnValueGenerator(this, seed); + return new ColumnValueGenerator(this, seed); } + @JsonIgnore public DimensionSchema getDimensionSchema() { switch (type) { @@ -166,82 +214,97 @@ public DimensionSchema getDimensionSchema() } } + @JsonProperty public String getName() { return name; } + @JsonProperty public Double getNullProbability() { return nullProbability; } + @JsonProperty public ValueType getType() { return type; } + @JsonProperty public boolean isMetric() { return isMetric; } + @JsonProperty public ValueDistribution getDistributionType() { return distributionType; } + @JsonProperty public int getRowSize() { return rowSize; } + @JsonProperty public List getEnumeratedValues() { return enumeratedValues; } + @JsonProperty public List getEnumeratedProbabilities() { return enumeratedProbabilities; } + @JsonProperty public Integer getStartInt() { return startInt; } + @JsonProperty public Integer getEndInt() { return endInt; } + @JsonProperty public Double getStartDouble() { return startDouble; } + @JsonProperty public Double getEndDouble() { return endDouble; } + @JsonProperty public Double getZipfExponent() { return zipfExponent; } + @JsonProperty public Double getMean() { return mean; } + @JsonProperty public Double getStandardDeviation() { return standardDeviation; } - public static BenchmarkColumnSchema makeSequential( + public static GeneratorColumnSchema makeSequential( String name, ValueType type, boolean isMetric, @@ -251,7 +314,7 @@ public static BenchmarkColumnSchema makeSequential( int endInt ) { - BenchmarkColumnSchema schema = new BenchmarkColumnSchema( + GeneratorColumnSchema schema = new GeneratorColumnSchema( name, type, isMetric, @@ -264,7 +327,7 @@ public static BenchmarkColumnSchema makeSequential( return schema; } - public static BenchmarkColumnSchema makeEnumeratedSequential( + public static GeneratorColumnSchema makeEnumeratedSequential( String name, ValueType type, boolean isMetric, @@ -273,7 +336,7 @@ public static BenchmarkColumnSchema makeEnumeratedSequential( List enumeratedValues ) { - BenchmarkColumnSchema schema = new BenchmarkColumnSchema( + GeneratorColumnSchema schema = new GeneratorColumnSchema( name, type, isMetric, @@ -285,7 +348,7 @@ public static BenchmarkColumnSchema makeEnumeratedSequential( return schema; } - public static BenchmarkColumnSchema makeDiscreteUniform( + public static GeneratorColumnSchema makeDiscreteUniform( String name, ValueType type, boolean isMetric, @@ -295,7 +358,7 @@ public static BenchmarkColumnSchema makeDiscreteUniform( int endInt ) { - BenchmarkColumnSchema schema = new BenchmarkColumnSchema( + GeneratorColumnSchema schema = new GeneratorColumnSchema( name, type, isMetric, @@ -308,7 +371,7 @@ public static BenchmarkColumnSchema makeDiscreteUniform( return schema; } - public static BenchmarkColumnSchema makeEnumeratedDiscreteUniform( + public static GeneratorColumnSchema makeEnumeratedDiscreteUniform( String name, ValueType type, boolean isMetric, @@ -317,7 +380,7 @@ public static BenchmarkColumnSchema makeEnumeratedDiscreteUniform( List enumeratedValues ) { - BenchmarkColumnSchema schema = new BenchmarkColumnSchema( + GeneratorColumnSchema schema = new GeneratorColumnSchema( name, type, isMetric, @@ -329,7 +392,31 @@ public static BenchmarkColumnSchema makeEnumeratedDiscreteUniform( return schema; } - public static BenchmarkColumnSchema makeContinuousUniform( + public static GeneratorColumnSchema makeLazyDiscreteUniform( + String name, + ValueType type, + boolean isMetric, + int rowSize, + Double nullProbability, + int startInt, + int endInt + ) + { + GeneratorColumnSchema schema = new GeneratorColumnSchema( + name, + type, + isMetric, + rowSize, + nullProbability, + ValueDistribution.LAZY_DISCRETE_UNIFORM + ); + schema.startInt = startInt; + schema.endInt = endInt; + return schema; + } + + + public static GeneratorColumnSchema makeContinuousUniform( String name, ValueType type, boolean isMetric, @@ -339,7 +426,7 @@ public static BenchmarkColumnSchema makeContinuousUniform( double endDouble ) { - BenchmarkColumnSchema schema = new BenchmarkColumnSchema( + GeneratorColumnSchema schema = new GeneratorColumnSchema( name, type, isMetric, @@ -352,7 +439,7 @@ public static BenchmarkColumnSchema makeContinuousUniform( return schema; } - public static BenchmarkColumnSchema makeNormal( + public static GeneratorColumnSchema makeNormal( String name, ValueType type, boolean isMetric, @@ -363,7 +450,7 @@ public static BenchmarkColumnSchema makeNormal( boolean useRounding ) { - BenchmarkColumnSchema schema = new BenchmarkColumnSchema( + GeneratorColumnSchema schema = new GeneratorColumnSchema( name, type, isMetric, @@ -376,7 +463,7 @@ public static BenchmarkColumnSchema makeNormal( return schema; } - public static BenchmarkColumnSchema makeZipf( + public static GeneratorColumnSchema makeZipf( String name, ValueType type, boolean isMetric, @@ -387,7 +474,7 @@ public static BenchmarkColumnSchema makeZipf( Double zipfExponent ) { - BenchmarkColumnSchema schema = new BenchmarkColumnSchema( + GeneratorColumnSchema schema = new GeneratorColumnSchema( name, type, isMetric, @@ -401,7 +488,32 @@ public static BenchmarkColumnSchema makeZipf( return schema; } - public static BenchmarkColumnSchema makeEnumeratedZipf( + public static GeneratorColumnSchema makeLazyZipf( + String name, + ValueType type, + boolean isMetric, + int rowSize, + Double nullProbability, + int startInt, + int endInt, + Double zipfExponent + ) + { + GeneratorColumnSchema schema = new GeneratorColumnSchema( + name, + type, + isMetric, + rowSize, + nullProbability, + ValueDistribution.LAZY_ZIPF + ); + schema.startInt = startInt; + schema.endInt = endInt; + schema.zipfExponent = zipfExponent; + return schema; + } + + public static GeneratorColumnSchema makeEnumeratedZipf( String name, ValueType type, boolean isMetric, @@ -411,7 +523,7 @@ public static BenchmarkColumnSchema makeEnumeratedZipf( Double zipfExponent ) { - BenchmarkColumnSchema schema = new BenchmarkColumnSchema( + GeneratorColumnSchema schema = new GeneratorColumnSchema( name, type, isMetric, @@ -424,7 +536,7 @@ public static BenchmarkColumnSchema makeEnumeratedZipf( return schema; } - public static BenchmarkColumnSchema makeEnumerated( + public static GeneratorColumnSchema makeEnumerated( String name, ValueType type, boolean isMetric, @@ -434,7 +546,7 @@ public static BenchmarkColumnSchema makeEnumerated( List enumeratedProbabilities ) { - BenchmarkColumnSchema schema = new BenchmarkColumnSchema( + GeneratorColumnSchema schema = new GeneratorColumnSchema( name, type, isMetric, @@ -468,4 +580,53 @@ public String toString() ", standardDeviation=" + standardDeviation + '}'; } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GeneratorColumnSchema that = (GeneratorColumnSchema) o; + return isMetric == that.isMetric && + rowSize == that.rowSize && + distributionType == that.distributionType && + name.equals(that.name) && + type == that.type && + Objects.equals(nullProbability, that.nullProbability) && + Objects.equals(enumeratedValues, that.enumeratedValues) && + Objects.equals(enumeratedProbabilities, that.enumeratedProbabilities) && + Objects.equals(startInt, that.startInt) && + Objects.equals(endInt, that.endInt) && + Objects.equals(startDouble, that.startDouble) && + Objects.equals(endDouble, that.endDouble) && + Objects.equals(zipfExponent, that.zipfExponent) && + Objects.equals(mean, that.mean) && + Objects.equals(standardDeviation, that.standardDeviation); + } + + @Override + public int hashCode() + { + return Objects.hash( + distributionType, + name, + type, + isMetric, + rowSize, + nullProbability, + enumeratedValues, + enumeratedProbabilities, + startInt, + endInt, + startDouble, + endDouble, + zipfExponent, + mean, + standardDeviation + ); + } } diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkSchemaInfo.java b/processing/src/main/java/org/apache/druid/segment/generator/GeneratorSchemaInfo.java similarity index 86% rename from benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkSchemaInfo.java rename to processing/src/main/java/org/apache/druid/segment/generator/GeneratorSchemaInfo.java index f8c1b60dd373..cb1591ab897e 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/BenchmarkSchemaInfo.java +++ b/processing/src/main/java/org/apache/druid/segment/generator/GeneratorSchemaInfo.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.druid.benchmark.datagen; +package org.apache.druid.segment.generator; import org.apache.druid.data.input.impl.DimensionSchema; import org.apache.druid.data.input.impl.DimensionsSpec; @@ -27,15 +27,15 @@ import java.util.List; import java.util.stream.Collectors; -public class BenchmarkSchemaInfo +public class GeneratorSchemaInfo { - private List columnSchemas; + private List columnSchemas; private List aggs; private Interval dataInterval; private boolean withRollup; - public BenchmarkSchemaInfo( - List columnSchemas, + public GeneratorSchemaInfo( + List columnSchemas, List aggs, Interval dataInterval, boolean withRollup @@ -47,7 +47,7 @@ public BenchmarkSchemaInfo( this.withRollup = withRollup; } - public List getColumnSchemas() + public List getColumnSchemas() { return columnSchemas; } @@ -56,7 +56,7 @@ public DimensionsSpec getDimensionsSpec() { List specs = getColumnSchemas().stream() .filter(x -> !x.isMetric()) - .map(BenchmarkColumnSchema::getDimensionSchema) + .map(GeneratorColumnSchema::getDimensionSchema) .collect(Collectors.toList()); return new DimensionsSpec(specs); diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/RealRoundingDistribution.java b/processing/src/main/java/org/apache/druid/segment/generator/RealRoundingDistribution.java similarity index 97% rename from benchmarks/src/test/java/org/apache/druid/benchmark/datagen/RealRoundingDistribution.java rename to processing/src/main/java/org/apache/druid/segment/generator/RealRoundingDistribution.java index 320a0cb9a352..6d663cfec484 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/RealRoundingDistribution.java +++ b/processing/src/main/java/org/apache/druid/segment/generator/RealRoundingDistribution.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.druid.benchmark.datagen; +package org.apache.druid.segment.generator; import org.apache.commons.math3.distribution.AbstractIntegerDistribution; import org.apache.commons.math3.distribution.AbstractRealDistribution; diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/SequentialDistribution.java b/processing/src/main/java/org/apache/druid/segment/generator/SequentialDistribution.java similarity index 97% rename from benchmarks/src/test/java/org/apache/druid/benchmark/datagen/SequentialDistribution.java rename to processing/src/main/java/org/apache/druid/segment/generator/SequentialDistribution.java index c10d39255355..514540be0631 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/datagen/SequentialDistribution.java +++ b/processing/src/main/java/org/apache/druid/segment/generator/SequentialDistribution.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.druid.benchmark.datagen; +package org.apache.druid.segment.generator; import org.apache.commons.math3.distribution.EnumeratedDistribution; import org.apache.commons.math3.util.Pair; diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/BenchmarkDataGeneratorTest.java b/processing/src/test/java/org/apache/druid/segment/generator/DataGeneratorTest.java similarity index 72% rename from benchmarks/src/test/java/org/apache/druid/benchmark/BenchmarkDataGeneratorTest.java rename to processing/src/test/java/org/apache/druid/segment/generator/DataGeneratorTest.java index 035e33079ce9..4f65d377d4ee 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/BenchmarkDataGeneratorTest.java +++ b/processing/src/test/java/org/apache/druid/segment/generator/DataGeneratorTest.java @@ -17,13 +17,13 @@ * under the License. */ -package org.apache.druid.benchmark; +package org.apache.druid.segment.generator; -import org.apache.druid.benchmark.datagen.BenchmarkColumnSchema; -import org.apache.druid.benchmark.datagen.BenchmarkDataGenerator; +import org.apache.commons.math3.distribution.NormalDistribution; import org.apache.druid.data.input.InputRow; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.segment.column.ValueType; +import org.junit.Assert; import org.junit.Test; import java.util.ArrayList; @@ -34,16 +34,16 @@ import java.util.Map; // Doesn't assert behavior right now, just generates rows and prints out some distribution numbers -public class BenchmarkDataGeneratorTest +public class DataGeneratorTest { @Test public void testSequential() { - List schemas = new ArrayList<>(); + List schemas = new ArrayList<>(); RowValueTracker tracker = new RowValueTracker(); schemas.add( - BenchmarkColumnSchema.makeSequential( + GeneratorColumnSchema.makeSequential( "dimA", ValueType.STRING, false, @@ -55,7 +55,7 @@ public void testSequential() ); schemas.add( - BenchmarkColumnSchema.makeEnumeratedSequential( + GeneratorColumnSchema.makeEnumeratedSequential( "dimB", ValueType.STRING, false, @@ -66,7 +66,7 @@ public void testSequential() ); schemas.add( - BenchmarkColumnSchema.makeSequential( + GeneratorColumnSchema.makeSequential( "dimC", ValueType.STRING, false, @@ -77,7 +77,7 @@ public void testSequential() ) ); - BenchmarkDataGenerator dataGenerator = new BenchmarkDataGenerator(schemas, 9999, 0, 0, 1000.0); + DataGenerator dataGenerator = new DataGenerator(schemas, 9999, 0, 0, 1000.0); for (int i = 0; i < 100; i++) { InputRow row = dataGenerator.nextRow(); //System.out.println("S-ROW: " + row); @@ -89,11 +89,11 @@ public void testSequential() @Test public void testDiscreteUniform() { - List schemas = new ArrayList<>(); + List schemas = new ArrayList<>(); RowValueTracker tracker = new RowValueTracker(); schemas.add( - BenchmarkColumnSchema.makeDiscreteUniform( + GeneratorColumnSchema.makeDiscreteUniform( "dimA", ValueType.STRING, false, @@ -105,7 +105,7 @@ public void testDiscreteUniform() ); schemas.add( - BenchmarkColumnSchema.makeEnumeratedDiscreteUniform( + GeneratorColumnSchema.makeEnumeratedDiscreteUniform( "dimB", ValueType.STRING, false, @@ -116,7 +116,7 @@ public void testDiscreteUniform() ); schemas.add( - BenchmarkColumnSchema.makeDiscreteUniform( + GeneratorColumnSchema.makeDiscreteUniform( "dimC", ValueType.STRING, false, @@ -128,7 +128,7 @@ public void testDiscreteUniform() ); schemas.add( - BenchmarkColumnSchema.makeDiscreteUniform( + GeneratorColumnSchema.makeDiscreteUniform( "dimD", ValueType.FLOAT, false, @@ -139,7 +139,7 @@ public void testDiscreteUniform() ) ); - BenchmarkDataGenerator dataGenerator = new BenchmarkDataGenerator(schemas, 9999, 0, 0, 1000.0); + DataGenerator dataGenerator = new DataGenerator(schemas, 9999, 0, 0, 1000.0); for (int i = 0; i < 100; i++) { InputRow row = dataGenerator.nextRow(); //System.out.println("U-ROW: " + row); @@ -154,11 +154,11 @@ public void testDiscreteUniform() @Test public void testRoundedNormal() { - List schemas = new ArrayList<>(); + List schemas = new ArrayList<>(); RowValueTracker tracker = new RowValueTracker(); schemas.add( - BenchmarkColumnSchema.makeNormal( + GeneratorColumnSchema.makeNormal( "dimA", ValueType.FLOAT, false, @@ -171,7 +171,7 @@ public void testRoundedNormal() ); schemas.add( - BenchmarkColumnSchema.makeNormal( + GeneratorColumnSchema.makeNormal( "dimB", ValueType.STRING, false, @@ -183,7 +183,7 @@ public void testRoundedNormal() ) ); - BenchmarkDataGenerator dataGenerator = new BenchmarkDataGenerator(schemas, 9999, 0, 0, 1000.0); + DataGenerator dataGenerator = new DataGenerator(schemas, 9999, 0, 0, 1000.0); for (int i = 0; i < 1000000; i++) { InputRow row = dataGenerator.nextRow(); //System.out.println("N-ROW: " + row); @@ -197,11 +197,11 @@ public void testRoundedNormal() @Test public void testZipf() { - List schemas = new ArrayList<>(); + List schemas = new ArrayList<>(); RowValueTracker tracker = new RowValueTracker(); schemas.add( - BenchmarkColumnSchema.makeZipf( + GeneratorColumnSchema.makeZipf( "dimA", ValueType.STRING, false, @@ -214,7 +214,7 @@ public void testZipf() ); schemas.add( - BenchmarkColumnSchema.makeZipf( + GeneratorColumnSchema.makeZipf( "dimB", ValueType.FLOAT, false, @@ -227,7 +227,7 @@ public void testZipf() ); schemas.add( - BenchmarkColumnSchema.makeEnumeratedZipf( + GeneratorColumnSchema.makeEnumeratedZipf( "dimC", ValueType.STRING, false, @@ -238,7 +238,7 @@ public void testZipf() ) ); - BenchmarkDataGenerator dataGenerator = new BenchmarkDataGenerator(schemas, 9999, 0, 0, 1000.0); + DataGenerator dataGenerator = new DataGenerator(schemas, 9999, 0, 0, 1000.0); for (int i = 0; i < 100; i++) { InputRow row = dataGenerator.nextRow(); //System.out.println("Z-ROW: " + row); @@ -252,11 +252,11 @@ public void testZipf() @Test public void testEnumerated() { - List schemas = new ArrayList<>(); + List schemas = new ArrayList<>(); RowValueTracker tracker = new RowValueTracker(); schemas.add( - BenchmarkColumnSchema.makeEnumerated( + GeneratorColumnSchema.makeEnumerated( "dimA", ValueType.STRING, false, @@ -267,7 +267,7 @@ public void testEnumerated() ) ); - BenchmarkDataGenerator dataGenerator = new BenchmarkDataGenerator(schemas, 9999, 0, 0, 1000.0); + DataGenerator dataGenerator = new DataGenerator(schemas, 9999, 0, 0, 1000.0); for (int i = 0; i < 10000; i++) { InputRow row = dataGenerator.nextRow(); //System.out.println("Z-ROW: " + row); @@ -281,11 +281,11 @@ public void testEnumerated() @Test public void testNormal() { - List schemas = new ArrayList<>(); + List schemas = new ArrayList<>(); RowValueTracker tracker = new RowValueTracker(); schemas.add( - BenchmarkColumnSchema.makeNormal( + GeneratorColumnSchema.makeNormal( "dimA", ValueType.FLOAT, false, @@ -298,7 +298,7 @@ public void testNormal() ); schemas.add( - BenchmarkColumnSchema.makeNormal( + GeneratorColumnSchema.makeNormal( "dimB", ValueType.STRING, false, @@ -310,7 +310,7 @@ public void testNormal() ) ); - BenchmarkDataGenerator dataGenerator = new BenchmarkDataGenerator(schemas, 9999, 0, 0, 1000.0); + DataGenerator dataGenerator = new DataGenerator(schemas, 9999, 0, 0, 1000.0); for (int i = 0; i < 100; i++) { InputRow row = dataGenerator.nextRow(); //System.out.println("N-ROW: " + row); @@ -324,11 +324,11 @@ public void testNormal() @Test public void testRealUniform() { - List schemas = new ArrayList<>(); + List schemas = new ArrayList<>(); RowValueTracker tracker = new RowValueTracker(); schemas.add( - BenchmarkColumnSchema.makeContinuousUniform( + GeneratorColumnSchema.makeContinuousUniform( "dimA", ValueType.STRING, false, @@ -340,7 +340,7 @@ public void testRealUniform() ); schemas.add( - BenchmarkColumnSchema.makeContinuousUniform( + GeneratorColumnSchema.makeContinuousUniform( "dimB", ValueType.FLOAT, false, @@ -351,7 +351,7 @@ public void testRealUniform() ) ); - BenchmarkDataGenerator dataGenerator = new BenchmarkDataGenerator(schemas, 9999, 0, 0, 1000.0); + DataGenerator dataGenerator = new DataGenerator(schemas, 9999, 0, 0, 1000.0); for (int i = 0; i < 100; i++) { InputRow row = dataGenerator.nextRow(); //System.out.println("U-ROW: " + row); @@ -365,10 +365,10 @@ public void testRealUniform() @Test public void testIntervalBasedTimeGeneration() { - List schemas = new ArrayList<>(); + List schemas = new ArrayList<>(); schemas.add( - BenchmarkColumnSchema.makeEnumeratedSequential( + GeneratorColumnSchema.makeEnumeratedSequential( "dimB", ValueType.STRING, false, @@ -378,17 +378,38 @@ public void testIntervalBasedTimeGeneration() ) ); - BenchmarkDataGenerator dataGenerator = new BenchmarkDataGenerator(schemas, 9999, Intervals.utc(50000, 600000), 100); + DataGenerator dataGenerator = new DataGenerator(schemas, 9999, Intervals.utc(50000, 600000), 100); for (int i = 0; i < 100; i++) { dataGenerator.nextRow(); } - BenchmarkDataGenerator dataGenerator2 = new BenchmarkDataGenerator(schemas, 9999, Intervals.utc(50000, 50001), 100); + DataGenerator dataGenerator2 = new DataGenerator(schemas, 9999, Intervals.utc(50000, 50001), 100); for (int i = 0; i < 100; i++) { dataGenerator2.nextRow(); } } + @Test + public void testBasicSchemasAndGeneratorSchemaInfo() + { + GeneratorSchemaInfo basicSchema = GeneratorBasicSchemas.SCHEMA_MAP.get("basic"); + Assert.assertEquals(13, basicSchema.getColumnSchemas().size()); + Assert.assertEquals(6, basicSchema.getAggs().size()); + Assert.assertEquals(6, basicSchema.getAggsArray().length); + Assert.assertNotNull(basicSchema.getDimensionsSpec()); + Assert.assertNotNull(basicSchema.getDataInterval()); + Assert.assertTrue(basicSchema.isWithRollup()); + } + + @Test + public void testRealRoundingDistributionZeroGetters() + { + RealRoundingDistribution dist = new RealRoundingDistribution(new NormalDistribution()); + Assert.assertEquals(0, dist.getSupportLowerBound()); + Assert.assertEquals(0, dist.getSupportUpperBound()); + Assert.assertEquals(0, dist.getNumericalMean(), 0); + Assert.assertEquals(0, dist.getNumericalVariance(), 0); + } private static class RowValueTracker { diff --git a/processing/src/test/java/org/apache/druid/segment/generator/GeneratorColumnSchemaTest.java b/processing/src/test/java/org/apache/druid/segment/generator/GeneratorColumnSchemaTest.java new file mode 100644 index 000000000000..4edf6bd362c4 --- /dev/null +++ b/processing/src/test/java/org/apache/druid/segment/generator/GeneratorColumnSchemaTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.generator; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import nl.jqno.equalsverifier.EqualsVerifier; +import nl.jqno.equalsverifier.Warning; +import org.apache.druid.jackson.DefaultObjectMapper; +import org.apache.druid.segment.column.ValueType; +import org.junit.Assert; +import org.junit.Test; + +public class GeneratorColumnSchemaTest +{ + private static final ObjectMapper MAPPER = new DefaultObjectMapper(); + + @Test + public void testSerde() throws JsonProcessingException + { + GeneratorColumnSchema schema = GeneratorColumnSchema.makeLazyZipf( + "test", + ValueType.LONG, + false, + 1, + 0.0, + 0, + 1000, + 1.3 + ); + + final String serialized = MAPPER.writeValueAsString(schema); + GeneratorColumnSchema again = MAPPER.readValue(serialized, GeneratorColumnSchema.class); + Assert.assertEquals(schema, again); + } + + @Test + public void testEquals() + { + EqualsVerifier.forClass(GeneratorColumnSchema.class) + .withNonnullFields("name", "distributionType", "type") + .suppress(Warning.NONFINAL_FIELDS) + .usingGetClass() + .verify(); + } +} From 8a127f59f57b2350c2e3cb5e64ad0d1ca1a33295 Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Tue, 9 Jun 2020 19:32:16 -0700 Subject: [PATCH 069/107] remove incorrect and unnecessary overrides from BooleanVectorValueMatcher (#9994) * remove incorrect and unnecessary overrides from BooleanVectorValueMatcher * add test case * add unit tests for ... part of VectorValueMatcherColumnProcessorFactory * Update VectorValueMatcherColumnProcessorFactoryTest.java --- .../vector/BooleanVectorValueMatcher.java | 14 - ...alueMatcherColumnProcessorFactoryTest.java | 277 ++++++++++++++++++ .../query/groupby/GroupByQueryRunnerTest.java | 30 ++ 3 files changed, 307 insertions(+), 14 deletions(-) create mode 100644 processing/src/test/java/org/apache/druid/query/filter/vector/VectorValueMatcherColumnProcessorFactoryTest.java diff --git a/processing/src/main/java/org/apache/druid/query/filter/vector/BooleanVectorValueMatcher.java b/processing/src/main/java/org/apache/druid/query/filter/vector/BooleanVectorValueMatcher.java index 65af27b83fc2..efa0236acfdb 100644 --- a/processing/src/main/java/org/apache/druid/query/filter/vector/BooleanVectorValueMatcher.java +++ b/processing/src/main/java/org/apache/druid/query/filter/vector/BooleanVectorValueMatcher.java @@ -23,13 +23,11 @@ public class BooleanVectorValueMatcher extends BaseVectorValueMatcher { - private final VectorSizeInspector selector; private final boolean matches; private BooleanVectorValueMatcher(final VectorSizeInspector selector, final boolean matches) { super(selector); - this.selector = selector; this.matches = matches; } @@ -38,18 +36,6 @@ public static BooleanVectorValueMatcher of(final VectorSizeInspector selector, f return new BooleanVectorValueMatcher(selector, matches); } - @Override - public int getCurrentVectorSize() - { - return selector.getCurrentVectorSize(); - } - - @Override - public int getMaxVectorSize() - { - return selector.getCurrentVectorSize(); - } - @Override public ReadableVectorMatch match(final ReadableVectorMatch mask) { diff --git a/processing/src/test/java/org/apache/druid/query/filter/vector/VectorValueMatcherColumnProcessorFactoryTest.java b/processing/src/test/java/org/apache/druid/query/filter/vector/VectorValueMatcherColumnProcessorFactoryTest.java new file mode 100644 index 000000000000..ffa0253b2cf3 --- /dev/null +++ b/processing/src/test/java/org/apache/druid/query/filter/vector/VectorValueMatcherColumnProcessorFactoryTest.java @@ -0,0 +1,277 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query.filter.vector; + +import org.apache.druid.common.config.NullHandling; +import org.apache.druid.segment.IdLookup; +import org.apache.druid.segment.vector.MultiValueDimensionVectorSelector; +import org.apache.druid.segment.vector.SingleValueDimensionVectorSelector; +import org.apache.druid.segment.vector.VectorValueSelector; +import org.apache.druid.testing.InitializedNullHandlingTest; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class VectorValueMatcherColumnProcessorFactoryTest extends InitializedNullHandlingTest +{ + private static final int VECTOR_SIZE = 128; + private static final int CURRENT_SIZE = 24; + private VectorValueSelector vectorValueSelector; + + @Before + public void setup() + { + vectorValueSelector = EasyMock.createMock(VectorValueSelector.class); + EasyMock.expect(vectorValueSelector.getCurrentVectorSize()).andReturn(CURRENT_SIZE).anyTimes(); + EasyMock.expect(vectorValueSelector.getMaxVectorSize()).andReturn(VECTOR_SIZE).anyTimes(); + EasyMock.replay(vectorValueSelector); + } + + @Test + public void testFloat() + { + VectorValueMatcherFactory matcherFactory = + VectorValueMatcherColumnProcessorFactory.instance().makeFloatProcessor(vectorValueSelector); + + Assert.assertTrue(matcherFactory instanceof FloatVectorValueMatcher); + + VectorValueMatcher matcher = matcherFactory.makeMatcher("2.0"); + Assert.assertFalse(matcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, matcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, matcher.getCurrentVectorSize()); + + // in default mode, matching null produces a boolean matcher + VectorValueMatcher booleanMatcher = matcherFactory.makeMatcher((String) null); + if (NullHandling.replaceWithDefault()) { + Assert.assertTrue(booleanMatcher instanceof BooleanVectorValueMatcher); + } else { + Assert.assertFalse(booleanMatcher instanceof BooleanVectorValueMatcher); + } + Assert.assertEquals(VECTOR_SIZE, booleanMatcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, booleanMatcher.getCurrentVectorSize()); + EasyMock.verify(vectorValueSelector); + } + + @Test + public void testDouble() + { + VectorValueMatcherFactory matcherFactory = + VectorValueMatcherColumnProcessorFactory.instance().makeDoubleProcessor(vectorValueSelector); + + Assert.assertTrue(matcherFactory instanceof DoubleVectorValueMatcher); + + + VectorValueMatcher matcher = matcherFactory.makeMatcher("1.0"); + Assert.assertFalse(matcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, matcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, matcher.getCurrentVectorSize()); + + // in default mode, matching null produces a boolean matcher + VectorValueMatcher booleanMatcher = matcherFactory.makeMatcher((String) null); + if (NullHandling.replaceWithDefault()) { + Assert.assertTrue(booleanMatcher instanceof BooleanVectorValueMatcher); + } else { + Assert.assertFalse(booleanMatcher instanceof BooleanVectorValueMatcher); + } + Assert.assertEquals(VECTOR_SIZE, booleanMatcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, booleanMatcher.getCurrentVectorSize()); + EasyMock.verify(vectorValueSelector); + } + + @Test + public void testLong() + { + VectorValueMatcherFactory matcherFactory = + VectorValueMatcherColumnProcessorFactory.instance().makeLongProcessor(vectorValueSelector); + + Assert.assertTrue(matcherFactory instanceof LongVectorValueMatcher); + + VectorValueMatcher matcher = matcherFactory.makeMatcher("1"); + Assert.assertFalse(matcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, matcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, matcher.getCurrentVectorSize()); + + // in default mode, matching null produces a boolean matcher + VectorValueMatcher booleanMatcher = matcherFactory.makeMatcher((String) null); + if (NullHandling.replaceWithDefault()) { + Assert.assertTrue(booleanMatcher instanceof BooleanVectorValueMatcher); + } else { + Assert.assertFalse(booleanMatcher instanceof BooleanVectorValueMatcher); + } + Assert.assertEquals(VECTOR_SIZE, booleanMatcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, booleanMatcher.getCurrentVectorSize()); + EasyMock.verify(vectorValueSelector); + } + + @Test + public void testSingleValueString() + { + IdLookup lookup = EasyMock.createMock(IdLookup.class); + SingleValueDimensionVectorSelector selector = + EasyMock.createMock(SingleValueDimensionVectorSelector.class); + EasyMock.expect(selector.getCurrentVectorSize()).andReturn(CURRENT_SIZE).anyTimes(); + EasyMock.expect(selector.getMaxVectorSize()).andReturn(VECTOR_SIZE).anyTimes(); + EasyMock.expect(selector.getValueCardinality()).andReturn(1024).anyTimes(); + EasyMock.expect(selector.nameLookupPossibleInAdvance()).andReturn(false).anyTimes(); + EasyMock.expect(selector.idLookup()).andReturn(lookup).anyTimes(); + EasyMock.expect(lookup.lookupId("any value")).andReturn(1).anyTimes(); + EasyMock.expect(lookup.lookupId("another value")).andReturn(-1).anyTimes(); + EasyMock.replay(selector, lookup); + + VectorValueMatcherFactory matcherFactory = + VectorValueMatcherColumnProcessorFactory.instance().makeSingleValueDimensionProcessor(selector); + + Assert.assertTrue(matcherFactory instanceof SingleValueStringVectorValueMatcher); + + // value exists in column nonboolean matcher + VectorValueMatcher matcher = matcherFactory.makeMatcher("any value"); + Assert.assertFalse(matcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, matcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, matcher.getCurrentVectorSize()); + + // value not exist in dictionary uses boolean matcher + VectorValueMatcher booleanMatcher = matcherFactory.makeMatcher("another value"); + Assert.assertTrue(booleanMatcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, booleanMatcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, booleanMatcher.getCurrentVectorSize()); + EasyMock.verify(selector, lookup); + } + + @Test + public void testSingleValueStringZeroCardinalityAlwaysBooleanMatcher() + { + // cardinality 0 has special path to always use boolean matcher + SingleValueDimensionVectorSelector selector = + EasyMock.createMock(SingleValueDimensionVectorSelector.class); + EasyMock.expect(selector.getCurrentVectorSize()).andReturn(CURRENT_SIZE).anyTimes(); + EasyMock.expect(selector.getMaxVectorSize()).andReturn(VECTOR_SIZE).anyTimes(); + EasyMock.expect(selector.getValueCardinality()).andReturn(0).anyTimes(); + EasyMock.replay(selector); + + VectorValueMatcherFactory matcherFactory = + VectorValueMatcherColumnProcessorFactory.instance().makeSingleValueDimensionProcessor(selector); + + Assert.assertTrue(matcherFactory instanceof SingleValueStringVectorValueMatcher); + + VectorValueMatcher matcher = matcherFactory.makeMatcher("any value"); + Assert.assertTrue(matcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, matcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, matcher.getCurrentVectorSize()); + + // all are boolean with no valued column i guess + VectorValueMatcher anotherMatcher = matcherFactory.makeMatcher((String) null); + Assert.assertTrue(anotherMatcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, anotherMatcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, anotherMatcher.getCurrentVectorSize()); + EasyMock.verify(selector); + } + + @Test + public void testSingleValueStringOneCardinalityBooleanMatcherIfNullAndNameLookupPossible() + { + // single value string column with cardinality 1 and name lookup possible in advance uses boolean matcher for + // matches + SingleValueDimensionVectorSelector selector = + EasyMock.createMock(SingleValueDimensionVectorSelector.class); + EasyMock.expect(selector.getCurrentVectorSize()).andReturn(CURRENT_SIZE).anyTimes(); + EasyMock.expect(selector.getMaxVectorSize()).andReturn(VECTOR_SIZE).anyTimes(); + EasyMock.expect(selector.getValueCardinality()).andReturn(1).anyTimes(); + EasyMock.expect(selector.nameLookupPossibleInAdvance()).andReturn(true).anyTimes(); + EasyMock.expect(selector.lookupName(0)).andReturn(null).anyTimes(); + EasyMock.replay(selector); + + VectorValueMatcherFactory matcherFactory = + VectorValueMatcherColumnProcessorFactory.instance().makeSingleValueDimensionProcessor(selector); + + Assert.assertTrue(matcherFactory instanceof SingleValueStringVectorValueMatcher); + + // false matcher + VectorValueMatcher booleanMatcher = matcherFactory.makeMatcher("any value"); + Assert.assertTrue(booleanMatcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, booleanMatcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, booleanMatcher.getCurrentVectorSize()); + + // true matcher + VectorValueMatcher anotherBooleanMatcher = matcherFactory.makeMatcher((String) null); + Assert.assertTrue(anotherBooleanMatcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, anotherBooleanMatcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, anotherBooleanMatcher.getCurrentVectorSize()); + EasyMock.verify(selector); + } + + @Test + public void testSingleValueStringOneCardinalityBooleanMatcherIfNullAndNameLookupNotPossible() + { + // if name lookup not possible in advance, use normal path, even if cardinality 1 + IdLookup lookup = EasyMock.createMock(IdLookup.class); + SingleValueDimensionVectorSelector selector = + EasyMock.createMock(SingleValueDimensionVectorSelector.class); + EasyMock.expect(selector.getCurrentVectorSize()).andReturn(CURRENT_SIZE).anyTimes(); + EasyMock.expect(selector.getMaxVectorSize()).andReturn(VECTOR_SIZE).anyTimes(); + EasyMock.expect(selector.getValueCardinality()).andReturn(1).anyTimes(); + EasyMock.expect(selector.nameLookupPossibleInAdvance()).andReturn(false).anyTimes(); + EasyMock.expect(selector.idLookup()).andReturn(lookup).anyTimes(); + EasyMock.expect(lookup.lookupId("any value")).andReturn(1).anyTimes(); + EasyMock.expect(lookup.lookupId(null)).andReturn(0).anyTimes(); + EasyMock.replay(selector, lookup); + + VectorValueMatcherFactory matcherFactory = + VectorValueMatcherColumnProcessorFactory.instance().makeSingleValueDimensionProcessor(selector); + + Assert.assertTrue(matcherFactory instanceof SingleValueStringVectorValueMatcher); + + VectorValueMatcher matcher = matcherFactory.makeMatcher("any value"); + Assert.assertFalse(matcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, matcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, matcher.getCurrentVectorSize()); + EasyMock.verify(selector, lookup); + } + + @Test + public void testMultiValueString() + { + IdLookup lookup = EasyMock.createMock(IdLookup.class); + MultiValueDimensionVectorSelector selector = EasyMock.createMock(MultiValueDimensionVectorSelector.class); + EasyMock.expect(selector.getCurrentVectorSize()).andReturn(CURRENT_SIZE).anyTimes(); + EasyMock.expect(selector.getMaxVectorSize()).andReturn(VECTOR_SIZE).anyTimes(); + EasyMock.expect(selector.getValueCardinality()).andReturn(11).anyTimes(); + EasyMock.expect(selector.nameLookupPossibleInAdvance()).andReturn(false).anyTimes(); + EasyMock.expect(selector.idLookup()).andReturn(lookup).anyTimes(); + EasyMock.expect(lookup.lookupId("any value")).andReturn(-1).anyTimes(); + EasyMock.expect(lookup.lookupId(null)).andReturn(0).anyTimes(); + EasyMock.replay(selector, lookup); + VectorValueMatcherFactory matcherFactory = + VectorValueMatcherColumnProcessorFactory.instance().makeMultiValueDimensionProcessor(selector); + + Assert.assertTrue(matcherFactory instanceof MultiValueStringVectorValueMatcher); + + VectorValueMatcher valueNotExistMatcher = matcherFactory.makeMatcher("any value"); + Assert.assertTrue(valueNotExistMatcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, valueNotExistMatcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, valueNotExistMatcher.getCurrentVectorSize()); + + VectorValueMatcher valueExistMatcher = matcherFactory.makeMatcher((String) null); + Assert.assertFalse(valueExistMatcher instanceof BooleanVectorValueMatcher); + Assert.assertEquals(VECTOR_SIZE, valueExistMatcher.getMaxVectorSize()); + Assert.assertEquals(CURRENT_SIZE, valueExistMatcher.getCurrentVectorSize()); + EasyMock.verify(selector, lookup); + } +} diff --git a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java index 0417d4a430ba..6719ac622328 100644 --- a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java +++ b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java @@ -102,6 +102,7 @@ import org.apache.druid.query.filter.ExtractionDimFilter; import org.apache.druid.query.filter.InDimFilter; import org.apache.druid.query.filter.JavaScriptDimFilter; +import org.apache.druid.query.filter.NotDimFilter; import org.apache.druid.query.filter.OrDimFilter; import org.apache.druid.query.filter.RegexDimFilter; import org.apache.druid.query.filter.SearchQueryDimFilter; @@ -10439,6 +10440,35 @@ public void testTypeConversionWithMergingChainedExecutionRunner() TestHelper.assertExpectedObjects(expectedResults, results, "type-conversion"); } + @Test + public void testGroupByNoMatchingPrefilter() + { + GroupByQuery query = makeQueryBuilder() + .setDataSource(QueryRunnerTestHelper.DATA_SOURCE) + .setQuerySegmentSpec(QueryRunnerTestHelper.FIRST_TO_THIRD) + .setDimensions( + new DefaultDimensionSpec("quality", "quality") + ) + .setDimFilter(new SelectorDimFilter("market", "spot", null, null)) + .setAggregatorSpecs( + QueryRunnerTestHelper.ROWS_COUNT, + new FilteredAggregatorFactory( + new LongSumAggregatorFactory("index", "index"), + new NotDimFilter(new SelectorDimFilter("longNumericNull", null, null)) + ) + ) + .setGranularity(QueryRunnerTestHelper.DAY_GRAN) + .setLimit(1) + .build(); + + List expectedResults = ImmutableList.of( + makeRow(query, "2011-04-01", "quality", "automotive", "rows", 1L, "index", 135L) + ); + + Iterable results = GroupByQueryRunnerTestHelper.runQuery(factory, runner, query); + TestHelper.assertExpectedObjects(expectedResults, results, "groupBy"); + } + private static ResultRow makeRow(final GroupByQuery query, final String timestamp, final Object... vals) { return GroupByQueryRunnerTestHelper.createExpectedRow(query, timestamp, vals); From 37a52c7bcd4296a72730aaf1faaeb5909f713445 Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Tue, 9 Jun 2020 20:12:36 -0700 Subject: [PATCH 070/107] make joinables closeable (#9982) * make joinables closeable * tests and adjustments * refactor to make join stuffs impelement ReferenceCountedObject instead of Closable, more tests * fixes * javadocs and stuff * fix bugs * more test * fix lgtm alert * simplify * fixup javadoc * review stuffs * safeguard against exceptions * i hate this checkstyle rule * make IndexedTable extend Closeable --- .../benchmark/JoinAndLookupBenchmark.java | 9 +- .../indexer/ITTestCoordinatorPausedTest.java | 2 +- .../{indexer => query}/ITUnionQueryTest.java | 9 +- .../{indexer => queries}/union_queries.json | 0 .../org/apache/druid/query/BaseQuery.java | 2 +- .../ReferenceCountingSegmentQueryRunner.java | 22 +- .../apache/druid/segment/AbstractSegment.java | 18 +- .../segment/IncrementalIndexSegment.java | 2 +- .../druid/segment/QueryableIndexSegment.java | 2 +- .../druid/segment/ReferenceCountedObject.java | 46 ++++ .../ReferenceCountingCloseableObject.java | 144 ++++++++++++ .../segment/ReferenceCountingSegment.java | 131 ++--------- .../apache/druid/segment/RowBasedSegment.java | 2 +- .../org/apache/druid/segment/Segment.java | 11 +- ...enceCounter.java => SegmentReference.java} | 24 +- .../druid/segment/join/HashJoinSegment.java | 45 +++- .../apache/druid/segment/join/Joinable.java | 3 +- .../druid/segment/join/JoinableClause.java | 11 +- .../apache/druid/segment/join/Joinables.java | 4 +- .../segment/join/lookup/LookupJoinable.java | 8 + .../segment/join/table/IndexedTable.java | 10 +- .../join/table/IndexedTableJoinable.java | 7 + .../join/table/RowBasedIndexedTable.java | 26 ++- .../segment/ReferenceCountingSegmentTest.java | 46 +++- .../segment/join/HashJoinSegmentTest.java | 207 +++++++++++++++-- .../druid/segment/join/JoinTestHelper.java | 8 +- .../druid/segment/join/JoinablesTest.java | 8 +- .../join/table/IndexedTableJoinableTest.java | 4 +- .../join/table/RowBasedIndexedTableTest.java | 7 + .../druid/client/CachingClusteredClient.java | 2 +- .../segment/join/InlineJoinableFactory.java | 4 +- .../druid/segment/realtime/FireHydrant.java | 90 ++++++-- .../appenderator/AppenderatorImpl.java | 4 +- .../appenderator/SinkQuerySegmentWalker.java | 24 +- .../realtime/plumber/RealtimePlumber.java | 4 +- .../druid/server/LocalQuerySegmentWalker.java | 13 +- .../server/coordination/ServerManager.java | 11 +- .../loading/CacheTestSegmentLoader.java | 3 +- .../segment/realtime/FireHydrantTest.java | 213 ++++++++++++++++++ .../druid/server/SegmentManagerTest.java | 3 +- .../server/TestClusterQuerySegmentWalker.java | 7 +- .../coordination/ServerManagerTest.java | 3 +- .../druid/sql/calcite/rel/QueryMaker.java | 2 +- 43 files changed, 939 insertions(+), 262 deletions(-) rename integration-tests/src/test/java/org/apache/druid/tests/{indexer => query}/ITUnionQueryTest.java (96%) rename integration-tests/src/test/resources/{indexer => queries}/union_queries.json (100%) create mode 100644 processing/src/main/java/org/apache/druid/segment/ReferenceCountedObject.java create mode 100644 processing/src/main/java/org/apache/druid/segment/ReferenceCountingCloseableObject.java rename processing/src/main/java/org/apache/druid/segment/{ReferenceCounter.java => SegmentReference.java} (59%) create mode 100644 server/src/test/java/org/apache/druid/segment/realtime/FireHydrantTest.java diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java index cb630a2e400b..567a34842c90 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java @@ -38,6 +38,7 @@ import org.apache.druid.segment.DimensionSelector; import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.QueryableIndexSegment; +import org.apache.druid.segment.ReferenceCountingSegment; import org.apache.druid.segment.Segment; import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.column.ColumnConfig; @@ -150,7 +151,7 @@ public void setup() throws IOException 0 ); hashJoinLookupStringKeySegment = new HashJoinSegment( - baseSegment, + ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment), joinableClausesLookupStringKey, preAnalysisLookupStringKey ); @@ -177,7 +178,7 @@ public void setup() throws IOException 0 ); hashJoinLookupLongKeySegment = new HashJoinSegment( - baseSegment, + ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment), joinableClausesLookupLongKey, preAnalysisLookupLongKey ); @@ -204,7 +205,7 @@ public void setup() throws IOException 0 ); hashJoinIndexedTableStringKeySegment = new HashJoinSegment( - baseSegment, + ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment), joinableClausesIndexedTableStringKey, preAnalysisIndexedTableStringKey ); @@ -231,7 +232,7 @@ public void setup() throws IOException 0 ); hashJoinIndexedTableLongKeySegment = new HashJoinSegment( - baseSegment, + ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment), joinableClausesIndexedTableLonggKey, preAnalysisIndexedTableLongKey ); diff --git a/integration-tests/src/test/java/org/apache/druid/tests/indexer/ITTestCoordinatorPausedTest.java b/integration-tests/src/test/java/org/apache/druid/tests/indexer/ITTestCoordinatorPausedTest.java index 7d34b47d7065..269c74d4cd19 100644 --- a/integration-tests/src/test/java/org/apache/druid/tests/indexer/ITTestCoordinatorPausedTest.java +++ b/integration-tests/src/test/java/org/apache/druid/tests/indexer/ITTestCoordinatorPausedTest.java @@ -34,7 +34,7 @@ @Guice(moduleFactory = DruidTestModuleFactory.class) public class ITTestCoordinatorPausedTest extends AbstractITBatchIndexTest { - private static final Logger LOG = new Logger(ITUnionQueryTest.class); + private static final Logger LOG = new Logger(ITTestCoordinatorPausedTest.class); private static final String INDEX_DATASOURCE = "wikipedia_index_test"; private static final String INDEX_TASK = "/indexer/wikipedia_index_task.json"; private static final String INDEX_QUERIES_RESOURCE = "/indexer/wikipedia_index_queries.json"; diff --git a/integration-tests/src/test/java/org/apache/druid/tests/indexer/ITUnionQueryTest.java b/integration-tests/src/test/java/org/apache/druid/tests/query/ITUnionQueryTest.java similarity index 96% rename from integration-tests/src/test/java/org/apache/druid/tests/indexer/ITUnionQueryTest.java rename to integration-tests/src/test/java/org/apache/druid/tests/query/ITUnionQueryTest.java index 4bf68ad8834c..c720b1fab5be 100644 --- a/integration-tests/src/test/java/org/apache/druid/tests/indexer/ITUnionQueryTest.java +++ b/integration-tests/src/test/java/org/apache/druid/tests/query/ITUnionQueryTest.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.druid.tests.indexer; +package org.apache.druid.tests.query; import com.google.inject.Inject; import org.apache.commons.io.IOUtils; @@ -39,6 +39,8 @@ import org.apache.druid.testing.utils.ITRetryUtil; import org.apache.druid.testing.utils.ServerDiscoveryUtil; import org.apache.druid.tests.TestNGGroup; +import org.apache.druid.tests.indexer.AbstractITBatchIndexTest; +import org.apache.druid.tests.indexer.AbstractIndexerTest; import org.jboss.netty.handler.codec.http.HttpMethod; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.joda.time.DateTime; @@ -62,7 +64,7 @@ public class ITUnionQueryTest extends AbstractIndexerTest private static final String UNION_TASK_RESOURCE = "/indexer/wikipedia_union_index_task.json"; private static final String EVENT_RECEIVER_SERVICE_PREFIX = "eventReceiverServiceName"; private static final String UNION_DATA_FILE = "/data/union_query/wikipedia_index_data.json"; - private static final String UNION_QUERIES_RESOURCE = "/indexer/union_queries.json"; + private static final String UNION_QUERIES_RESOURCE = "/queries/union_queries.json"; private static final String UNION_DATASOURCE = "wikipedia_index_test"; @Inject @@ -92,7 +94,7 @@ public void testUnionQuery() throws IOException closer.register(unloader(fullDatasourceName + i)); } try { - // Load 4 datasources with same dimensions + // Load 3 datasources with same dimensions String task = setShutOffTime( getResourceAsString(UNION_TASK_RESOURCE), DateTimes.utc(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(3)) @@ -117,6 +119,7 @@ public void testUnionQuery() throws IOException () -> { for (int i = 0; i < numTasks; i++) { final int countRows = queryHelper.countRows(fullDatasourceName + i, "2013-08-31/2013-09-01"); + // there are 10 rows, but query only covers the first 5 if (countRows < 5) { LOG.warn("%d events have been ingested to %s so far", countRows, fullDatasourceName + i); return false; diff --git a/integration-tests/src/test/resources/indexer/union_queries.json b/integration-tests/src/test/resources/queries/union_queries.json similarity index 100% rename from integration-tests/src/test/resources/indexer/union_queries.json rename to integration-tests/src/test/resources/queries/union_queries.json diff --git a/processing/src/main/java/org/apache/druid/query/BaseQuery.java b/processing/src/main/java/org/apache/druid/query/BaseQuery.java index f95961cf40a6..68be7ba48fd0 100644 --- a/processing/src/main/java/org/apache/druid/query/BaseQuery.java +++ b/processing/src/main/java/org/apache/druid/query/BaseQuery.java @@ -123,7 +123,7 @@ public static QuerySegmentSpec getQuerySegmentSpecForLookUp(BaseQuery query) { return DataSourceAnalysis.forDataSource(query.getDataSource()) .getBaseQuerySegmentSpec() - .orElse(query.getQuerySegmentSpec()); + .orElseGet(query::getQuerySegmentSpec); } @Override diff --git a/processing/src/main/java/org/apache/druid/query/ReferenceCountingSegmentQueryRunner.java b/processing/src/main/java/org/apache/druid/query/ReferenceCountingSegmentQueryRunner.java index 8691441ec201..a92bbd6421b7 100644 --- a/processing/src/main/java/org/apache/druid/query/ReferenceCountingSegmentQueryRunner.java +++ b/processing/src/main/java/org/apache/druid/query/ReferenceCountingSegmentQueryRunner.java @@ -22,50 +22,42 @@ import org.apache.druid.java.util.common.guava.Sequence; import org.apache.druid.java.util.common.guava.Sequences; import org.apache.druid.query.context.ResponseContext; -import org.apache.druid.segment.ReferenceCounter; -import org.apache.druid.segment.Segment; +import org.apache.druid.segment.SegmentReference; public class ReferenceCountingSegmentQueryRunner implements QueryRunner { private final QueryRunnerFactory> factory; - private final Segment segment; - private final ReferenceCounter segmentReferenceCounter; + private final SegmentReference segment; private final SegmentDescriptor descriptor; public ReferenceCountingSegmentQueryRunner( QueryRunnerFactory> factory, - Segment segment, - ReferenceCounter segmentReferenceCounter, + SegmentReference segment, SegmentDescriptor descriptor ) { this.factory = factory; this.segment = segment; - this.segmentReferenceCounter = segmentReferenceCounter; this.descriptor = descriptor; } @Override public Sequence run(final QueryPlus queryPlus, ResponseContext responseContext) { - if (segmentReferenceCounter.increment()) { + return segment.acquireReferences().map(closeable -> { try { final Sequence baseSequence = factory.createRunner(segment).run(queryPlus, responseContext); - - return Sequences.withBaggage(baseSequence, segmentReferenceCounter.decrementOnceCloseable()); + return Sequences.withBaggage(baseSequence, closeable); } catch (Throwable t) { try { - segmentReferenceCounter.decrement(); + closeable.close(); } catch (Exception e) { t.addSuppressed(e); } throw t; } - } else { - // Segment was closed before we had a chance to increment the reference count - return new ReportTimelineMissingSegmentQueryRunner(descriptor).run(queryPlus, responseContext); - } + }).orElseGet(() -> new ReportTimelineMissingSegmentQueryRunner(descriptor).run(queryPlus, responseContext)); } } diff --git a/processing/src/main/java/org/apache/druid/segment/AbstractSegment.java b/processing/src/main/java/org/apache/druid/segment/AbstractSegment.java index 1518c44dcf07..c41be41ec4d9 100644 --- a/processing/src/main/java/org/apache/druid/segment/AbstractSegment.java +++ b/processing/src/main/java/org/apache/druid/segment/AbstractSegment.java @@ -19,19 +19,11 @@ package org.apache.druid.segment; -import javax.annotation.Nullable; - +/** + * @deprecated use {@link Segment} directly as this does nothing + */ +@Deprecated public abstract class AbstractSegment implements Segment { - @Override - @Nullable - public T as(Class clazz) - { - if (clazz.equals(QueryableIndex.class)) { - return (T) asQueryableIndex(); - } else if (clazz.equals(StorageAdapter.class)) { - return (T) asStorageAdapter(); - } - return null; - } + // i used to have a purpose } diff --git a/processing/src/main/java/org/apache/druid/segment/IncrementalIndexSegment.java b/processing/src/main/java/org/apache/druid/segment/IncrementalIndexSegment.java index 683106cf3871..b270b54c4d1d 100644 --- a/processing/src/main/java/org/apache/druid/segment/IncrementalIndexSegment.java +++ b/processing/src/main/java/org/apache/druid/segment/IncrementalIndexSegment.java @@ -26,7 +26,7 @@ /** */ -public class IncrementalIndexSegment extends AbstractSegment +public class IncrementalIndexSegment implements Segment { private final IncrementalIndex index; private final SegmentId segmentId; diff --git a/processing/src/main/java/org/apache/druid/segment/QueryableIndexSegment.java b/processing/src/main/java/org/apache/druid/segment/QueryableIndexSegment.java index 30b3fcba4df9..a829dfae184b 100644 --- a/processing/src/main/java/org/apache/druid/segment/QueryableIndexSegment.java +++ b/processing/src/main/java/org/apache/druid/segment/QueryableIndexSegment.java @@ -24,7 +24,7 @@ /** */ -public class QueryableIndexSegment extends AbstractSegment +public class QueryableIndexSegment implements Segment { private final QueryableIndex index; private final QueryableIndexStorageAdapter storageAdapter; diff --git a/processing/src/main/java/org/apache/druid/segment/ReferenceCountedObject.java b/processing/src/main/java/org/apache/druid/segment/ReferenceCountedObject.java new file mode 100644 index 000000000000..4770743f3bbe --- /dev/null +++ b/processing/src/main/java/org/apache/druid/segment/ReferenceCountedObject.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment; + +import java.io.Closeable; +import java.util.Optional; + +/** + * Interface for an object that may have a reference acquired in the form of a {@link Closeable}. This is intended to be + * used with an implementation of {@link ReferenceCountingCloseableObject}, or anything else that wishes to provide + * a method to account for the acquire and release of a reference to the object. + */ +public interface ReferenceCountedObject +{ + /** + * This method is expected to increment a reference count and provide a {@link Closeable} that decrements the + * reference count when closed. This is likely just a wrapper around + * {@link ReferenceCountingCloseableObject#incrementReferenceAndDecrementOnceCloseable()}, but may also include any + * other associated references which should be incremented when this method is called, and decremented/released by the + * closeable. + * + * IMPORTANT NOTE: to fulfill the contract of this method, implementors must return a closeable to indicate that the + * reference can be acquired, even if there is nothing to close. Implementors should avoid allowing this method or the + * {@link Closeable} it creates to throw exceptions. + * + * For callers: if this method returns non-empty, IT MUST BE CLOSED, else reference counts can potentially leak. + */ + Optional acquireReferences(); +} diff --git a/processing/src/main/java/org/apache/druid/segment/ReferenceCountingCloseableObject.java b/processing/src/main/java/org/apache/druid/segment/ReferenceCountingCloseableObject.java new file mode 100644 index 000000000000..db37fc595684 --- /dev/null +++ b/processing/src/main/java/org/apache/druid/segment/ReferenceCountingCloseableObject.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment; + +import org.apache.druid.java.util.common.io.Closer; +import org.apache.druid.java.util.common.logger.Logger; + +import java.io.Closeable; +import java.util.Optional; +import java.util.concurrent.Phaser; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * ReferenceCountingCloseableObject implements something like automatic reference count-based resource management, + * backed by a {@link Phaser}. + * + * ReferenceCountingCloseableObject allows consumers to call {@link #close()} before some other "users", which called + * {@link #increment()} or {@link #incrementReferenceAndDecrementOnceCloseable()}, but have not called + * {@link #decrement()} yet or the closer for {@link #incrementReferenceAndDecrementOnceCloseable()}, and the wrapped + * object won't be actually closed until that all references are released. + */ +public abstract class ReferenceCountingCloseableObject implements Closeable +{ + private static final Logger log = new Logger(ReferenceCountingCloseableObject.class); + + private final AtomicBoolean closed = new AtomicBoolean(false); + private final Phaser referents = new Phaser(1) + { + @Override + protected boolean onAdvance(int phase, int registeredParties) + { + // Ensure that onAdvance() doesn't throw exception, otherwise termination won't happen + if (registeredParties != 0) { + log.error("registeredParties[%s] is not 0", registeredParties); + } + try { + baseObject.close(); + } + catch (Exception e) { + try { + log.error(e, "Exception while closing reference counted object[%s]", baseObject); + } + catch (Exception e2) { + // ignore + } + } + // Always terminate. + return true; + } + }; + + protected final BaseObject baseObject; + + public ReferenceCountingCloseableObject(BaseObject object) + { + this.baseObject = object; + } + + public int getNumReferences() + { + return Math.max(referents.getRegisteredParties() - 1, 0); + } + + public boolean isClosed() + { + return referents.isTerminated(); + } + + /** + * Increment the reference count by one. + */ + public boolean increment() + { + // Negative return from referents.register() means the Phaser is terminated. + return referents.register() >= 0; + } + + /** + * Decrement the reference count by one. + */ + public void decrement() + { + referents.arriveAndDeregister(); + } + + /** + * Returns an {@link Optional} of a {@link Closeable} from {@link #decrementOnceCloseable}, if it is able to + * successfully {@link #increment}, else nothing indicating that the reference could not be acquired. + */ + public Optional incrementReferenceAndDecrementOnceCloseable() + { + final Closer closer; + if (increment()) { + closer = Closer.create(); + closer.register(decrementOnceCloseable()); + } else { + closer = null; + } + return Optional.ofNullable(closer); + } + + /** + * Returns a {@link Closeable} which action is to call {@link #decrement()} only once. If close() is called on the + * returned Closeable object for the second time, it won't call {@link #decrement()} again. + */ + public Closeable decrementOnceCloseable() + { + AtomicBoolean decremented = new AtomicBoolean(false); + return () -> { + if (decremented.compareAndSet(false, true)) { + decrement(); + } else { + log.warn("close() is called more than once on ReferenceCountingCloseableObject.decrementOnceCloseable()"); + } + }; + } + + @Override + public void close() + { + if (closed.compareAndSet(false, true)) { + referents.arriveAndDeregister(); + } else { + log.warn("close() is called more than once on ReferenceCountingCloseableObject"); + } + } +} diff --git a/processing/src/main/java/org/apache/druid/segment/ReferenceCountingSegment.java b/processing/src/main/java/org/apache/druid/segment/ReferenceCountingSegment.java index ba1c34318e33..5b531316cd5a 100644 --- a/processing/src/main/java/org/apache/druid/segment/ReferenceCountingSegment.java +++ b/processing/src/main/java/org/apache/druid/segment/ReferenceCountingSegment.java @@ -20,7 +20,6 @@ package org.apache.druid.segment; import com.google.common.base.Preconditions; -import org.apache.druid.java.util.emitter.EmittingLogger; import org.apache.druid.timeline.Overshadowable; import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.partition.ShardSpec; @@ -28,50 +27,20 @@ import javax.annotation.Nullable; import java.io.Closeable; -import java.util.concurrent.Phaser; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.Optional; /** - * ReferenceCountingSegment allows to call {@link #close()} before some other "users", which called {@link - * #increment()}, has not called {@link #decrement()} yet, and the wrapped {@link Segment} won't be actually closed - * until that. So ReferenceCountingSegment implements something like automatic reference count-based resource - * management. + * {@link Segment} that is also a {@link ReferenceCountingSegment}, allowing query engines that operate directly on + * segments to track references so that dropping a {@link Segment} can be done safely to ensure there are no in-flight + * queries. */ -public class ReferenceCountingSegment extends AbstractSegment - implements Overshadowable, ReferenceCounter +public class ReferenceCountingSegment extends ReferenceCountingCloseableObject + implements SegmentReference, Overshadowable { - private static final EmittingLogger log = new EmittingLogger(ReferenceCountingSegment.class); - - private final Segment baseSegment; private final short startRootPartitionId; private final short endRootPartitionId; private final short minorVersion; private final short atomicUpdateGroupSize; - private final AtomicBoolean closed = new AtomicBoolean(false); - private final Phaser referents = new Phaser(1) - { - @Override - protected boolean onAdvance(int phase, int registeredParties) - { - // Ensure that onAdvance() doesn't throw exception, otherwise termination won't happen - if (registeredParties != 0) { - log.error("registeredParties[%s] is not 0", registeredParties); - } - try { - baseSegment.close(); - } - catch (Exception e) { - try { - log.error(e, "Exception while closing segment[%s]", baseSegment.getId()); - } - catch (Exception e2) { - // ignore - } - } - // Always terminate. - return true; - } - }; public static ReferenceCountingSegment wrapRootGenerationSegment(Segment baseSegment) { @@ -106,7 +75,7 @@ private ReferenceCountingSegment( short atomicUpdateGroupSize ) { - this.baseSegment = baseSegment; + super(baseSegment); this.startRootPartitionId = (short) startRootPartitionId; this.endRootPartitionId = (short) endRootPartitionId; this.minorVersion = minorVersion; @@ -116,105 +85,43 @@ private ReferenceCountingSegment( @Nullable public Segment getBaseSegment() { - return !isClosed() ? baseSegment : null; - } - - public int getNumReferences() - { - return Math.max(referents.getRegisteredParties() - 1, 0); - } - - public boolean isClosed() - { - return referents.isTerminated(); + return !isClosed() ? baseObject : null; } @Override @Nullable public SegmentId getId() { - return !isClosed() ? baseSegment.getId() : null; + return !isClosed() ? baseObject.getId() : null; } @Override @Nullable public Interval getDataInterval() { - return !isClosed() ? baseSegment.getDataInterval() : null; + return !isClosed() ? baseObject.getDataInterval() : null; } @Override @Nullable public QueryableIndex asQueryableIndex() { - return !isClosed() ? baseSegment.asQueryableIndex() : null; + return !isClosed() ? baseObject.asQueryableIndex() : null; } @Override @Nullable public StorageAdapter asStorageAdapter() { - return !isClosed() ? baseSegment.asStorageAdapter() : null; - } - - @Override - public void close() - { - if (closed.compareAndSet(false, true)) { - referents.arriveAndDeregister(); - } else { - log.warn("close() is called more than once on ReferenceCountingSegment"); - } - } - - public ReferenceCounter referenceCounter() - { - return this; - } - - @Override - public boolean increment() - { - // Negative return from referents.register() means the Phaser is terminated. - return referents.register() >= 0; - } - - /** - * Returns a {@link Closeable} which action is to call {@link #decrement()} only once. If close() is called on the - * returned Closeable object for the second time, it won't call {@link #decrement()} again. - */ - @Override - public Closeable decrementOnceCloseable() - { - AtomicBoolean decremented = new AtomicBoolean(false); - return () -> { - if (decremented.compareAndSet(false, true)) { - decrement(); - } else { - log.warn("close() is called more than once on ReferenceCountingSegment.decrementOnceCloseable()"); - } - }; - } - - @Override - public void decrement() - { - referents.arriveAndDeregister(); - } - - @Override - public T as(Class clazz) - { - return getBaseSegment().as(clazz); + return !isClosed() ? baseObject.asStorageAdapter() : null; } @Override public boolean overshadows(ReferenceCountingSegment other) { - if (baseSegment.getId().getDataSource().equals(other.baseSegment.getId().getDataSource()) - && baseSegment.getId().getInterval().overlaps(other.baseSegment.getId().getInterval())) { - final int majorVersionCompare = baseSegment.getId().getVersion() - .compareTo(other.baseSegment.getId().getVersion()); + if (baseObject.getId().getDataSource().equals(other.baseObject.getId().getDataSource()) + && baseObject.getId().getInterval().overlaps(other.baseObject.getId().getInterval())) { + final int majorVersionCompare = baseObject.getId().getVersion().compareTo(other.baseObject.getId().getVersion()); if (majorVersionCompare > 0) { return true; } else if (majorVersionCompare == 0) { @@ -245,7 +152,7 @@ public int getEndRootPartitionId() @Override public String getVersion() { - return baseSegment.getId().getVersion(); + return baseObject.getId().getVersion(); } @Override @@ -259,4 +166,10 @@ public short getAtomicUpdateGroupSize() { return atomicUpdateGroupSize; } + + @Override + public Optional acquireReferences() + { + return incrementReferenceAndDecrementOnceCloseable(); + } } diff --git a/processing/src/main/java/org/apache/druid/segment/RowBasedSegment.java b/processing/src/main/java/org/apache/druid/segment/RowBasedSegment.java index 512cbc22a0ce..36e84e8bd0e9 100644 --- a/processing/src/main/java/org/apache/druid/segment/RowBasedSegment.java +++ b/processing/src/main/java/org/apache/druid/segment/RowBasedSegment.java @@ -30,7 +30,7 @@ /** * A {@link Segment} that is based on a stream of objects. */ -public class RowBasedSegment extends AbstractSegment +public class RowBasedSegment implements Segment { private final SegmentId segmentId; private final StorageAdapter storageAdapter; diff --git a/processing/src/main/java/org/apache/druid/segment/Segment.java b/processing/src/main/java/org/apache/druid/segment/Segment.java index 8fb9b4e620dc..245c776b0aca 100644 --- a/processing/src/main/java/org/apache/druid/segment/Segment.java +++ b/processing/src/main/java/org/apache/druid/segment/Segment.java @@ -57,6 +57,15 @@ public interface Segment extends Closeable * @param desired interface * @return instance of clazz, or null if the interface is not supported by this segment */ + @SuppressWarnings("unused") @Nullable - T as(Class clazz); + default T as(Class clazz) + { + if (clazz.equals(QueryableIndex.class)) { + return (T) asQueryableIndex(); + } else if (clazz.equals(StorageAdapter.class)) { + return (T) asStorageAdapter(); + } + return null; + } } diff --git a/processing/src/main/java/org/apache/druid/segment/ReferenceCounter.java b/processing/src/main/java/org/apache/druid/segment/SegmentReference.java similarity index 59% rename from processing/src/main/java/org/apache/druid/segment/ReferenceCounter.java rename to processing/src/main/java/org/apache/druid/segment/SegmentReference.java index 970d487b7703..fae2a7b36f65 100644 --- a/processing/src/main/java/org/apache/druid/segment/ReferenceCounter.java +++ b/processing/src/main/java/org/apache/druid/segment/SegmentReference.java @@ -19,26 +19,12 @@ package org.apache.druid.segment; -import java.io.Closeable; - /** - * An interface to reference-counted objects. Used by {@link ReferenceCountingSegment}. Thread-safe. + * A {@link Segment} with a associated references, such as {@link ReferenceCountingSegment} where the reference is + * the segment itself, and {@link org.apache.druid.segment.join.HashJoinSegment} which wraps a + * {@link ReferenceCountingSegment} and also includes the associated list of + * {@link org.apache.druid.segment.join.JoinableClause} */ -public interface ReferenceCounter +public interface SegmentReference extends Segment, ReferenceCountedObject { - /** - * Increment the reference count by one. - */ - boolean increment(); - - /** - * Returns a {@link Closeable} which action is to call {@link #decrement()} only once. If close() is called on the - * returned Closeable object for the second time, it won't call {@link #decrement()} again. - */ - Closeable decrementOnceCloseable(); - - /** - * Decrement the reference count by one. - */ - void decrement(); } diff --git a/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegment.java b/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegment.java index d321af958008..3ca240604da6 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegment.java +++ b/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegment.java @@ -20,26 +20,29 @@ package org.apache.druid.segment.join; import org.apache.druid.java.util.common.IAE; -import org.apache.druid.segment.AbstractSegment; +import org.apache.druid.java.util.common.guava.CloseQuietly; +import org.apache.druid.java.util.common.io.Closer; import org.apache.druid.segment.QueryableIndex; -import org.apache.druid.segment.Segment; +import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.StorageAdapter; import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; import org.apache.druid.timeline.SegmentId; import org.joda.time.Interval; import javax.annotation.Nullable; +import java.io.Closeable; import java.io.IOException; import java.util.List; +import java.util.Optional; /** * Represents a deep, left-heavy join of a left-hand side baseSegment onto a series of right-hand side clauses. * * In other words, logically the operation is: join(join(join(baseSegment, clauses[0]), clauses[1]), clauses[2]) etc. */ -public class HashJoinSegment extends AbstractSegment +public class HashJoinSegment implements SegmentReference { - private final Segment baseSegment; + private final SegmentReference baseSegment; private final List clauses; private final JoinFilterPreAnalysis joinFilterPreAnalysis; @@ -50,7 +53,7 @@ public class HashJoinSegment extends AbstractSegment * @param joinFilterPreAnalysis Pre-analysis computed by {@link org.apache.druid.segment.join.filter.JoinFilterAnalyzer#computeJoinFilterPreAnalysis} */ public HashJoinSegment( - Segment baseSegment, + SegmentReference baseSegment, List clauses, JoinFilterPreAnalysis joinFilterPreAnalysis ) @@ -98,4 +101,36 @@ public void close() throws IOException { baseSegment.close(); } + + @Override + public Optional acquireReferences() + { + Closer closer = Closer.create(); + try { + boolean acquireFailed = baseSegment.acquireReferences().map(closeable -> { + closer.register(closeable); + return false; + }).orElse(true); + + for (JoinableClause joinClause : clauses) { + if (acquireFailed) { + break; + } + acquireFailed |= joinClause.acquireReferences().map(closeable -> { + closer.register(closeable); + return false; + }).orElse(true); + } + if (acquireFailed) { + CloseQuietly.close(closer); + return Optional.empty(); + } else { + return Optional.of(closer); + } + } + catch (Exception ex) { + CloseQuietly.close(closer); + return Optional.empty(); + } + } } diff --git a/processing/src/main/java/org/apache/druid/segment/join/Joinable.java b/processing/src/main/java/org/apache/druid/segment/join/Joinable.java index 001a0fccd21e..7ad7799a1099 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/Joinable.java +++ b/processing/src/main/java/org/apache/druid/segment/join/Joinable.java @@ -20,6 +20,7 @@ package org.apache.druid.segment.join; import org.apache.druid.segment.ColumnSelectorFactory; +import org.apache.druid.segment.ReferenceCountedObject; import org.apache.druid.segment.column.ColumnCapabilities; import javax.annotation.Nullable; @@ -33,7 +34,7 @@ * This class's most important method is {@link #makeJoinMatcher}. Its main user is * {@link HashJoinEngine#makeJoinCursor}. */ -public interface Joinable +public interface Joinable extends ReferenceCountedObject { int CARDINALITY_UNKNOWN = -1; diff --git a/processing/src/main/java/org/apache/druid/segment/join/JoinableClause.java b/processing/src/main/java/org/apache/druid/segment/join/JoinableClause.java index a2ddefe1bf0a..2f8bd3ac322f 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/JoinableClause.java +++ b/processing/src/main/java/org/apache/druid/segment/join/JoinableClause.java @@ -21,9 +21,12 @@ import com.google.common.base.Preconditions; import org.apache.druid.java.util.common.IAE; +import org.apache.druid.segment.ReferenceCountedObject; +import java.io.Closeable; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; /** @@ -33,7 +36,7 @@ * * Created from {@link org.apache.druid.query.planning.PreJoinableClause} by {@link Joinables#createSegmentMapFn}. */ -public class JoinableClause +public class JoinableClause implements ReferenceCountedObject { private final String prefix; private final Joinable joinable; @@ -151,4 +154,10 @@ public String toString() ", condition=" + condition + '}'; } + + @Override + public Optional acquireReferences() + { + return joinable.acquireReferences(); + } } diff --git a/processing/src/main/java/org/apache/druid/segment/join/Joinables.java b/processing/src/main/java/org/apache/druid/segment/join/Joinables.java index 76bd19e877d2..c9a9d2fc2478 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/Joinables.java +++ b/processing/src/main/java/org/apache/druid/segment/join/Joinables.java @@ -22,7 +22,7 @@ import org.apache.druid.java.util.common.IAE; import org.apache.druid.query.filter.Filter; import org.apache.druid.query.planning.PreJoinableClause; -import org.apache.druid.segment.Segment; +import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.column.ColumnHolder; import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; @@ -88,7 +88,7 @@ public static boolean isPrefixedBy(final String columnName, final String prefix) * @param originalFilter The original filter from the query. * @param virtualColumns The virtual columns from the query. */ - public static Function createSegmentMapFn( + public static Function createSegmentMapFn( final List clauses, final JoinableFactory joinableFactory, final AtomicLong cpuTimeAccumulator, diff --git a/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinable.java b/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinable.java index 7fae4e0db3b0..9321a184ebbc 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinable.java +++ b/processing/src/main/java/org/apache/druid/segment/join/lookup/LookupJoinable.java @@ -31,6 +31,7 @@ import org.apache.druid.segment.join.Joinable; import javax.annotation.Nullable; +import java.io.Closeable; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -123,4 +124,11 @@ public Optional> getCorrelatedColumnValues( } return Optional.of(correlatedValues); } + + @Override + public Optional acquireReferences() + { + // nothing to close for lookup joinables, they are managed externally and have no per query accounting of usage + return Optional.of(() -> {}); + } } diff --git a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTable.java b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTable.java index cbc858112f30..b47214487299 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTable.java +++ b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTable.java @@ -20,9 +20,11 @@ package org.apache.druid.segment.join.table; import it.unimi.dsi.fastutil.ints.IntList; +import org.apache.druid.segment.ReferenceCountedObject; import org.apache.druid.segment.column.RowSignature; import javax.annotation.Nullable; +import java.io.Closeable; import java.util.Set; /** @@ -30,8 +32,14 @@ * * The main user of this class is {@link IndexedTableJoinable}, and its main purpose is to participate in joins. */ -public interface IndexedTable +public interface IndexedTable extends ReferenceCountedObject, Closeable { + /** + * Returns the version of this table, used to compare against when loading a new version of the table + */ + @SuppressWarnings("unused") + String version(); + /** * Returns the columns of this table that have indexes. */ diff --git a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinable.java b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinable.java index 66ef1213d6f5..47166793ed20 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinable.java +++ b/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinable.java @@ -27,6 +27,7 @@ import org.apache.druid.segment.join.Joinable; import javax.annotation.Nullable; +import java.io.Closeable; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -133,4 +134,10 @@ public Optional> getCorrelatedColumnValues( return Optional.of(correlatedValues); } } + + @Override + public Optional acquireReferences() + { + return table.acquireReferences(); + } } diff --git a/processing/src/main/java/org/apache/druid/segment/join/table/RowBasedIndexedTable.java b/processing/src/main/java/org/apache/druid/segment/join/table/RowBasedIndexedTable.java index 87ac7d48e17f..c18ec6a44b7f 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/table/RowBasedIndexedTable.java +++ b/processing/src/main/java/org/apache/druid/segment/join/table/RowBasedIndexedTable.java @@ -30,11 +30,13 @@ import org.apache.druid.segment.column.RowSignature; import org.apache.druid.segment.column.ValueType; +import java.io.Closeable; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -50,12 +52,14 @@ public class RowBasedIndexedTable implements IndexedTable private final RowSignature rowSignature; private final List> columnFunctions; private final Set keyColumns; + private final String version; public RowBasedIndexedTable( final List table, final RowAdapter rowAdapter, final RowSignature rowSignature, - final Set keyColumns + final Set keyColumns, + final String version ) { this.table = table; @@ -63,6 +67,7 @@ public RowBasedIndexedTable( this.columnFunctions = rowSignature.getColumnNames().stream().map(rowAdapter::columnFunction).collect(Collectors.toList()); this.keyColumns = keyColumns; + this.version = version; if (new HashSet<>(keyColumns).size() != keyColumns.size()) { throw new ISE("keyColumns[%s] must not contain duplicates", keyColumns); @@ -106,6 +111,12 @@ public RowBasedIndexedTable( } } + @Override + public String version() + { + return version; + } + @Override public Set keyColumns() { @@ -163,4 +174,17 @@ public int numRows() { return table.size(); } + + @Override + public Optional acquireReferences() + { + // nothing to close by default, whatever loaded this thing (probably) lives on heap + return Optional.of(() -> {}); + } + + @Override + public void close() + { + // nothing to close + } } diff --git a/processing/src/test/java/org/apache/druid/segment/ReferenceCountingSegmentTest.java b/processing/src/test/java/org/apache/druid/segment/ReferenceCountingSegmentTest.java index cee0a46e2896..6566592e9d5c 100644 --- a/processing/src/test/java/org/apache/druid/segment/ReferenceCountingSegmentTest.java +++ b/processing/src/test/java/org/apache/druid/segment/ReferenceCountingSegmentTest.java @@ -21,6 +21,7 @@ import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.timeline.SegmentId; +import org.easymock.EasyMock; import org.joda.time.Days; import org.joda.time.Interval; import org.junit.Assert; @@ -38,39 +39,50 @@ public class ReferenceCountingSegmentTest private ReferenceCountingSegment segment; private ExecutorService exec; + private final SegmentId segmentId = SegmentId.dummy("test_segment"); + private final Interval dataInterval = new Interval(DateTimes.nowUtc().minus(Days.days(1)), DateTimes.nowUtc()); + private QueryableIndex index; + private StorageAdapter adapter; + private int underlyingSegmentClosedCount; + @Before public void setUp() { + underlyingSegmentClosedCount = 0; + index = EasyMock.createNiceMock(QueryableIndex.class); + adapter = EasyMock.createNiceMock(StorageAdapter.class); + segment = ReferenceCountingSegment.wrapRootGenerationSegment( - new AbstractSegment() + new Segment() { @Override public SegmentId getId() { - return SegmentId.dummy("test_segment"); + return segmentId; } @Override public Interval getDataInterval() { - return new Interval(DateTimes.nowUtc().minus(Days.days(1)), DateTimes.nowUtc()); + return dataInterval; } @Override public QueryableIndex asQueryableIndex() { - return null; + return index; } @Override public StorageAdapter asStorageAdapter() { - return null; + return adapter; } @Override public void close() { + underlyingSegmentClosedCount++; } } ); @@ -81,13 +93,17 @@ public void close() @Test public void testMultipleClose() throws Exception { + Assert.assertEquals(0, underlyingSegmentClosedCount); Assert.assertFalse(segment.isClosed()); Assert.assertTrue(segment.increment()); Assert.assertEquals(1, segment.getNumReferences()); Closeable closeable = segment.decrementOnceCloseable(); + Assert.assertEquals(0, underlyingSegmentClosedCount); closeable.close(); + Assert.assertEquals(0, underlyingSegmentClosedCount); closeable.close(); + Assert.assertEquals(0, underlyingSegmentClosedCount); exec.submit( () -> { try { @@ -99,10 +115,16 @@ public void testMultipleClose() throws Exception } ).get(); Assert.assertEquals(0, segment.getNumReferences()); + Assert.assertEquals(0, underlyingSegmentClosedCount); Assert.assertFalse(segment.isClosed()); + // close for reals segment.close(); + Assert.assertTrue(segment.isClosed()); + Assert.assertEquals(1, underlyingSegmentClosedCount); + // ... but make sure it only happens once segment.close(); + Assert.assertEquals(1, underlyingSegmentClosedCount); exec.submit( () -> { try { @@ -116,13 +138,25 @@ public void testMultipleClose() throws Exception Assert.assertEquals(0, segment.getNumReferences()); Assert.assertTrue(segment.isClosed()); + Assert.assertEquals(1, underlyingSegmentClosedCount); segment.increment(); segment.increment(); segment.increment(); Assert.assertEquals(0, segment.getNumReferences()); - + Assert.assertEquals(1, underlyingSegmentClosedCount); segment.close(); Assert.assertEquals(0, segment.getNumReferences()); + Assert.assertEquals(1, underlyingSegmentClosedCount); } + + @Test + public void testExposesWrappedSegment() + { + Assert.assertEquals(segmentId, segment.getId()); + Assert.assertEquals(dataInterval, segment.getDataInterval()); + Assert.assertEquals(index, segment.asQueryableIndex()); + Assert.assertEquals(adapter, segment.asStorageAdapter()); + } + } diff --git a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java index 3ff19e4d5918..966d62023ffe 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java @@ -20,29 +20,37 @@ package org.apache.druid.segment.join; import com.google.common.collect.ImmutableList; -import org.apache.druid.common.config.NullHandling; +import org.apache.druid.java.util.common.io.Closer; import org.apache.druid.math.expr.ExprMacroTable; import org.apache.druid.query.QueryContexts; +import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.QueryableIndexSegment; +import org.apache.druid.segment.ReferenceCountingSegment; +import org.apache.druid.segment.SegmentReference; +import org.apache.druid.segment.StorageAdapter; import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; import org.apache.druid.segment.join.filter.JoinableClauses; import org.apache.druid.segment.join.table.IndexedTableJoinable; +import org.apache.druid.testing.InitializedNullHandlingTest; import org.apache.druid.timeline.SegmentId; import org.hamcrest.CoreMatchers; +import org.joda.time.Interval; import org.junit.Assert; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; +import javax.annotation.Nullable; +import java.io.Closeable; import java.io.IOException; import java.util.List; +import java.util.Optional; -public class HashJoinSegmentTest +public class HashJoinSegmentTest extends InitializedNullHandlingTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -51,17 +59,30 @@ public class HashJoinSegmentTest public ExpectedException expectedException = ExpectedException.none(); private QueryableIndexSegment baseSegment; + private ReferenceCountingSegment referencedSegment; private HashJoinSegment hashJoinSegment; - @BeforeClass - public static void setUpStatic() - { - NullHandling.initializeForTests(); - } + private int allReferencesAcquireCount; + private int allReferencesCloseCount; + private int referencedSegmentAcquireCount; + private int referencedSegmentClosedCount; + private int indexedTableJoinableReferenceAcquireCount; + private int indexedTableJoinableReferenceCloseCount; + private boolean j0Closed; + private boolean j1Closed; @Before public void setUp() throws IOException { + allReferencesAcquireCount = 0; + allReferencesCloseCount = 0; + referencedSegmentAcquireCount = 0; + referencedSegmentClosedCount = 0; + indexedTableJoinableReferenceAcquireCount = 0; + indexedTableJoinableReferenceCloseCount = 0; + j0Closed = false; + j1Closed = false; + baseSegment = new QueryableIndexSegment( JoinTestHelper.createFactIndexBuilder(temporaryFolder.newFolder()).buildMMappedIndex(), SegmentId.dummy("facts") @@ -70,13 +91,39 @@ public void setUp() throws IOException List joinableClauses = ImmutableList.of( new JoinableClause( "j0.", - new IndexedTableJoinable(JoinTestHelper.createCountriesIndexedTable()), + new IndexedTableJoinable(JoinTestHelper.createCountriesIndexedTable()) + { + @Override + public Optional acquireReferences() + { + if (!j0Closed) { + indexedTableJoinableReferenceAcquireCount++; + Closer closer = Closer.create(); + closer.register(() -> indexedTableJoinableReferenceCloseCount++); + return Optional.of(closer); + } + return Optional.empty(); + } + }, JoinType.LEFT, JoinConditionAnalysis.forExpression("1", "j0.", ExprMacroTable.nil()) ), new JoinableClause( "j1.", - new IndexedTableJoinable(JoinTestHelper.createRegionsIndexedTable()), + new IndexedTableJoinable(JoinTestHelper.createRegionsIndexedTable()) + { + @Override + public Optional acquireReferences() + { + if (!j1Closed) { + indexedTableJoinableReferenceAcquireCount++; + Closer closer = Closer.create(); + closer.register(() -> indexedTableJoinableReferenceCloseCount++); + return Optional.of(closer); + } + return Optional.empty(); + } + }, JoinType.LEFT, JoinConditionAnalysis.forExpression("1", "j1.", ExprMacroTable.nil()) ) @@ -92,11 +139,70 @@ public void setUp() throws IOException QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE ); + referencedSegment = ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment); + SegmentReference testWrapper = new SegmentReference() + { + @Override + public Optional acquireReferences() + { + Closer closer = Closer.create(); + return referencedSegment.acquireReferences().map(closeable -> { + referencedSegmentAcquireCount++; + closer.register(closeable); + closer.register(() -> referencedSegmentClosedCount++); + return closer; + }); + } + + @Override + public SegmentId getId() + { + return referencedSegment.getId(); + } + + @Override + public Interval getDataInterval() + { + return referencedSegment.getDataInterval(); + } + + @Nullable + @Override + public QueryableIndex asQueryableIndex() + { + return referencedSegment.asQueryableIndex(); + } + + @Override + public StorageAdapter asStorageAdapter() + { + return referencedSegment.asStorageAdapter(); + } + + @Override + public void close() + { + referencedSegment.close(); + } + }; hashJoinSegment = new HashJoinSegment( - baseSegment, + testWrapper, joinableClauses, joinFilterPreAnalysis - ); + ) + { + @Override + public Optional acquireReferences() + { + Closer closer = Closer.create(); + return super.acquireReferences().map(closeable -> { + allReferencesAcquireCount++; + closer.register(closeable); + closer.register(() -> allReferencesCloseCount++); + return closer; + }); + } + }; } @Test @@ -118,7 +224,7 @@ public void test_constructor_noClauses() ); final HashJoinSegment ignored = new HashJoinSegment( - baseSegment, + ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment), joinableClauses, joinFilterPreAnalysis ); @@ -150,4 +256,79 @@ public void test_asStorageAdapter() CoreMatchers.instanceOf(HashJoinSegmentStorageAdapter.class) ); } + + @Test + public void testJoinableClausesAreClosedWhenReferencesUsed() throws IOException + { + Assert.assertFalse(referencedSegment.isClosed()); + + Optional maybeCloseable = hashJoinSegment.acquireReferences(); + Assert.assertTrue(maybeCloseable.isPresent()); + + Assert.assertEquals(1, referencedSegmentAcquireCount); + Assert.assertEquals(2, indexedTableJoinableReferenceAcquireCount); + Assert.assertEquals(1, allReferencesAcquireCount); + Assert.assertEquals(0, referencedSegmentClosedCount); + Assert.assertEquals(0, indexedTableJoinableReferenceCloseCount); + Assert.assertEquals(0, allReferencesCloseCount); + + Closeable closer = maybeCloseable.get(); + closer.close(); + + Assert.assertFalse(referencedSegment.isClosed()); + Assert.assertEquals(1, referencedSegmentClosedCount); + Assert.assertEquals(2, indexedTableJoinableReferenceCloseCount); + Assert.assertEquals(1, allReferencesCloseCount); + + } + + @Test + public void testJoinableClausesClosedIfSegmentIsAlreadyClosed() + { + Assert.assertFalse(referencedSegment.isClosed()); + + referencedSegment.close(); + Assert.assertTrue(referencedSegment.isClosed()); + + Optional maybeCloseable = hashJoinSegment.acquireReferences(); + Assert.assertFalse(maybeCloseable.isPresent()); + Assert.assertEquals(0, referencedSegmentAcquireCount); + Assert.assertEquals(0, indexedTableJoinableReferenceAcquireCount); + Assert.assertEquals(0, allReferencesAcquireCount); + Assert.assertEquals(0, referencedSegmentClosedCount); + Assert.assertEquals(0, indexedTableJoinableReferenceCloseCount); + Assert.assertEquals(0, allReferencesCloseCount); + } + + @Test + public void testJoinableClausesClosedIfJoinableZeroIsAlreadyClosed() + { + Assert.assertFalse(referencedSegment.isClosed()); + j0Closed = true; + + Optional maybeCloseable = hashJoinSegment.acquireReferences(); + Assert.assertFalse(maybeCloseable.isPresent()); + Assert.assertEquals(1, referencedSegmentAcquireCount); + Assert.assertEquals(0, indexedTableJoinableReferenceAcquireCount); + Assert.assertEquals(0, allReferencesAcquireCount); + Assert.assertEquals(1, referencedSegmentClosedCount); + Assert.assertEquals(0, indexedTableJoinableReferenceCloseCount); + Assert.assertEquals(0, allReferencesCloseCount); + } + + @Test + public void testJoinableClausesClosedIfJoinableOneIsAlreadyClosed() + { + Assert.assertFalse(referencedSegment.isClosed()); + j1Closed = true; + + Optional maybeCloseable = hashJoinSegment.acquireReferences(); + Assert.assertFalse(maybeCloseable.isPresent()); + Assert.assertEquals(1, referencedSegmentAcquireCount); + Assert.assertEquals(1, indexedTableJoinableReferenceAcquireCount); + Assert.assertEquals(0, allReferencesAcquireCount); + Assert.assertEquals(1, referencedSegmentClosedCount); + Assert.assertEquals(1, indexedTableJoinableReferenceCloseCount); + Assert.assertEquals(0, allReferencesCloseCount); + } } diff --git a/processing/src/test/java/org/apache/druid/segment/join/JoinTestHelper.java b/processing/src/test/java/org/apache/druid/segment/join/JoinTestHelper.java index 0f960dee5465..d33e2627a344 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/JoinTestHelper.java +++ b/processing/src/test/java/org/apache/druid/segment/join/JoinTestHelper.java @@ -142,6 +142,8 @@ public Supplier makeComplexProcessor(BaseObjectColumnValueSelector se } }; + public static final String INDEXED_TABLE_VERSION = DateTimes.nowUtc().toString(); + private static RowAdapter> createMapRowAdapter(final RowSignature signature) { return new RowAdapter>() @@ -255,7 +257,8 @@ public static RowBasedIndexedTable> createCountriesIndexedTa rows, createMapRowAdapter(COUNTRIES_SIGNATURE), COUNTRIES_SIGNATURE, - ImmutableSet.of("countryNumber", "countryIsoCode") + ImmutableSet.of("countryNumber", "countryIsoCode"), + INDEXED_TABLE_VERSION ) ); } @@ -268,7 +271,8 @@ public static RowBasedIndexedTable> createRegionsIndexedTabl rows, createMapRowAdapter(REGIONS_SIGNATURE), REGIONS_SIGNATURE, - ImmutableSet.of("regionIsoCode", "countryIsoCode") + ImmutableSet.of("regionIsoCode", "countryIsoCode"), + INDEXED_TABLE_VERSION ) ); } diff --git a/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java b/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java index 4fa521dbcbc9..ae36d175ff16 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java @@ -27,7 +27,7 @@ import org.apache.druid.query.QueryContexts; import org.apache.druid.query.extraction.MapLookupExtractor; import org.apache.druid.query.planning.PreJoinableClause; -import org.apache.druid.segment.Segment; +import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.column.ColumnHolder; import org.apache.druid.segment.join.lookup.LookupJoinable; @@ -95,7 +95,7 @@ public void test_isPrefixedBy() @Test public void test_createSegmentMapFn_noClauses() { - final Function segmentMapFn = Joinables.createSegmentMapFn( + final Function segmentMapFn = Joinables.createSegmentMapFn( ImmutableList.of(), NoopJoinableFactory.INSTANCE, new AtomicLong(), @@ -124,7 +124,7 @@ public void test_createSegmentMapFn_unusableClause() expectedException.expect(IllegalStateException.class); expectedException.expectMessage("dataSource is not joinable"); - final Function ignored = Joinables.createSegmentMapFn( + final Function ignored = Joinables.createSegmentMapFn( ImmutableList.of(clause), NoopJoinableFactory.INSTANCE, new AtomicLong(), @@ -153,7 +153,7 @@ public void test_createSegmentMapFn_usableClause() conditionAnalysis ); - final Function segmentMapFn = Joinables.createSegmentMapFn( + final Function segmentMapFn = Joinables.createSegmentMapFn( ImmutableList.of(clause), (dataSource, condition) -> { if (dataSource.equals(lookupDataSource) && condition.equals(conditionAnalysis)) { diff --git a/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java b/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java index 13cc45fa590d..c75232c9be9b 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/table/IndexedTableJoinableTest.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import org.apache.druid.common.config.NullHandling; +import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.math.expr.ExprMacroTable; import org.apache.druid.query.InlineDataSource; import org.apache.druid.query.dimension.DefaultDimensionSpec; @@ -96,7 +97,8 @@ public ColumnCapabilities getColumnCapabilities(String columnName) inlineDataSource.getRowsAsList(), inlineDataSource.rowAdapter(), inlineDataSource.getRowSignature(), - ImmutableSet.of("str") + ImmutableSet.of("str"), + DateTimes.nowUtc().toString() ); private IndexedTableJoinable target; diff --git a/processing/src/test/java/org/apache/druid/segment/join/table/RowBasedIndexedTableTest.java b/processing/src/test/java/org/apache/druid/segment/join/table/RowBasedIndexedTableTest.java index 49ebc36465b8..31bdc56a0ec7 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/table/RowBasedIndexedTableTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/table/RowBasedIndexedTableTest.java @@ -172,4 +172,11 @@ public void test_columnReader_countriesOutOfBoundsColumn() expectedException.expect(IndexOutOfBoundsException.class); countriesTable.columnReader(99); } + + @Test + public void testVersion() + { + Assert.assertEquals(JoinTestHelper.INDEXED_TABLE_VERSION, countriesTable.version()); + Assert.assertEquals(JoinTestHelper.INDEXED_TABLE_VERSION, regionsTable.version()); + } } diff --git a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java index c382be3493e0..ded9cf09d4ee 100644 --- a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java +++ b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java @@ -261,7 +261,7 @@ private class SpecificQueryRunnable // For nested queries, we need to look at the intervals of the inner most query. this.intervals = dataSourceAnalysis.getBaseQuerySegmentSpec() .map(QuerySegmentSpec::getIntervals) - .orElse(query.getIntervals()); + .orElseGet(() -> query.getIntervals()); } private ImmutableMap makeDownstreamQueryContext() diff --git a/server/src/main/java/org/apache/druid/segment/join/InlineJoinableFactory.java b/server/src/main/java/org/apache/druid/segment/join/InlineJoinableFactory.java index 3890e6c3736a..5945d42957cf 100644 --- a/server/src/main/java/org/apache/druid/segment/join/InlineJoinableFactory.java +++ b/server/src/main/java/org/apache/druid/segment/join/InlineJoinableFactory.java @@ -19,6 +19,7 @@ package org.apache.druid.segment.join; +import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.query.DataSource; import org.apache.druid.query.InlineDataSource; import org.apache.druid.segment.join.table.IndexedTable; @@ -49,7 +50,8 @@ public Optional build(final DataSource dataSource, final JoinCondition inlineDataSource.getRowsAsList(), inlineDataSource.rowAdapter(), inlineDataSource.getRowSignature(), - rightKeyColumns + rightKeyColumns, + DateTimes.nowUtc().toString() ) ) ); diff --git a/server/src/main/java/org/apache/druid/segment/realtime/FireHydrant.java b/server/src/main/java/org/apache/druid/segment/realtime/FireHydrant.java index fbd6e8d39885..bf5ba26c5299 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/FireHydrant.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/FireHydrant.java @@ -19,18 +19,22 @@ package org.apache.druid.segment.realtime; +import com.google.common.annotations.VisibleForTesting; import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.Pair; import org.apache.druid.segment.IncrementalIndexSegment; import org.apache.druid.segment.ReferenceCountingSegment; import org.apache.druid.segment.Segment; +import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.incremental.IncrementalIndex; import org.apache.druid.timeline.SegmentId; import org.joda.time.Interval; import javax.annotation.Nullable; import java.io.Closeable; +import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; /** */ @@ -71,27 +75,6 @@ public Interval getSegmentDataInterval() return adapter.get().getDataInterval(); } - public ReferenceCountingSegment getIncrementedSegment() - { - ReferenceCountingSegment segment = adapter.get(); - while (true) { - if (segment.increment()) { - return segment; - } - // segment.increment() returned false, means it is closed. Since close() in swapSegment() happens after segment - // swap, the new segment should already be visible. - ReferenceCountingSegment newSegment = adapter.get(); - if (segment == newSegment) { - throw new ISE("segment.close() is called somewhere outside FireHydrant.swapSegment()"); - } - if (newSegment == null) { - throw new ISE("FireHydrant was 'closed' by swapping segment to null while acquiring a segment"); - } - segment = newSegment; - // Spin loop. - } - } - public int getCount() { return count; @@ -133,12 +116,75 @@ public void swapSegment(@Nullable Segment newSegment) } } - public Pair getAndIncrementSegment() + public ReferenceCountingSegment getIncrementedSegment() + { + ReferenceCountingSegment segment = adapter.get(); + while (true) { + if (segment.increment()) { + return segment; + } + // segment.increment() returned false, means it is closed. Since close() in swapSegment() happens after segment + // swap, the new segment should already be visible. + ReferenceCountingSegment newSegment = adapter.get(); + if (segment == newSegment) { + throw new ISE("segment.close() is called somewhere outside FireHydrant.swapSegment()"); + } + if (newSegment == null) { + throw new ISE("FireHydrant was 'closed' by swapping segment to null while acquiring a segment"); + } + segment = newSegment; + // Spin loop. + } + } + + public Pair getAndIncrementSegment() { ReferenceCountingSegment segment = getIncrementedSegment(); return new Pair<>(segment, segment.decrementOnceCloseable()); } + /** + * This method is like a combined form of {@link #getIncrementedSegment} and {@link #getAndIncrementSegment} that + * deals in {@link SegmentReference} instead of directly with {@link ReferenceCountingSegment} in order to acquire + * reference count for both hydrant's segment and any tracked joinables taking part in the query. + */ + public Optional> getSegmentForQuery( + Function segmentMapFn + ) + { + ReferenceCountingSegment sinkSegment = adapter.get(); + SegmentReference segment = segmentMapFn.apply(sinkSegment); + while (true) { + Optional reference = segment.acquireReferences(); + if (reference.isPresent()) { + + return Optional.of(new Pair<>(segment, reference.get())); + } + // segment.acquireReferences() returned false, means it is closed. Since close() in swapSegment() happens after + // segment swap, the new segment should already be visible. + ReferenceCountingSegment newSinkSegment = adapter.get(); + if (newSinkSegment == null) { + throw new ISE("FireHydrant was 'closed' by swapping segment to null while acquiring a segment"); + } + if (sinkSegment == newSinkSegment) { + if (newSinkSegment.isClosed()) { + throw new ISE("segment.close() is called somewhere outside FireHydrant.swapSegment()"); + } + // if segment is not closed, but is same segment it means we are having trouble getting references for joinables + // of a HashJoinSegment created by segmentMapFn + return Optional.empty(); + } + segment = segmentMapFn.apply(newSinkSegment); + // Spin loop. + } + } + + @VisibleForTesting + public ReferenceCountingSegment getHydrantSegment() + { + return adapter.get(); + } + @Override public String toString() { diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/AppenderatorImpl.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/AppenderatorImpl.java index fe6986d2f0dd..24fcb67efa29 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/AppenderatorImpl.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/AppenderatorImpl.java @@ -59,7 +59,7 @@ import org.apache.druid.segment.IndexMerger; import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.QueryableIndexSegment; -import org.apache.druid.segment.Segment; +import org.apache.druid.segment.ReferenceCountingSegment; import org.apache.druid.segment.incremental.IncrementalIndexAddResult; import org.apache.druid.segment.incremental.IndexSizeExceededException; import org.apache.druid.segment.indexing.DataSchema; @@ -756,7 +756,7 @@ private DataSegment mergeAndPush( Closer closer = Closer.create(); try { for (FireHydrant fireHydrant : sink) { - Pair segmentAndCloseable = fireHydrant.getAndIncrementSegment(); + Pair segmentAndCloseable = fireHydrant.getAndIncrementSegment(); final QueryableIndex queryableIndex = segmentAndCloseable.lhs.asQueryableIndex(); log.debug("Segment[%s] adding hydrant[%s]", identifier, fireHydrant); indexes.add(queryableIndex); diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java index 504daf3d60a0..4527287b4f66 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java @@ -28,7 +28,6 @@ import org.apache.druid.client.cache.CachePopulatorStats; import org.apache.druid.client.cache.ForegroundCachePopulator; import org.apache.druid.java.util.common.ISE; -import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.Pair; import org.apache.druid.java.util.common.concurrent.Execs; import org.apache.druid.java.util.common.guava.CloseQuietly; @@ -57,7 +56,7 @@ import org.apache.druid.query.planning.DataSourceAnalysis; import org.apache.druid.query.spec.SpecificSegmentQueryRunner; import org.apache.druid.query.spec.SpecificSegmentSpec; -import org.apache.druid.segment.Segment; +import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.join.JoinableFactory; import org.apache.druid.segment.join.Joinables; import org.apache.druid.segment.realtime.FireHydrant; @@ -171,7 +170,7 @@ public QueryRunner getQueryRunnerForSegments(final Query query, final } // segmentMapFn maps each base Segment into a joined Segment if necessary. - final Function segmentMapFn = Joinables.createSegmentMapFn( + final Function segmentMapFn = Joinables.createSegmentMapFn( analysis.getPreJoinableClauses(), joinableFactory, cpuTimeAccumulator, @@ -211,15 +210,24 @@ public QueryRunner getQueryRunnerForSegments(final Query query, final final boolean hydrantDefinitelySwapped = hydrant.hasSwapped(); if (skipIncrementalSegment && !hydrantDefinitelySwapped) { - return new Pair<>(Intervals.ETERNITY, new NoopQueryRunner<>()); + return new Pair<>(hydrant.getSegmentDataInterval(), new NoopQueryRunner<>()); } // Prevent the underlying segment from swapping when its being iterated - final Pair segmentAndCloseable = hydrant.getAndIncrementSegment(); + final Optional> maybeSegmentAndCloseable = + hydrant.getSegmentForQuery(segmentMapFn); + + // if optional isn't present, we failed to acquire reference to the segment or any joinables + if (!maybeSegmentAndCloseable.isPresent()) { + return new Pair<>( + hydrant.getSegmentDataInterval(), + new ReportTimelineMissingSegmentQueryRunner<>(descriptor) + ); + } + final Pair segmentAndCloseable = maybeSegmentAndCloseable.get(); try { - final Segment mappedSegment = segmentMapFn.apply(segmentAndCloseable.lhs); - QueryRunner runner = factory.createRunner(mappedSegment); + QueryRunner runner = factory.createRunner(segmentAndCloseable.lhs); // 1) Only use caching if data is immutable // 2) Hydrants are not the same between replicas, make sure cache is local @@ -245,7 +253,7 @@ public QueryRunner getQueryRunnerForSegments(final Query query, final runner, segmentAndCloseable.rhs ); - return new Pair<>(mappedSegment.getDataInterval(), runner); + return new Pair<>(segmentAndCloseable.lhs.getDataInterval(), runner); } catch (RuntimeException e) { CloseQuietly.close(segmentAndCloseable.rhs); diff --git a/server/src/main/java/org/apache/druid/segment/realtime/plumber/RealtimePlumber.java b/server/src/main/java/org/apache/druid/segment/realtime/plumber/RealtimePlumber.java index 7a7db66d1f90..1fdc0b0f6804 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/plumber/RealtimePlumber.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/plumber/RealtimePlumber.java @@ -56,7 +56,7 @@ import org.apache.druid.segment.Metadata; import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.QueryableIndexSegment; -import org.apache.druid.segment.Segment; +import org.apache.druid.segment.ReferenceCountingSegment; import org.apache.druid.segment.incremental.IncrementalIndexAddResult; import org.apache.druid.segment.incremental.IndexSizeExceededException; import org.apache.druid.segment.indexing.DataSchema; @@ -424,7 +424,7 @@ public void doRun() Closer closer = Closer.create(); try { for (FireHydrant fireHydrant : sink) { - Pair segmentAndCloseable = fireHydrant.getAndIncrementSegment(); + Pair segmentAndCloseable = fireHydrant.getAndIncrementSegment(); final QueryableIndex queryableIndex = segmentAndCloseable.lhs.asQueryableIndex(); log.info("Adding hydrant[%s]", fireHydrant); indexes.add(queryableIndex); diff --git a/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java b/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java index d7f39adaa4ba..5e3928c799e0 100644 --- a/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java +++ b/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java @@ -24,6 +24,7 @@ import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.concurrent.Execs; +import org.apache.druid.java.util.common.guava.FunctionalIterable; import org.apache.druid.java.util.emitter.service.ServiceEmitter; import org.apache.druid.query.FluentQueryRunnerBuilder; import org.apache.druid.query.Query; @@ -35,7 +36,9 @@ import org.apache.druid.query.QuerySegmentWalker; import org.apache.druid.query.SegmentDescriptor; import org.apache.druid.query.planning.DataSourceAnalysis; +import org.apache.druid.segment.ReferenceCountingSegment; import org.apache.druid.segment.Segment; +import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.SegmentWrangler; import org.apache.druid.segment.join.JoinableFactory; import org.apache.druid.segment.join.Joinables; @@ -89,11 +92,15 @@ public QueryRunner getQueryRunnerForIntervals(final Query query, final throw new IAE("Cannot query dataSource locally: %s", analysis.getDataSource()); } - final Iterable segments = segmentWrangler.getSegmentsForIntervals(analysis.getBaseDataSource(), intervals); + // wrap in ReferenceCountingSegment, these aren't currently managed by SegmentManager so reference tracking doesn't + // matter, but at least some or all will be in a future PR + final Iterable segments = + FunctionalIterable.create(segmentWrangler.getSegmentsForIntervals(analysis.getBaseDataSource(), intervals)) + .transform(ReferenceCountingSegment::wrapRootGenerationSegment); final Query prioritizedAndLaned = prioritizeAndLaneQuery(query, segments); final AtomicLong cpuAccumulator = new AtomicLong(0L); - final Function segmentMapFn = Joinables.createSegmentMapFn( + final Function segmentMapFn = Joinables.createSegmentMapFn( analysis.getPreJoinableClauses(), joinableFactory, cpuAccumulator, @@ -129,7 +136,7 @@ public QueryRunner getQueryRunnerForSegments(final Query query, final throw new ISE("Cannot run with specific segments"); } - private Query prioritizeAndLaneQuery(Query query, Iterable segments) + private Query prioritizeAndLaneQuery(Query query, Iterable segments) { Set segmentServerSelectors = new HashSet<>(); for (Segment s : segments) { diff --git a/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java b/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java index ca26e0dd4f75..086673fabc13 100644 --- a/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java +++ b/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java @@ -53,9 +53,8 @@ import org.apache.druid.query.planning.DataSourceAnalysis; import org.apache.druid.query.spec.SpecificSegmentQueryRunner; import org.apache.druid.query.spec.SpecificSegmentSpec; -import org.apache.druid.segment.ReferenceCounter; import org.apache.druid.segment.ReferenceCountingSegment; -import org.apache.druid.segment.Segment; +import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.join.JoinableFactory; import org.apache.druid.segment.join.Joinables; import org.apache.druid.server.SegmentManager; @@ -194,7 +193,7 @@ public QueryRunner getQueryRunnerForSegments(Query query, Iterable segmentMapFn = Joinables.createSegmentMapFn( + final Function segmentMapFn = Joinables.createSegmentMapFn( analysis.getPreJoinableClauses(), joinableFactory, cpuTimeAccumulator, @@ -230,7 +229,6 @@ public QueryRunner getQueryRunnerForSegments(Query query, Iterable QueryRunner getQueryRunnerForSegments(Query query, Iterable QueryRunner buildAndDecorateQueryRunner( final QueryRunnerFactory> factory, final QueryToolChest> toolChest, - final Segment segment, - final ReferenceCounter segmentReferenceCounter, + final SegmentReference segment, final SegmentDescriptor segmentDescriptor, final AtomicLong cpuTimeAccumulator ) @@ -266,7 +263,7 @@ private QueryRunner buildAndDecorateQueryRunner( MetricsEmittingQueryRunner metricsEmittingQueryRunnerInner = new MetricsEmittingQueryRunner<>( emitter, toolChest, - new ReferenceCountingSegmentQueryRunner<>(factory, segment, segmentReferenceCounter, segmentDescriptor), + new ReferenceCountingSegmentQueryRunner<>(factory, segment, segmentDescriptor), QueryMetrics::reportSegmentTime, queryMetrics -> queryMetrics.segment(segmentIdString) ); diff --git a/server/src/test/java/org/apache/druid/segment/loading/CacheTestSegmentLoader.java b/server/src/test/java/org/apache/druid/segment/loading/CacheTestSegmentLoader.java index e604b29f7631..a42c553bad5a 100644 --- a/server/src/test/java/org/apache/druid/segment/loading/CacheTestSegmentLoader.java +++ b/server/src/test/java/org/apache/druid/segment/loading/CacheTestSegmentLoader.java @@ -20,7 +20,6 @@ package org.apache.druid.segment.loading; import org.apache.druid.java.util.common.MapUtils; -import org.apache.druid.segment.AbstractSegment; import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.Segment; import org.apache.druid.segment.StorageAdapter; @@ -49,7 +48,7 @@ public boolean isSegmentLoaded(DataSegment segment) @Override public Segment getSegment(final DataSegment segment, boolean lazy) { - return new AbstractSegment() + return new Segment() { @Override public SegmentId getId() diff --git a/server/src/test/java/org/apache/druid/segment/realtime/FireHydrantTest.java b/server/src/test/java/org/apache/druid/segment/realtime/FireHydrantTest.java new file mode 100644 index 000000000000..4f288426e50f --- /dev/null +++ b/server/src/test/java/org/apache/druid/segment/realtime/FireHydrantTest.java @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.realtime; + +import org.apache.druid.java.util.common.ISE; +import org.apache.druid.java.util.common.Pair; +import org.apache.druid.segment.IncrementalIndexSegment; +import org.apache.druid.segment.QueryableIndex; +import org.apache.druid.segment.QueryableIndexSegment; +import org.apache.druid.segment.ReferenceCountingSegment; +import org.apache.druid.segment.SegmentReference; +import org.apache.druid.segment.StorageAdapter; +import org.apache.druid.segment.TestIndex; +import org.apache.druid.testing.InitializedNullHandlingTest; +import org.apache.druid.timeline.SegmentId; +import org.joda.time.Interval; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import javax.annotation.Nullable; +import java.io.Closeable; +import java.io.IOException; +import java.util.Optional; +import java.util.function.Function; + +public class FireHydrantTest extends InitializedNullHandlingTest +{ + private IncrementalIndexSegment incrementalIndexSegment; + private QueryableIndexSegment queryableIndexSegment; + private FireHydrant hydrant; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setup() + { + incrementalIndexSegment = new IncrementalIndexSegment(TestIndex.getIncrementalTestIndex(), SegmentId.dummy("test")); + queryableIndexSegment = new QueryableIndexSegment(TestIndex.getMMappedTestIndex(), SegmentId.dummy("test")); + + // hydrant starts out with incremental segment loaded + hydrant = new FireHydrant(incrementalIndexSegment, 0); + } + + @Test + public void testGetIncrementedSegmentNotSwapped() + { + Assert.assertEquals(0, hydrant.getHydrantSegment().getNumReferences()); + ReferenceCountingSegment segment = hydrant.getIncrementedSegment(); + Assert.assertNotNull(segment); + Assert.assertTrue(segment.getBaseSegment() == incrementalIndexSegment); + Assert.assertEquals(1, segment.getNumReferences()); + } + + @Test + public void testGetIncrementedSegmentSwapped() + { + ReferenceCountingSegment incrementalSegmentReference = hydrant.getHydrantSegment(); + Assert.assertEquals(0, incrementalSegmentReference.getNumReferences()); + hydrant.swapSegment(queryableIndexSegment); + ReferenceCountingSegment segment = hydrant.getIncrementedSegment(); + Assert.assertNotNull(segment); + Assert.assertTrue(segment.getBaseSegment() == queryableIndexSegment); + Assert.assertEquals(0, incrementalSegmentReference.getNumReferences()); + Assert.assertEquals(1, segment.getNumReferences()); + } + + @Test + public void testGetIncrementedSegmentClosed() + { + expectedException.expect(ISE.class); + expectedException.expectMessage("segment.close() is called somewhere outside FireHydrant.swapSegment()"); + hydrant.getHydrantSegment().close(); + Assert.assertEquals(0, hydrant.getHydrantSegment().getNumReferences()); + ReferenceCountingSegment segment = hydrant.getIncrementedSegment(); + } + + @Test + public void testGetAndIncrementSegment() throws IOException + { + ReferenceCountingSegment incrementalSegmentReference = hydrant.getHydrantSegment(); + Assert.assertEquals(0, incrementalSegmentReference.getNumReferences()); + + Pair segmentAndCloseable = hydrant.getAndIncrementSegment(); + Assert.assertEquals(1, segmentAndCloseable.lhs.getNumReferences()); + segmentAndCloseable.rhs.close(); + Assert.assertEquals(0, segmentAndCloseable.lhs.getNumReferences()); + } + + @Test + public void testGetSegmentForQuery() throws IOException + { + ReferenceCountingSegment incrementalSegmentReference = hydrant.getHydrantSegment(); + Assert.assertEquals(0, incrementalSegmentReference.getNumReferences()); + + Optional> maybeSegmentAndCloseable = hydrant.getSegmentForQuery( + Function.identity() + ); + Assert.assertTrue(maybeSegmentAndCloseable.isPresent()); + Assert.assertEquals(1, incrementalSegmentReference.getNumReferences()); + + Pair segmentAndCloseable = maybeSegmentAndCloseable.get(); + segmentAndCloseable.rhs.close(); + Assert.assertEquals(0, incrementalSegmentReference.getNumReferences()); + } + + @Test + public void testGetSegmentForQuerySwapped() throws IOException + { + ReferenceCountingSegment incrementalSegmentReference = hydrant.getHydrantSegment(); + hydrant.swapSegment(queryableIndexSegment); + ReferenceCountingSegment queryableSegmentReference = hydrant.getHydrantSegment(); + Assert.assertEquals(0, incrementalSegmentReference.getNumReferences()); + Assert.assertEquals(0, queryableSegmentReference.getNumReferences()); + + Optional> maybeSegmentAndCloseable = hydrant.getSegmentForQuery( + Function.identity() + ); + Assert.assertTrue(maybeSegmentAndCloseable.isPresent()); + Assert.assertEquals(0, incrementalSegmentReference.getNumReferences()); + Assert.assertEquals(1, queryableSegmentReference.getNumReferences()); + + Pair segmentAndCloseable = maybeSegmentAndCloseable.get(); + segmentAndCloseable.rhs.close(); + Assert.assertEquals(0, incrementalSegmentReference.getNumReferences()); + Assert.assertEquals(0, queryableSegmentReference.getNumReferences()); + } + + @Test + public void testGetSegmentForQueryButNotAbleToAcquireReferences() + { + ReferenceCountingSegment incrementalSegmentReference = hydrant.getHydrantSegment(); + Assert.assertEquals(0, incrementalSegmentReference.getNumReferences()); + + Optional> maybeSegmentAndCloseable = hydrant.getSegmentForQuery( + segmentReference -> new SegmentReference() + { + @Override + public Optional acquireReferences() + { + return Optional.empty(); + } + + @Override + public SegmentId getId() + { + return incrementalIndexSegment.getId(); + } + + @Override + public Interval getDataInterval() + { + return incrementalIndexSegment.getDataInterval(); + } + + @Nullable + @Override + public QueryableIndex asQueryableIndex() + { + return incrementalIndexSegment.asQueryableIndex(); + } + + @Override + public StorageAdapter asStorageAdapter() + { + return incrementalIndexSegment.asStorageAdapter(); + } + + @Override + public void close() + { + incrementalIndexSegment.close(); + } + } + ); + Assert.assertFalse(maybeSegmentAndCloseable.isPresent()); + Assert.assertEquals(0, incrementalSegmentReference.getNumReferences()); + } + + @Test + public void testGetSegmentForQueryButNotAbleToAcquireReferencesSegmentClosed() + { + expectedException.expect(ISE.class); + expectedException.expectMessage("segment.close() is called somewhere outside FireHydrant.swapSegment()"); + ReferenceCountingSegment incrementalSegmentReference = hydrant.getHydrantSegment(); + Assert.assertEquals(0, incrementalSegmentReference.getNumReferences()); + incrementalSegmentReference.close(); + + Optional> maybeSegmentAndCloseable = hydrant.getSegmentForQuery( + Function.identity() + ); + } +} diff --git a/server/src/test/java/org/apache/druid/server/SegmentManagerTest.java b/server/src/test/java/org/apache/druid/server/SegmentManagerTest.java index 04f796e9918d..562fc1dfffbf 100644 --- a/server/src/test/java/org/apache/druid/server/SegmentManagerTest.java +++ b/server/src/test/java/org/apache/druid/server/SegmentManagerTest.java @@ -26,7 +26,6 @@ import org.apache.druid.java.util.common.MapUtils; import org.apache.druid.query.TableDataSource; import org.apache.druid.query.planning.DataSourceAnalysis; -import org.apache.druid.segment.AbstractSegment; import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.ReferenceCountingSegment; import org.apache.druid.segment.Segment; @@ -90,7 +89,7 @@ public void cleanup(DataSegment segment) } }; - private static class SegmentForTesting extends AbstractSegment + private static class SegmentForTesting implements Segment { private final String version; private final Interval interval; diff --git a/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java b/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java index cc3a406cfe78..e41ad268d160 100644 --- a/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java +++ b/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java @@ -45,6 +45,7 @@ import org.apache.druid.query.spec.SpecificSegmentSpec; import org.apache.druid.segment.ReferenceCountingSegment; import org.apache.druid.segment.Segment; +import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.join.JoinableFactory; import org.apache.druid.segment.join.Joinables; import org.apache.druid.timeline.TimelineObjectHolder; @@ -139,7 +140,7 @@ public QueryRunner getQueryRunnerForSegments(final Query query, final throw new ISE("Cannot handle subquery: %s", analysis.getDataSource()); } - final Function segmentMapFn = Joinables.createSegmentMapFn( + final Function segmentMapFn = Joinables.createSegmentMapFn( analysis.getPreJoinableClauses(), joinableFactory, new AtomicLong(), @@ -197,7 +198,7 @@ private QueryRunner makeTableRunner( final QueryToolChest> toolChest, final QueryRunnerFactory> factory, final Iterable segments, - final Function segmentMapFn + final Function segmentMapFn ) { final List segmentsList = Lists.newArrayList(segments); @@ -217,7 +218,7 @@ private QueryRunner makeTableRunner( .transform( segment -> new SpecificSegmentQueryRunner<>( - factory.createRunner(segmentMapFn.apply(segment.getSegment())), + factory.createRunner(segmentMapFn.apply(ReferenceCountingSegment.wrapRootGenerationSegment(segment.getSegment()))), new SpecificSegmentSpec(segment.getDescriptor()) ) ) diff --git a/server/src/test/java/org/apache/druid/server/coordination/ServerManagerTest.java b/server/src/test/java/org/apache/druid/server/coordination/ServerManagerTest.java index 356302cee228..9ca113e2515d 100644 --- a/server/src/test/java/org/apache/druid/server/coordination/ServerManagerTest.java +++ b/server/src/test/java/org/apache/druid/server/coordination/ServerManagerTest.java @@ -57,7 +57,6 @@ import org.apache.druid.query.context.ResponseContext; import org.apache.druid.query.search.SearchQuery; import org.apache.druid.query.search.SearchResultValue; -import org.apache.druid.segment.AbstractSegment; import org.apache.druid.segment.IndexIO; import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.ReferenceCountingSegment; @@ -600,7 +599,7 @@ public TypeReference getResultTypeReference() } } - private static class SegmentForTesting extends AbstractSegment + private static class SegmentForTesting implements Segment { private final String version; private final Interval interval; diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rel/QueryMaker.java b/sql/src/main/java/org/apache/druid/sql/calcite/rel/QueryMaker.java index c0e73163c021..f8c3dc2a152d 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/rel/QueryMaker.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/rel/QueryMaker.java @@ -126,7 +126,7 @@ private List findBaseDataSourceIntervals(Query query) return DataSourceAnalysis.forDataSource(query.getDataSource()) .getBaseQuerySegmentSpec() .map(QuerySegmentSpec::getIntervals) - .orElse(query.getIntervals()); + .orElseGet(query::getIntervals); } private Sequence execute(Query query, final List newFields, final List newTypes) From a4351d009c6ca9efe121282e47dae3eb21d561e5 Mon Sep 17 00:00:00 2001 From: BIGrey Date: Thu, 11 Jun 2020 00:19:38 +0800 Subject: [PATCH 071/107] Fix failed tests in TimestampParserTest when running locally (#9997) * fix failed tests in TimestampPaserTest due to timezone * remove unneeded -Duser.country=US Co-authored-by: huagnhui.bigrey --- core/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/pom.xml b/core/pom.xml index 627b3af4b80f..839d19b480c4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -394,6 +394,7 @@ @{jacocoArgLine} -Djava.library.path=${project.build.directory}/hyperic-sigar-${sigar.base.version}/sigar-bin/lib/ + -Duser.language=en From fb19d9b81efb69a452ba5358e9149fb70dc6d338 Mon Sep 17 00:00:00 2001 From: Stefan Birkner Date: Wed, 10 Jun 2020 18:32:00 +0200 Subject: [PATCH 072/107] Simplify CompressedVSizeColumnarIntsSupplierTest (#10003) The parameters generator uses CompressionStrategy.noNoneValues() instead of CompressionStrategyTest.compressionStrategies() which wrapped each strategy in a single element array. This improves readability of the test. --- .../data/CompressedVSizeColumnarIntsSupplierTest.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/processing/src/test/java/org/apache/druid/segment/data/CompressedVSizeColumnarIntsSupplierTest.java b/processing/src/test/java/org/apache/druid/segment/data/CompressedVSizeColumnarIntsSupplierTest.java index 3b3d427a23ae..f113dc3510ad 100644 --- a/processing/src/test/java/org/apache/druid/segment/data/CompressedVSizeColumnarIntsSupplierTest.java +++ b/processing/src/test/java/org/apache/druid/segment/data/CompressedVSizeColumnarIntsSupplierTest.java @@ -56,13 +56,8 @@ public class CompressedVSizeColumnarIntsSupplierTest extends CompressionStrategy @Parameterized.Parameters(name = "{index}: compression={0}, byteOrder={1}") public static Iterable compressionStrategies() { - final Iterable compressionStrategies = Iterables.transform( - CompressionStrategyTest.compressionStrategies(), - (Object[] input) -> (CompressionStrategy) input[0] - ); - Set> combinations = Sets.cartesianProduct( - Sets.newHashSet(compressionStrategies), + Sets.newHashSet(CompressionStrategy.noNoneValues()), Sets.newHashSet(ByteOrder.BIG_ENDIAN, ByteOrder.LITTLE_ENDIAN) ); From d5702905425fbb9bd3cd4dcbac5b6081885ebe4c Mon Sep 17 00:00:00 2001 From: danc Date: Wed, 10 Jun 2020 18:32:49 +0200 Subject: [PATCH 073/107] Update password-provider.md (#9857) --- docs/operations/password-provider.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/operations/password-provider.md b/docs/operations/password-provider.md index 4a28e64aae8b..209b2d3882ea 100644 --- a/docs/operations/password-provider.md +++ b/docs/operations/password-provider.md @@ -33,7 +33,7 @@ Environment variable password provider provides password by looking at specified e.g ```json -{ "type": "environment", "variable": "METADATA_STORAGE_PASSWORD" } +druid.metadata.storage.connector.password={ "type": "environment", "variable": "METADATA_STORAGE_PASSWORD" } ``` The values are described below. @@ -50,5 +50,5 @@ Please have a look at "Adding a new Password Provider implementation" on this [p To use this implementation, simply set the relevant password runtime property to something similar as was done for Environment variable password provider like - ```json -{ "type": "", "": "", ... } +druid.metadata.storage.connector.password={ "type": "", "": "", ... } ``` From 6e282f075a980070e8e1503a9a2d6231de274aef Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Wed, 10 Jun 2020 12:29:30 -0700 Subject: [PATCH 074/107] ignore brokers in broker views (#10017) --- .../apache/druid/client/BrokerServerView.java | 11 ++ .../druid/client/BrokerServerViewTest.java | 143 +++++++++++++++--- .../druid/sql/calcite/schema/DruidSchema.java | 10 ++ .../sql/calcite/schema/DruidSchemaTest.java | 32 ++++ 4 files changed, 171 insertions(+), 25 deletions(-) diff --git a/server/src/main/java/org/apache/druid/client/BrokerServerView.java b/server/src/main/java/org/apache/druid/client/BrokerServerView.java index 337e9a46fb2f..3b5d40872cd6 100644 --- a/server/src/main/java/org/apache/druid/client/BrokerServerView.java +++ b/server/src/main/java/org/apache/druid/client/BrokerServerView.java @@ -42,6 +42,7 @@ import org.apache.druid.query.TableDataSource; import org.apache.druid.query.planning.DataSourceAnalysis; import org.apache.druid.server.coordination.DruidServerMetadata; +import org.apache.druid.server.coordination.ServerType; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.VersionedIntervalTimeline; @@ -217,6 +218,12 @@ private QueryableDruidServer removeServer(DruidServer server) private void serverAddedSegment(final DruidServerMetadata server, final DataSegment segment) { + if (server.getType().equals(ServerType.BROKER)) { + // in theory we could just filter this to ensure we don't put ourselves in here, to make dope broker tree + // query topologies, but for now just skip all brokers, so we don't create some sort of wild infinite query + // loop... + return; + } SegmentId segmentId = segment.getId(); synchronized (lock) { log.debug("Adding segment[%s] for server[%s]", segment, server); @@ -246,6 +253,10 @@ private void serverAddedSegment(final DruidServerMetadata server, final DataSegm private void serverRemovedSegment(DruidServerMetadata server, DataSegment segment) { + if (server.getType().equals(ServerType.BROKER)) { + // might as well save the trouble of grabbing a lock for something that isn't there.. + return; + } SegmentId segmentId = segment.getId(); final ServerSelector selector; diff --git a/server/src/test/java/org/apache/druid/client/BrokerServerViewTest.java b/server/src/test/java/org/apache/druid/client/BrokerServerViewTest.java index dd3961e04f42..30b93a18c84d 100644 --- a/server/src/test/java/org/apache/druid/client/BrokerServerViewTest.java +++ b/server/src/test/java/org/apache/druid/client/BrokerServerViewTest.java @@ -22,7 +22,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.smile.SmileFactory; import com.fasterxml.jackson.dataformat.smile.SmileGenerator; -import com.google.common.base.Function; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -161,22 +160,15 @@ public void testMultipleServerAddedRemovedSegment() throws Exception final List druidServers = Lists.transform( ImmutableList.of("locahost:0", "localhost:1", "localhost:2", "localhost:3", "localhost:4"), - new Function() - { - @Override - public DruidServer apply(String input) - { - return new DruidServer( - input, - input, - null, - 10000000L, - ServerType.HISTORICAL, - "default_tier", - 0 - ); - } - } + input -> new DruidServer( + input, + input, + null, + 10000000L, + ServerType.HISTORICAL, + "default_tier", + 0 + ) ); for (DruidServer druidServer : druidServers) { @@ -190,14 +182,7 @@ public DruidServer apply(String input) Pair.of("2011-04-01/2011-04-09", "v2"), Pair.of("2011-04-06/2011-04-09", "v3"), Pair.of("2011-04-01/2011-04-02", "v3") - ), new Function, DataSegment>() - { - @Override - public DataSegment apply(Pair input) - { - return dataSegmentWithIntervalAndVersion(input.lhs, input.rhs); - } - } + ), input -> dataSegmentWithIntervalAndVersion(input.lhs, input.rhs) ); for (int i = 0; i < 5; ++i) { @@ -261,6 +246,114 @@ public DataSegment apply(Pair input) ); } + @Test + public void testMultipleServerAndBroker() throws Exception + { + segmentViewInitLatch = new CountDownLatch(1); + segmentAddedLatch = new CountDownLatch(6); + + // temporarily set latch count to 1 + segmentRemovedLatch = new CountDownLatch(1); + + setupViews(); + + final DruidServer druidBroker = new DruidServer( + "localhost:5", + "localhost:5", + null, + 10000000L, + ServerType.BROKER, + "default_tier", + 0 + ); + + final List druidServers = Lists.transform( + ImmutableList.of("locahost:0", "localhost:1", "localhost:2", "localhost:3", "localhost:4"), + input -> new DruidServer( + input, + input, + null, + 10000000L, + ServerType.HISTORICAL, + "default_tier", + 0 + ) + ); + + setupZNodeForServer(druidBroker, zkPathsConfig, jsonMapper); + for (DruidServer druidServer : druidServers) { + setupZNodeForServer(druidServer, zkPathsConfig, jsonMapper); + } + + final List segments = Lists.transform( + ImmutableList.of( + Pair.of("2011-04-01/2011-04-03", "v1"), + Pair.of("2011-04-03/2011-04-06", "v1"), + Pair.of("2011-04-01/2011-04-09", "v2"), + Pair.of("2011-04-06/2011-04-09", "v3"), + Pair.of("2011-04-01/2011-04-02", "v3") + ), + input -> dataSegmentWithIntervalAndVersion(input.lhs, input.rhs) + ); + + DataSegment brokerSegment = dataSegmentWithIntervalAndVersion("2011-04-01/2011-04-11", "v4"); + announceSegmentForServer(druidBroker, brokerSegment, zkPathsConfig, jsonMapper); + for (int i = 0; i < 5; ++i) { + announceSegmentForServer(druidServers.get(i), segments.get(i), zkPathsConfig, jsonMapper); + } + Assert.assertTrue(timing.forWaiting().awaitLatch(segmentViewInitLatch)); + Assert.assertTrue(timing.forWaiting().awaitLatch(segmentAddedLatch)); + + TimelineLookup timeline = brokerServerView.getTimeline( + DataSourceAnalysis.forDataSource(new TableDataSource("test_broker_server_view")) + ).get(); + + assertValues( + Arrays.asList( + createExpected("2011-04-01/2011-04-02", "v3", druidServers.get(4), segments.get(4)), + createExpected("2011-04-02/2011-04-06", "v2", druidServers.get(2), segments.get(2)), + createExpected("2011-04-06/2011-04-09", "v3", druidServers.get(3), segments.get(3)) + ), + (List) timeline.lookup( + Intervals.of( + "2011-04-01/2011-04-09" + ) + ) + ); + + // unannounce the broker segment should do nothing to announcements + unannounceSegmentForServer(druidBroker, brokerSegment, zkPathsConfig); + Assert.assertTrue(timing.forWaiting().awaitLatch(segmentRemovedLatch)); + + // renew segmentRemovedLatch since we still have 5 segments to unannounce + segmentRemovedLatch = new CountDownLatch(5); + + timeline = brokerServerView.getTimeline( + DataSourceAnalysis.forDataSource(new TableDataSource("test_broker_server_view")) + ).get(); + + // expect same set of segments as before + assertValues( + Arrays.asList( + createExpected("2011-04-01/2011-04-02", "v3", druidServers.get(4), segments.get(4)), + createExpected("2011-04-02/2011-04-06", "v2", druidServers.get(2), segments.get(2)), + createExpected("2011-04-06/2011-04-09", "v3", druidServers.get(3), segments.get(3)) + ), + (List) timeline.lookup( + Intervals.of( + "2011-04-01/2011-04-09" + ) + ) + ); + + // unannounce all the segments + for (int i = 0; i < 5; ++i) { + unannounceSegmentForServer(druidServers.get(i), segments.get(i), zkPathsConfig); + } + Assert.assertTrue(timing.forWaiting().awaitLatch(segmentRemovedLatch)); + } + + private Pair>> createExpected( String intervalStr, String version, diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java b/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java index 54e7e5a8f961..35037e2ff2cb 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java @@ -350,6 +350,12 @@ protected Multimap getFunctionMultim @VisibleForTesting void addSegment(final DruidServerMetadata server, final DataSegment segment) { + if (server.getType().equals(ServerType.BROKER)) { + // in theory we could just filter this to ensure we don't put ourselves in here, to make dope broker tree + // query topologies, but for now just skip all brokers, so we don't create some sort of wild infinite metadata + // loop... + return; + } synchronized (lock) { final Map knownSegments = segmentMetadataInfo.get(segment.getDataSource()); AvailableSegmentMetadata segmentMetadata = knownSegments != null ? knownSegments.get(segment.getId()) : null; @@ -428,6 +434,10 @@ void removeSegment(final DataSegment segment) @VisibleForTesting void removeServerSegment(final DruidServerMetadata server, final DataSegment segment) { + if (server.getType().equals(ServerType.BROKER)) { + // cheese it + return; + } synchronized (lock) { log.debug("Segment[%s] is gone from server[%s]", segment.getId(), server.getName()); final Map knownSegments = segmentMetadataInfo.get(segment.getDataSource()); diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java index 6e2f8f62103f..ee3bca1a4f8e 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java @@ -57,6 +57,7 @@ import org.apache.druid.timeline.DataSegment.PruneSpecsHolder; import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.partition.LinearShardSpec; +import org.apache.druid.timeline.partition.NoneShardSpec; import org.apache.druid.timeline.partition.NumberedShardSpec; import org.junit.After; import org.junit.AfterClass; @@ -418,4 +419,35 @@ public void testAvailableSegmentMetadataIsRealtime() Assert.assertEquals(0L, currentMetadata.isRealtime()); } + @Test + public void testAvailableSegmentFromBrokerIsIgnored() + { + + Assert.assertEquals(4, schema.getTotalSegments()); + + DruidServerMetadata metadata = new DruidServerMetadata( + "broker", + "localhost:0", + null, + 1000L, + ServerType.BROKER, + "broken", + 0 + ); + + DataSegment segment = new DataSegment( + "test", + Intervals.of("2011-04-01/2011-04-11"), + "v1", + ImmutableMap.of(), + ImmutableList.of(), + ImmutableList.of(), + NoneShardSpec.instance(), + 1, + 100L + ); + schema.addSegment(metadata, segment); + Assert.assertEquals(4, schema.getTotalSegments()); + + } } From 84a1c1e04d24b85aa256c23b4e4fbdcdc54332b0 Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn <52679095+maytasm@users.noreply.github.com> Date: Wed, 10 Jun 2020 18:17:36 -1000 Subject: [PATCH 075/107] Add instruction for code coverage checks (#9995) * Add instruction for code coverage checks * address comments --- .github/pull_request_template.md | 2 +- dev/code-review/code-coverage.md | 38 ++++++++++++++++++++++++ dev/intellij-images/code_coverage_1.png | Bin 0 -> 392408 bytes dev/intellij-images/code_coverage_2.png | Bin 0 -> 281814 bytes dev/intellij-setup.md | 10 +++++++ 5 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 dev/code-review/code-coverage.md create mode 100644 dev/intellij-images/code_coverage_1.png create mode 100644 dev/intellij-images/code_coverage_2.png diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 6893cab217ae..3855cf60e6b8 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -44,7 +44,7 @@ This PR has: - [ ] added Javadocs for most classes and all non-trivial methods. Linked related entities via Javadoc links. - [ ] added or updated version, license, or notice information in [licenses.yaml](https://github.com/apache/druid/blob/master/licenses.yaml) - [ ] added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader. -- [ ] added unit tests or modified existing tests to cover new code paths. +- [ ] added unit tests or modified existing tests to cover new code paths, ensuring the threshold for [code coverage](https://github.com/apache/druid/blob/master/dev/code-review/code-coverage.md) is met. - [ ] added integration tests. - [ ] been tested in a test Druid cluster. diff --git a/dev/code-review/code-coverage.md b/dev/code-review/code-coverage.md new file mode 100644 index 000000000000..b5c52391452d --- /dev/null +++ b/dev/code-review/code-coverage.md @@ -0,0 +1,38 @@ + + +# Druid's Code Coverage Enforcement + +Druid code repository has an automated way of checking if new code has enough code coverage. +Druid CI checks are configured to enforce code coverage using JaCoCo. The CI checks will prevent a PR from being merged +if test coverage of new added code is below the set threshold. The CI checks filters test coverage based on a diff from +your PR and make sure that the thresholds are met. Druid currently enforce branch and line code coverage. + +However, do note that our current code coverage checks are merely smoke tests. They only verify that a line or branch +of code has been called during the test, but not that the functionality has been tested sufficiently. +Reviewers should still verify that all the different branches are sufficiently tested by reviewing the tests. + +## Running code coverage locally +Code coverage should be run locally to make sure the PR will pass Druid CI checks. +1. Code coverage on the codebase can be generated directly in [Intellij](../intellij-setup.md#Set-Code-Coverage-Runner). +2. Code coverage on just the diff of your PR can be generated in your terminal. First, you will have to install +diff-test-coverage by running `npm install @connectis/diff-test-coverage`. Next, run the unit tests +for the module you are working on `mvn -pl test jacoco:report` +(this will create a HTML report in target/site/jacoco/index.html). Finally, run +`git diff master...HEAD | diff-test-coverage --coverage "**/target/site/jacoco/jacoco.xml" --type jacoco --log-template "full" --` diff --git a/dev/intellij-images/code_coverage_1.png b/dev/intellij-images/code_coverage_1.png new file mode 100644 index 0000000000000000000000000000000000000000..439cb05d90ab9b3ed7284769951ad364d7f9589d GIT binary patch literal 392408 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 z zFTl8Q%~F#>1J#hg6^=IE1(IGq=fdRS&{1xb`+2vG(4uust;+%-sZ9C+drZjQV2+_1 z85Tqp2d+T-2~U;(<=g2@#RjKXA9gnTrlX635)}@pYT&7!OO!f)@GH}>U-RaX#=e6k z&oLf2aqSm_&qUxEPS=A>wMJ+bx>el^!JX@^s!{*eb!(`=k`EBumVJ&j4LU~Vb2mBS^ce{z%D9Z&5?Su0>K25tiTe|J z3D&1SIoX^V(Cu`~VhmaXKyzT6Zib}ihHYZ@BusKr@={{%8`FAY^&yEa{6MrF^Yj7C`}7qbOZiN!4#i0(v9JJzZJ z8dB{5aF|T1`!2bTX|q>wO!z)EP)OzS2>aoviNiXaGE95oe>=L$4nWi^Uo1H;0tBBe z$2A|vh&X_VD$hZAa?r*9!*Z1;c2fdUXBu1H_lenznsqA7qeoRhRI@R1d)TAr@m}1Y!_(npF59m%ours zMxg>mka*Rm!-8iqZfw>qBcuW&OVMK7|7ur5;=%hK{D2WcejqR0B!TG!6${W~po7V& z-{(5#j*~`WMiI$^8Gwwm1eewhct^wiJ^8oZK_^z=A($uEY<(BbC@F}t_MhD0KQ__} zJtbK7JzvjKl^PslJWjv{Sgxc|SF%zvD)cwFM&OONK4=j8$uRvn^5(5{QRKBo-Q?nw zGNQDqh{>nBIAgVxM_&nn+lT3C=M>Fk`cD5@AmoR?{SLV1kv`o zjTDn{?HI_`r|M^`R&!a3@t#p4vC9x65tFVpb~qmK`H9IC8hLyoW|ZC&MqOVji$t?* z0}5(tnhAh@CKY&`CJWR$p}AqDh4n~UPwx9dJ%|^?W8GqKI<#z-_zs%l;{7)QZ}%o- z;}cU1w!gVKXc)2MCd+r7pS=|A3Av%PCB^}|&W zy}c}*jC67@=~MNq|Msapu+Ieiu|2Gj471~mT`XTq{_ISh-UQ^Y|4k>%0V_7Ia7feB zai*@*MLetoi`f@>hoF?ai2>J~$f+>hEr>o$eCeXT=C-`D-DeXRqZ`cnaHm15tR4_^KP6(AjQ@&{ z&MrM{)kM*0JXKIgZbeAkJ=H}q+kK>;*?fc%#aea2(Y6SmLY zcVoC%F#j5SB=K{UYu$<>n+)s=J1uztuJ**dVMJV|UPdZx;t)=(QugW1bYL4X>3i+| zngDEx-f{i+&t2q6_dK;CLnXgW4GF7p!S1J^{$}@9ci;Hq{eU-}=Fifh(fX`i1S5Jo zI|NV>hvll&0A<4Yi+BbN{{Sa$s$Fd-LZ?24Mv(DH!e|g?rZMaD#A)s~7aY_n8>PTm zK#ULen6j3G0|&e@(QEc;2xJqWr4_-g32K6DlTEEG=JmUMUflHnRYP!z#-*dnNCj`7 zZP;Ziaz`Rh&PXlaSU5#{_rS_<_g&g(>1u@^Ec(Z~)ZnC>YhH|$q<~mh$HZ0^JibJ z_^VZck;t8iGwSQPrh<8toFu#D}(W`RiypT zegg+*6suay-;I7bou2MnT{6)X?t-o*IjB=(9igFOgTbO^ zFb5ZfwF+tQpyw6t#^FIz;QmoXp%I-%P7)YdT$dERnvPQGykZ!%0=sjI!qBT>kcZ8$$Gj{3?6Ob<>DxIlMiR7qopHc#uZv$vi zVp3h};(-4zbb^czbNX1%aJ=;8^=!mU$_wL?re`-k_yFQFb`L8Nf?Y240a+!^6*nW6 zho;U)bY+wUZ<1&C15%aG^54~7_MG@(sON=|7J5=|>3(>!ZJLk+tGfWG&m0D1tiqfO7Ay@9&ouabFNDB33b8c=i^q+{zNiyne6^cOpprRUpZeCM;5 zjtI)knHA=@hM*p-f*U}1D5v2bWI*GsD5<<$ZHt_1* z>q106wgJm?vNQ-lIH)e+T258mxhM4~p|kS+>K;HnsXo-Nnc&VI{6zH=9nFlK z677zrr?r#k#$%$X9`K#M0`MK|+^Um#VZpk72qwDaMsXr7S4B7GPwQ)AifU=?VJRoj zAoL)2$mtc6{45qw7esr!2NpC3&dJNlNnYq7WT@HCQ}5*&MtG5q*g32W@tc-VCe3wCh7*8N0Tm3@1< zGVfg+ypUjcz)>-Q6@>D)fK4jzu+hK`CR zqWq@Zy`N=NG+t>Y{PkrdZ`P^S%8~0j3KEH&xcKTN%J=xBS1r{>X$aT3b|Q7}C;!|d zm(oJ=praiFJ-LyWz683M;KBYP&Dt94qb|y*>81mgl0|w~D?E43w-s(TpTXQ>%>DC0 zcG$NhasAs6L=Snf{;dRPNaq^ft`@Z-(~3}+UU_wBvC9G4s8+W|ncYa-sir#YcB*t! z(_&4TPl;};^ z)wj=VYIctfBcX1Nr~8IySFcyhXncQ^z^!BVR`-VAe2-4?Xi5LXYrmGM4aGzf0!Ata z-st~?e5P5;b9u(N4nN4tQdb$lOHFKYl`^BzY5jAM$LEj4`DvS9pNq@(C`>uv;RN0Q zVxzjR>=mxZ3~x7buMkPIuwFX`I@vA5+Vpq$tLq-y)>p4LK36%u)+d5TPZ&T;SFTe)qO!>uW%wnWOPg#!`?(#l-9E=O3LC*~|W* z)u9Y#iBqD0NLi8*^p0=^nd>~0JHL+Ctu`nBdZJ|L!%0D96qhwQ_LvLY7bc3q%Z550 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 Sn6v==_x&D+-8s9w&;B0{95W{X literal 0 HcmV?d00001 diff --git a/dev/intellij-images/code_coverage_2.png b/dev/intellij-images/code_coverage_2.png new file mode 100644 index 0000000000000000000000000000000000000000..8eb0c88629495d3ea7d5d96b6f03fbb9830838a4 GIT binary patch literal 281814 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
l#XtOMT^mW=M%o3M0Hp`BF0AL&F%M^ zpqj<9AFSB$>(SHGqisp`iw#4!%~Q+1F0ZW8y}7aV)7sFb;4mrRg`Rp=K7v;*K%m8! z)=iI`R|ewZ`w5PabS+JhO;n&o9t?`8Ar!?Ef!k4L_zr~9QZ=8?^w0tLCkHxuq;@R1 zABfP&`P&gHt&E03hB@{QB2siP9GWKJQXre|YAF{kUL_33t)kWGd>E?Z6S9I&-*aCd zGN|0iQ?l%OH0F!xt^4q6vB;i(jKBdNP= ziN#-ghwPuyrO)xGT#l6;UhOZ=lngcLj~ne#$&ZD+A+T6NHI)l1Y;e3Vt@&meb)ViK zYr*hsn`)i`#Vx~v>Xyi>we@waLHUzV`ZmoD$?j(~5HH05qtn;|=9!M{qpQO>i8I>) zqu>`%X+k^_)6eP>+xQ6-&9r@^kXsjWzP|8;%N68qosuA79}JFld$rAAa7y|rUU`E{ zp=5}IAl)n#2OImqx$%D7oC4DDUfsm(40#UK(iK!Jotc7dSANyXM@dCfCeZjHfrzO> z*`)8a>j=RIyIQ5hBMV3BD@kopkewvrgW?wR8F;D{k>)1D{pz-Jm6&zliy@R5eL_`Z z*8&(^Pq6Cxcq7E$#M9&s&%A_j|J+`4n&=OU3g3qYw-7X3b|Kh}dLb_EQtvgg!VB0c zU-OgLiN~!5N6;4xg-Cj??5E+}bubo1vt zH)G#(zE z$9^CZVd|-neuUqqCzb=&)YA&Rp0EMRC|b<{pq-k*RL~nkOpGNIdPu8L0hWv3XW*tt zg5GA#tb;Wx!4jo>%g>VL6_E#x%}wzGhrTvQ+76BwQ*875(0s+^vV`mq2zOz@kzT{p zNLu;7ii(WEdaN0EHB zJ>D$4%fu+x*0rVz=AJ7LiVJ%-%7noUi4|+QJJI7SYX>|VnS7~H`mogEO7=+si>@KER&!cfc8(JG6XWXlM9y=kZZ#MQGo~_DXAVV36)i zg6)8jgwbz07K89pnqM^AZkuYI`z7tP7w?8tkBW%|xJFxITWw@r755I4RLMHuRJ%Q- zot$onAa+#C_^dMy<#Y|DcoJ8G#fG0mmg4C@{d;=@EVAis_G7h zYc|Iw(ZEBPqiDkoIyu=543p3m&Dp3VqpI;(0E5)1pT`p!H8mwcHnlK zxnSjlV#fAbJ{J#2PGHt^IZkV8{sBIGN73nsj+qU;84BC zl<44m^^%$3$lbOmyE}R5Dzy8@58R|8;@SsV}o+u!pAlP$Uy?_OQq9*rB!kZ^E_qtnHMp2nTqvAgx3k_CrG(6x4&wBwB|%``32 zZ6!PK4bzKG< zspFxf!OGK;{N2kNe5kyTb8q8s6t65#x?{4FzT`i`suFVER(bYBlP8&mI+>82DKOUM zRkBSMB;UQaV1Pt&Gh{;?L%d2QCxNbf@W}a|r3H>Vxy4F`*0-1Zdk-XEmCQjbXGQB^Ear%mMRMj#)PpoIhqElFcDWp@mG z$f~*BNDiR<(>etdT5pmv&6cZKG1D8UG4pd0aAkk!hqc=O0&d(z6{!_zghm1q zlKaO@dK;P?xwH*kg2$&HljiucNv%(546?`=x?ynl=R8|!33#l>xm$+`xaMj1P*CKC zxZ8w$-lS7v=PFsBMh~*g(VpwndqjDHljq7Gmx*wY&?NENZjA|SBpwZimM#J-a z4DI?=y}hCs$fuKWni!y49BiBoecu*7%lLhCa4RA*+!(K~6&e1Pl|5{U^!mNy^|p0_ z688d}s7aG)_@-;U?$JTiCbuF1&2W<$2K(dAkcaa~2j^dNsM31N`f+BdCvk<}-c1Ij znP(u`zWHMKm@uVfwL=zCeft@Z>gO-Atw!$L(W834?#b=>5r}9c4Cx|D9uH)k&j`@_ zukWa9>JAR@PDkic_D!ZJq=y1x}&wj9|(7lt;?s|BdS}X8r`&ke8 z*>wF8cg6jWn|v>GBq=<0eGxsmsBL<5Fj}5Qq7Wc=SaC@IyhD;+lG0>ppc!mvc)vX@4 zoW|2g@Pos?b6%L~N*DEy&itWAytsH6g&>SuBT7t-*{?vOLw$alO7C(!?oxLN(spi98 zqa1Rzo#w-ZOQW3Ig1bFC;I+>jqbfMVv*pN$bh2=)zCW8FAQ)lR$iziYECj1_k)v z#7*nL4B480gkXoTklia8t(2Tw{HqcCweD;G(ON@)+T-k{qrPclMej00(dX{V&QnOP z^rOLsO`|J9VXr62LpnPul>;RPWdXdecxpaov=3scDw#4oMPYQKbYRz zQ>4~deUW5+?&OEZPTHd{hN)K^;4$lV-#rwF_2M(m2i;C9O3V~c1+_8Z7wq$C_cbm@ zMZ;_h+sjh(SF<)Ere+`^bH~DiNi$7t2ItJgJ=X$iq&>AqwPnKwL_MdB^RMEW>-sVcK!Zu%_It-~G?ws@uNX&QNn!Xv1>myI=h}}8e zY`J(jfS&$YQ{y6TiH(TI=eb?<~|6bS3tn8raBB7BOq^#yNzVkQv9|UZC=QlMRCpc?oW_YO+FZi`iqJijU zOi3#aow2gFYd|TT+oJFw#BPWrg=yMBC||s$%XJCp>f8Y{_G7ykg1>}cV9SIYXh)Oo z{s@~Y5|C>hWJtABd@kHl&q1;9SD>*AJuGlN3WYiB1L=$=P9-U;cU{}|giK(aCfs)S zFS%W1;uh^gu5No#^G=OYlZclf?fQM96k;|Y^{2an|HKiPjnIâ&b;63q?KSPUc zTW^TJOW)ui%KhepGH^_-2{LRLwf$o|_=XmXS?MJIiC#pYdpE$F2jfz$E5kt>+&Sj49|ugE|E-AW72SV)~XkS&0~Vj zLTzqb3PSqD;^xRmfbO#U4;Hvae;MXavmyKfEAf>BnlADhGG_N@^aGdNsu3lu)Iyn> z6h(zaeoZ`->2uJReo>QP4vTGTzCiwpr3kH|j*e`KXy$oCme@)Re$~3>$x$npoIn=rjKmvqoZCWf&9ylaO}*vu$D`f5t|D&WQ-&NKwkvXDJ1Ll z*G)(Ew{>S*8|_&G!=~AP-8tMB+UNauVg$OUlEVMY3a|Dj_-%@{pX(v zNPHIvz!pOdZTU_ZUfar;>Y;jic4i^O%sjK9)$-`J8YBDb!RtTo{`beH3av=Yksq6b zybM*hz$?PG-a5DeXM%}*$z^4bq~b&1}f^LJvBo+5PS5%7xqx4svl0-_~&>E1z?SZU|?I7x=ekX zyK9Rr-HKOMC+!G$S+g*bQexx7#H8jAXUW9G)Y~VQv)xxGxlkb-lAs&L*p#T^oeVPnk^5H$E}3c+uf+Fc7jcv2UZcfXAk$vSX?DTbT8~ zFLuHNScz^#IBc-@{{(=5WV;F2xz+L z5?6lm5CYJZyZc0vthzj@t>H){&^*3onE47!5)-qvKGWi?fSr{1?I7?(G@$6fXjlEvy=$weHZE@klgBwsDEM z$-HvomCs(^5f`<5@IH!*uTyD#<;4A%-m_QtS-dZr@>FZk+5YVtiypw6Y+N2JjE{{G z<>loufGq3WPR?-d0jAXZZg4$h?4MY+u|Q1m3vGbg0-!|f?BkhPcJD&_R(}dPxjbY{TvDG4UG;srrGz|HV1NBcVc3 z?6%uoIhX|?4Uk3?CQ_9sT6zy=Y5!{i+S<>zQwt4qQ6FsZ;^T~#Yj(d)6_|HFv)GBD znzCtwQTwp5oF*__2&CP@T@{P$;yQ-R7n->Ux7+8Ajw48h3zOX2HC7VD&Yh$c92{I! z06a$;B0wX20mh}z{HUp^8Fx_lcY5PjSA>}l4M`|?v{gzeXdLI3f3JI3qk403e#|=( zPqqH!m%nt8;qCCpGR?&Be&#wgA))cgnUs~a=fxFjqiuT6vzfT0EgW6J8R>6KG7sxXkIes% zv9At@YTw$H5J4$LrCUHmx*H^wQW2#==|*B`5GfI9>27Js5r#&(JBDr;7@7fwfp2r} zx#~H`_q)e`+@mvl?_aHW*0a{qkNJH2!s{H5obRDN`HjB|t;`KYn9sh5rKb#h6%KPKQOzEnX&y*p=Akm{e+hg0k2kV(7=m!v0rX>;C;oL z*yEiDi3%qa^Z6%I)j5#W-qPbehW)+h;TIQPSMMmt(#ZG`Ee$TS%;==Y+gO@M076_| zR@NV{?*4;^pFf_i1;9Tbj&IV-e%l&y*)D~o*)y_mvpC=B`sFb{_W%jnK4~0I-MD9s zREVBwWE|0nJl~*Inyh)$8*!}zuR4--lFI);_nf{pimVO63VPTHFttIO;GjiZaHmNAFc_) z$jPRb=i;C}J&pP5zZgG1&J+n3`YE%8K4VY=uW<*ao;xgnb#Fm0=ZR<0c3BZ^Gm(*; z%`jMtUQhGJ1=HBMpp8u0m@7h`>gVC~uDdB8e$a41v6e7o7rk`1e_o7HWzPs~YGV`A z+S)JWYs${e#mxZSEY31~5}4F_+UL8wD1*UY4*Zb^jt!^U&Fx*sLp(0)DFL^l_;|93 z$VEpNW+Ot-O00)ms-1$&X(|FSj*weQY*p6EjAoK#M^XWaT8boBOCqQ2C* zs(J2Yr1)+PMaTp#bsVaUZ_&%GbPO^C83BRS*9d}&v>NwHNVQ-{_pMzjFWAdMx0&>G zIw!HwI7K4&N53;3NQBVI_~&la@A%EhMQnBDY*EM;10!U;16*Z_5z)9W!nQV z+&3cdKDGAl;Ice__LlBV#u0p$-2$$|N-q~uj*RMyqvYF3H|#VVK>2iauMbVDne@J_ z-f!d%q@vcI_4Y|=pW3qa=nTGAS=_GMwGv*yo=u^eJcVg*0lnV-T# z-Wm<>c7NL23e!}H)dIE&N9AKb$YuebmQ$>R4`rJ*bn(zfbN0eRb+)n2)#C#6GRBw4 zD@J&=Tbf(@_`KNruF!T0NLD*G83eaM7VMuJiGF83ayIe)6QcWLtx$CO{$%Sh`*wqw zcVam`;y(dJ3@htPavxV(qP{@u5(^7H``x%Zj{2yyf{pj)o6As&Qd6!ze9DfyyiI+= zYc7>S$X)vS4W`)CU^nz$@QDv=FI*@4a^UlRK{3)k>@Oec8S}Gheru@oxH$K*TNq{r z+_JEBMrlp83S%PK+iz0_^GkHY)`PMA6gziIsy?nb1GB09o%#|o)5Uk z$~!WhQ&^}3+M1-~w51(boQ`xcp7BJ8GT*-+R+^htIh)16kX{VQE)*KgGJQuyusUvk#7Qz^mCQ96Id1a0uStyQVErA*AZH)%~l0OF=(p zf}$p__a^b618nBXLb9c=PBvq&B_BC1J#nniuCi;H&XjuZ`+(Z=0=%>Xet5T8Ho*-o#|1~>*j#RJryvZodym-7st_P12_-Mkke0~6Jkwy`Or_(qP33k?RbUrFASjLA6DWEH87jOT z$WoDH5khWXH=PTFr>k-s#BA1-S@Kt-5m#UP#nkoozMtxqd&`-Vu(+GhCbB2&b*bTZ zty`jEgD&~Sx_3Nev^HSmhJe5vfcJX-B6NQ!#K(-OdQ^i2g>~tY7?j2ntwgbAtEK*T z*4XO2(A~aBUNcUhjFJxJ)>3MhU9C%kY?O4UmvdWlV)fR5`i`Ex`Vw9?8eBX^jz$tP z{W-1aD&B8uD+1)+X=zwVjl{idJ~VX=Tkn%N?@scY%p?iHM!La1+=tJ`qv|xye~A|9 zzr!}A;qxZOSbEcs`^(|}WsAuxdp8siGbT5DzjhpRD1kGz5VQC8N_r7r3N>X`qH9q9W9XaH$^3+p$6Z;%2Fe-pOPxp!M zadS8Gf$QpMSB#ll-Mg9oVln=lfk)VwCbj)E7)-}a;@g-y0^TH=;U~u7fe+Ud9R@V) zO*s6;m1)0|Mp{MHbAx(y4tkt@Sekl-MH!)f^o>7Hl|Bu2Z2b!FPE#b8K27;YK3rz+ z=;*rxuWA?u78gYU0sG64f4p>mt%U84AJsfw_0EWX00ZalHS33u5Ost?hK4I%QqEGqf`J&E3ptf^AL$LD#JQ>aX*pHKSBl>{}mFMeG=QNWD`Te9Uo z#i@atLK2M=+!{IF)G|(62DVpZZN+h_vG2Yn{UK-c^|8m(WU*vbO|Slbr_*qd0En+Q zzOcW%ZRi;qNp(wokT@YNhXn2Mabv~ic7FXuqo{+3&sf${ z+tmiTxnTQVzrQ9%Lho`)tfC+l_g$5s?T7fmrs>y1KjrtRCVlp`zPHcizYFxY0eaK2 zO3XEe)tx!~w0QM7uw&C?p9ge)@%EW_9k}-UqyAT^OGqLQ*z*q3#b#apcka2oZD`zX&uxc)ENyOSdqk^Z$6&zx;=w(Tx7(}Yh=B~b z2@C`r$__O23#`2r^0ycK-A9sibjHQ27{Ry$7PB{|+1MU@PE4fGvX_$b0U9?{5%&(3 zsYS>1g#%%J=o5CG>OqE`H*emCeQiv8 zWB;B7BogJBhWcOk=HS6poV)51j2o;l4XI5O_bSTFJjNh$@SHz8c}(qmN#r0UCB0jm zTb1i{Gvf=Y!!fk$b4MftH;V!YKE)sm+QN4kmauSYmo2^ZKc`3D4$b3C(zd`}c(fww!n#vvk9_D}XeNW`jl;wR9Dqxk~U>{0R<1c>r+m zs};HF7C!VT-!Et3m&y8d0v|t2&1EAozZb|pCt-Ffn*Va*$_*xoYDCdb3Wv)dbH1}fq_Zx;eg1H-K3sQC@V z=0dM|SO?&S7&ZCsR&L`39tWqo!RkxN-==ZoSRooBNswxAOHOqq4a;uJIQDC^PF!!rEU&+?3kV1&7ho3 zXuaNDc1^=luG`~k+}TP&B+ezC(?nda&zvFKu!Nb<3+Z%m+-}i^CMLpao$+ql;hmlK zd$8G>Nd^*{OPky`4yVX#_8dd!h~hQ6+^FboES|s)maMbW*~E={Z0t|YX9IGU{olVl z%Ru)x9WZ!Vs7(uWa*QJO+*JU7ywerkA)5cHs!OQMq2m^E@MLAh_yp;7W|CCF5D&6w zXi>_a%?x9Ly0Eh>gi5n>x&BvYWEhw;G z8}32>y%hdsb8t9g3f+tHmc{B@-V9Ct+!x1s_2lGe`0((s)L_h7(4#e_@b%QUlINPw zZ>bG+Dqd+w_q&1qp6x)QC;6GCCY|2NURgCsL#rw97>1}mYw^1Kb-*W!u<#F3S zZL*M*<$T9NFLIrcqZOyx)WU+u!|btwLS!7bNjtSDf|s9zaVnRrCqekXwE%e3jkkOV z#tSH23jvINsIzk^uuTMI%g6PL`SwG*{8chx`AK}tL{c5%f1JVHP__glNa&Fb%D-I_ zcOcnv{{4xyzfY(A>gH=GXp>NI4VJSB{`VGoHKu7gSsq{V@*)x{z=pjA8rGvkcy<40!0V@=^m!&xAK&|w z-ehCKpJPB+_J5Nw&^cdA+SrV64QK%6V^c2Fk-WZYQe!j-ijKV@YT^GeTc z8q@Z;?DOVK*;q2{Mha9P!vsLu4XsZo1l^ZR$5pktJW%s0=hM@kN0wRchb(%fl7h{0 zxK3l-KEKMxpM(D83YXCve;uIzOt6`57MmYCg?c;{pS=myeMny%_<*MgKhPdZt8(?EHo;_#`eMVOqqW&_`E{NcZeNAE`Z zFuGiy9PWe6;;S`M@)X5iumw;|0r*|P5l7!_cR@jBc`Sj z!;lQF?(I+L3%YyuuXzs-54Rt}_~|w&J%`e--Z0$qL3CZpuT_2#`dqVG@-K6Tkhk3K z2{uy)di5eIdOmKYZV0M@hfIw!WoA!*`V;@hm3Jds1F!>8*AdSzj1we2avEHpnq*Q* zzNg2Tp3VpJ0a@P-d-^}ocxc$k9%H;}x|(n5R^!pRdr-j+?X3Dg-sk5W zdV8};%(Y0=Kf);Y08m}0fQf!a7=afraiE(cN+r=VBFH$i^WSh`DFU_AT&W;q7 z?kn_uIRDW}%gv-U+!k5-(|Y|s#2g_2|JNcMoW6X-_x6O@Qb#hKyvLgCvAs3Zjomoi z*WUfvvaCKjf|&tC1`{W04JF2#>IZ`EbQNIhmKVU*3=odPFXjXwT!>(F!JU-z;YjQL zmY#$PMv`SmK|QtQWaF7BJEuB+V=^*w!_9HdN^_ghkU4voGHvP3PdD8ueh-@eqJ`cy zJiXapTn)fpFN#0}NBc8wFCT+Im4zjz8)_$wp`N!Yj~IgPc0|HbzCz_fx+Aj`?p_9z zn&8ytQZ6T!PLB5WWx~{3ikGk2S19ClrG31_VCyR_5b^SZPV0!9El4|^Hy2W0#RQ1W z>9HL=lB~O*tE*iRUpcRyz}Dw!4i}A{qnW!v zUZy4R1EU*aJ4P!R_s+8YndI%pYtf2v6g8b}eTibzI$kT5S;EvJe}vB*!8JA)XbIj@ zh@mt|DqUo%DPI`;qf-hHQN`L61MF0Oh@7jeKVb1;TGrdQiPv=a;w)lR%0QmJUGSaS zqF^+t>0snC87*K7U`rI&YmL*t#adlVCG|U8`mdUf5c~Gp+8WKG3=NLLG){M{SP!o1 zyc^Bz`0za~#a4fK=S1fdjc@wi(}mx@$jbs<;)K4-+QwP`#yI#YPRGY*j0UGwyEMjZ<>M;{gz=uTSg^&;uzLLZO#-G~4f zCPYkk6BJ1zjSy{ahA}Q?W~P?6O+JLStf=FC|58{O1+>D7d&xI~S0k^W6xDKr7>i`g zrPOkb@Ik0Z&C5!gS!P$(bk7t12ENFI}Ad_=m^Bm!c6D9RVODW4abV6Z(`uy z5}grz^>B!P#$Dlhw=E@uYW8^Z_3PJ{>Ua^L+@^%(M`gaId^nEXJsshH1Qh|rd|$># zwfs`qGjlhRh@Y(YZI&8O#B!U27$)fB71|$mMwPyq;4(8c{cPj43IU~WJfUghO4cSf*oD2PxQM2RwOTY2VOh`SnR#C)oM%$GwxcI)=>E9O>@Sg zMJ1H-fS4omv=#rgv$j6H&q<8;Gl4j9*E}mLs~zgpF*iTo32eoO8ZMx+vT~YoR2H9g zL5QB5uzS)5GqU*K#e3g>1t-TeIyh8p@-E@Ki0Ej>T(fhUI}jWGt-6=0^cSb7)#a}p z4+keF%?SqV1tp0m;TYG%!?#5*e8MIkcgxtt-=m#&$cKb_=^<#0|F^7`q|r|I#I!8(;iOND z@+R~G5CZm<&w*qX`@W)yhtaGWmNB7fzz>2O5T@4FA>E0bQnJAmW*A}zBH@WsADg?s zfblUh&}aZTcCDPDo3=ofW*zfC8wbKzyF5r8~+BB_kOkdK;ieQ6}}K==uJSbv$a%_Fj=AYrH)?>JJW) zjc8joCOMRf*8(KGrrUg^LUsS~idWEguV1ou{!8k@Rrh|x{V*K=@$DkOmStPb&s!4r zJb&{|M?DnK58oL$8qdxK8uDJR$gGQ^CA#%R$Nv`BStCN*Uw*+ojBOBnfCEZongBo` zfl$SWW)ZNSo15Eau=2t2cwhU?%ekn>F0ZKQ0N8H~qO3ZlrCdf1o^?Po$H(U8d7l;I z=m4dxy0>9bj8K3I01>u>qK3(9@R;}S-S?i80J3_xF0*n^;yFwuBNB-wQC9^(>KlzR@uKXAHZPb)#5a!q0*rQ zc_A{u*3fUb4n%u_08Q;?rAocu`AMJ0OGcG=!!f0}&4g-U4nPL^$m(0WRDGfneG;FL zfGk(JIAEV$AKJ$oiJTXCQS5Ga2&wnPXNb8~_z#!OJMBT(8zF0dvfWHvlDu07kXcLs zd6L%qa~D%w%J>7Y1YqLTARx0{)Sf7dN@jZ5PuLTgfh{u6U#Q=&a;X}e8h!|nBr4;$ zAHgZgxw;I8V>zvWU?ab*rZrgpa3tSu;~*}zmX6(=QnLwFppxkk$#_=*AnT;xO`#Cl zOwNGVZ@dK@%gl-jwU876+Ogu<4^ud0jsX7)ARn3fYm+b@Iv%zzXby*@JqXR2oh;UG z156Z!kg6l#REc(gDV)7fRUs3W0j1v}&bRrh z8cxb+0)z*j+ZW2>QwpZ%*eleCo0Owlz0yO?yd|f2%@~dpxO;6?Q&4G_?V8@I`iA3% z!N-4kNB1D_j9HXb{-Evs2WFBe#nH~MvJOC#=WY&`@890<6G44e4Gl2-*-pj&gC{^5OD46963j8{?ocT)~}5@&wCNsRn<-8-1w8 zS4M-GfuOEN75uBXei8D>yR7Oo1XJ~`BKp0KNT5O`LC5Y#A}&xj2^3ygDZB^vLLkrz zl>#9nM<+NuD$H+wN3y1Y-G%;`-d1NZ4db~}nXo|d02#ME!to%`Kb>GDC$63-nT z8-&XH?QR4P5+M|7*h+L$)Df>z&TDK&MbE!H)T$n?Rc!Xhi@eykIq%f*gl#z)B9d!C zEj_a+PP>B)Cj%;;8I zTd!nO%NiGOZPj{UY2|`rrznHH2=jtJLgp!eKHY_ zw+qrp{mvrnQ|+ z&>>CuJG2ftHpK!ofjciFKJiK5~p0Mm9q4w(<6i6s@>v$uk{vMF#I-Pn$ zG@vNOx-?Q`cRT|vr^aODi3Z|$u_(rqTcbF~H7bAX+gmhbTXnzck8MDGAED$l24uu8 z?^X4Q(Jyu%4!db$gl^VO&R7>aY5+R`ZlQMv#ptTo;zBQ|x;odptiIirT&~D# z8Gm9O9A7UPnfnK?^}*qPCLh0(Aojl59MH89#WHosZ!_bt>X_Z@URGOL`W)m{^NMFj zGDz&o5H5k{$LkQWcOyct;;tRW#l?YAw6a4Go`^aM!4>j6{gqGaD~(nq+STq8)&z(? z0Qv)fJ>&5nUPhL-V%-zR7@b9{+0rb9O&>$NWS|uA*RvIHF|^u5F~zvlp&+Wnpv^%r zCRz1+Z*GEPfc_w}{UZJ6yt#(wE~N%y%qCiw0to=p!`>hffT z1z&=%G49hG4MxA(O4`v|D)qIi{S7xgc;n{PdJ_d%a{7{^G+JF(6jQ*SLDO11TJMh_ z(<1LK#tmhik<*Ax<1Ls6V#cEGj#wweO>tk;v3~9G;@iN()9=+665niQ+>tsHd-HN2 zdD_~St{3O|Og=vNWf@!AX6Wn(9{FP@UrAp!y6CNO0^*RF+%k*w%>nA7jM($$kYh-> z{X>L-$_#t>@fO@yypE;wHoS>|WdB_zwFjbcm9d zl=)IT;sFb}-~{W^pV7r1pn*J>_iZBQn`b?F_h9Hf|53&I_p#TCVQ+{io~o;xQmdQk z`()dVr4!8a+f3YQ*zuvhaBC=KTWc<62qK=ih5*>VP7_50M*H2(8mwR?V_$$?qfs<6 z{0*s=D8PLB@dX@$pmM|an0u+GXG3-6f^UhCYPV7)0NQWYh#RvYqCL7y#F$i5cYdI9 zuD-V~2YUcs`wG-e(6}`DtN_1b46seN`MqfLP6mm$z!|(gANY6Pd^#B|SG67)scPm~ zYlhb%5yA;?;#-Hd=ZH-GLjgQcM>$!@ir*~ZB7oFYG9azrCRTVkt%4xdV#|%t1%Bqj zzxD_W8Kccqrr3K=7XG`!RW=8n`_XeoRyq3(Cry402tTp@^Ia7$ zMz6y{ac*&TyUl*V`bLvkhKspkiT=ZBoaw2;72!Qqk_M?eyP zgBNGCEz#~Hr2~-tS8ntasCpdcL2>bX?YSOM3b}tT+Gd_5+ZTrAh`Z66d2N4_e{FoL zHnd{W*>eW#VhTv91kZ3x8}j}W+6OWrC?nz{e0%;I=DsA<2^?pv*0I4-oTsq(j(ynT zd-xZ?@e;lY&%$|l_$E<23~sih+^y86ww;)q&OU7K5qK3frj6)TV}>LB)x5TgJ>Xf+ z3C<$lN#4zB`K^89G6B0K({`arc)6p-Hl_07TFLcgka}}iTnp#;^Na4Ro2GU#kEGy&4A5o@BqO-hu$lZu;z`!D`P|8)NXxq5+6f)^3 zm^5+EAq_Gw{hpL|Im%au z@0c}0SBpKD6<#}0MpMRKmBt|Iji1+aP!H$XZ_q6Jq{xd#5-<7&CI_9S1xn157)uqP zj$URE+$IRX7Q*4(?68fbn`JiX4!b9+bE``4Q3b=x81-V60wh5>lJRp~5&1yeiR@~# zjtM1Q8?%t5j7pR`ye+v-@6DD}`X4e$9ZG6`p`Iv3xvfJCNF~JiB*Z4=VXSzcapis1 z-K(0W!Y|qMR*Y$c>?;SYG*ynx8)jP+!ULK<9-{t;#;AweIr9>)LV|9#@)fk5aY$DiL=lR{JCzF5(+={f{55S} z1GKl(S!Xh-fUcz`?-amWWW=cu~eu<=`8s!#K7$I+HL zw1*3p8FvgUwi$(xZpbQJ^4Ng@3m7>As$!AqzPCW$*B4h%Pb`V@UK*(ZG?4SuOUO+1{l52%uVR{QQ$TMxccXEes%@fdZiI`~;nEm`|@1*VXAVDka`0OWpZy5c)CO zL)`IHF~)qsD#bHFBLD+1;>)BwrG;;Q#422S>l8d+e-1?$T%YW3tw%y;6d} zr}UVVjOp-d`e$YKSNo#K`E8!Op2vg&%_-Ow0N2kN;R@xSN#RBvy0!V?#6La|3mK#R zLaKAuEnjy#Fakx+K2yhW>DB)g6!1}Pz}@N9r}x<(9AvrNn}DOrQD~Xx(LXXe%2;f9 zRzbQC(A*sck^->I1hT$;8;=WiJ>4tjo3s&tp~a&u<}!^X(1bRzwY5RTw!ufdejc`Q zf(F$~%PUyLe2q+ppd@@^u31C0z?`)mI`;a~XS%K@cYsuo5;fDE79h%-QN%XmDd+J% zl`kO*edep4$puqpl&PTs~mRi0KdF#SUF`9iOoiEQv5 z0lOXSWs;L>N)KWfwP0oY#C#0tLhE+V6W9A@h0!FEZUoM>?F#vt2RZ53jKD;%?f-4MfR7Cl zcl%WyD9y=?5j?zRmtjW!Lv*%xb(IO1$2SB4C5|vaqjMNJpQtDWbWczA)p{BCQ#~&M zjs-S3?hMj9x-;LNSzlHUFeUuXopN6On48);*%|88I27Yz-?e2+tqcLA5aT5pvXJ@O zX%tqTxA7kGB~Bt;$!wcyDK1yUNjZe#(1eOq7*t?lu{7a~q^ug<(W)g-UnMJ^+nZ1k zx8e@KEDi+;4j@|uRaC0KyL!-VT~4Fp#@db%J3u3ko}2ra|EgH{gDWtq3rY6R#(bf zRrQ*R(dj_`#)P#wNAmg9=<`>vm>`JFiv3>k+AZ?!zBun4w>Hi}vvkDU8hlr{ym-K^ z+*h#dzi!1prn-YIHAOwUCth?F$fEz;Y*olQ%1qB9JW*>Zj*jh1=xn-$u_D=L83&N8 z69rrze5WdS1hC2uMz%AO#ropiUP9A&HMO9IZM{0rUT=UumUZ*jaVyFCzdv)Ya@~sm zti;ModVe&oe7o+o>?X%OW7Dv_8|SIMKycjXs30z4NH8&=u2@w`))zKr^TD_;_LVLh zL0mvQ*_7@2pb<2BB>&~)zNyfW4n(|i*18Vx|9ifsqTyE;iyer9&b}2&5qt!D2Yd4dg5=@~tEG8NZq) zcPypD_V?_JhTFqIrWDiJQlaiAL3$iR=sG-zUBOEvf6JZc!WXjLROqyU>nMISj7ncs zuXYGTk>+fh)Pl=;yi5wJ5L6nK-39#OPKoIk!DYxrQ4l8}>S}=P6fHkwn>;@%?C56) zs`N{^+*-Bu2TmW(DF)Mp^$guedARwrJW@7*81vG zxMoIU2DPhRZry{n5X$Y{-DPA&bK=zXha9tPBLNYb0MTE5 z1BQ~iGahvY0EHfGsk8kfSFW?3;u(0TFW@o{n0nfL;_$wsd6}|zo0zx*;0WaCjCzTk z?a*r=z8iq3QBw1?`|r-Zk=tcZ-c(72o?sY=44x9ic>fPJnMv~bXG%N6v*WjWegK$| za&};H6ec~1fFNdLe4I|D zP|qZEXiM*S3XA;Sf?L?u4g3+C*KR>#!$};k-+N4#xkgQU#d5x_Q}Tk1vQ8ZCw#`V< z^)d}^f4CDc;k-1?sZr`24z!0Ghv`|vF+2oid{hqRYxkedXi#^R1Hg{64j`*b;KleD z^K}EDL{3RLp%*^Umu_(ZF_~fJcG*WFCZp9NoxM2>1Qg+G?8wZI7G5n1#at9X07?qM z@1+l0EB10-@EoXorKT1qkvnlzH(D=su>5%_r*AyP!nL+Gi)n^ncZy(!5BKwldXbND zq5RUh5WrA4p)8dJG=h;>wFP<;DBz@OBH>c34_I8{Jk=06!1Ew_`{BU;ez+?H#{FmR z{11TmQtvGhu5suYDuqPjPrsDKu73LY^~$`)?hh(Ym)fsbI?UG0U#MI@@(U`L89`8{) z0)+Zg@}q>U-V!qH{!vhyd-JzPmv#LVDscRVB3^U7xqau9OJ5+0nm3MF_qc^!$@MrW zouIwkw85{gJY;fGGq*V32{bmfSm$CgStU~@QFC%6oF2zfz9zNUXXD%oELB~zTu5i1 zp8I2binH(dw9f&LjP!KzOHIi-tIJ`<*t88~mecAmIxiAbKNd(?WOs_~TNRaC$lYzMTJ)p>p7HgydAHlwa8L~q5yJp=4P%4<8qswSvD z-7~))z6EUs9j~#2SICO>La>rQ?kq39Sg&9mUPQEoxLW7`e7M89CL|FD<6I>hxulXvM})F zva(IZq{AdtlL~IUNeU3>C@gFZ<=D&pX#{8=bD20t3%v_2#+Xx-8m8LrBQ4AWfCgVv z*BG_HskT4?=~Kba^0hib?brWd*-OwcUwwXWdEJE1VJ%?0RxN(RLxaSBHDt3;6?en+ zG7&oaoKoEVsWP=V*%0S0ykLEl8c2nS6ES-(ap%-lPW()npWuLJq;{XxVtPH#MdHXt z{P}*ECoetUo z8#ZAhKhh9sADja;?Qe75HyI(#n-gW5{q#W3#xkT(8{#|9z|mE(WB|qS;>9T*`BS?s z&0zQK6Kc%u1-6)kh6Wr)VSi(^{?`%aN=F~%jj~DeGQWK7C)?P0sK%HtUz3%Cq*M~N zQ)0}U;5M1Wy(bq!6=}iCJ3w)FNGx!MJ@DHfOFZkn)`ygZa+tj-QH&FrzVGyzIE5@ju&k_rCI^+`{?xJ2^Mq)bq(GB_5cC>YQoz#fNf)VL}}trX7+iT z$ay{<&z}^X9jjT5g3MzrNUz|*#H&?OvR8!`?1yQ3-)I8+Y>o?rzjuo5Bjp2L|H(27 z`*5Y+>DIua`ad^F>c!R5g(@XU$+}F0*DEtgl^fmh5Q&33DwpxGFZf!>Ih1w3U$(aR zo9Fcxrz5i?`q|85=}?1zM(VuN=)xY&wLGmF7M)N*jgA4W-liV# z4#|8;z2S7NPtn7}$AsDTr4N^}VuJPa`p`0|3BBB+=#L+X7!_ny=R%URfJhWyRAtfN zZblN%sl9Pe1Xtvn`qs?nvn3YbRqWYKAZOBOrpB(Lw-X-{3MVZ(_uF6X%6v?em6q1{ z>B93t9_P^FdP+7?UuS3eVg>AYGp+N$%m^e96CM51z*bB@#&&CrrvaeMf)8J+>3n#A zS2dc|widfLvNCC!cPc-|m`eLc1uM~esm)9Pxg@^(ZkZwIgOJT^9V8eC zJZnM-uqto(7qS1jD*E@YhwZ+(uX~7zta0dgGq9UlVOkk9?RLcYA_ME5NQGyTVv>;B z;B!@s4Nb(cf!)TiSpu8bu+;e>ESV55y+56K*+Y`{hL!C*)d3GgVR)?@qGNkz_Tgn7 z?s`u0=l7fj=)D!+57rO)XEtwrBh>bK`z*XWPE?_^tSo862;iN_0|3y;p^YxyNh*8z zxyMnQ3Q)73U7`Krbk+-&JQPQ2ak`tu?%BvT1F#cj&wHJclt5%t=LOx08WnbaOR++# z&!2CKb;Yno`I)SA0r5%-+k!F<&nbbE1K|0)trLQ>V9~RFDWE8Zm`U&; z^&2-BfSz7ffInivFDttM8fy&)&2#&}m(KAw_PLK9 zkObK173@|MkRcoF0IPYspG_ff90;L+eKLMFzSGUkXyu;lW}4vgE}Mb=kD4cjE8V0k zX%_-6CsBHG?y-C>gZC~28dQrfjg3poN~L{dc*wsm0t}RslarYqP+dcT8_>RR34jgL zy7)|F;&5=|L@kUZcD${J^nC{hZ=H0;q(rTH9UJH@M<;KlXqhE)lkso?+3$~42;89- z$egIC4x&)e2R60)))HF)Yo*;p48`2RJ3XSMi=7GYWqM<(3MSA9 zJ42JZQyO?_$m5Y%5d(c!km)afuvB!KbWmXrNv54P$-ZQt{bD8)Awy; zjgXP-UMd<ixlpReSLgfuaC5u|fCp8nGrCC%7$|$tG1RJ{ z%iiZRg!qXKAB|L*z-{6?oiaqGEeW`+$KQYr6}>y%>zCY72H~}(If*o0ZfC| zw~>h)od`6@t47>;z-GKC-OO`<8;12_ zZTD5TkGoC3pK6ZSc)eZLBwKde+zm=Nz4m}2?hH^+W4knWj9E!ZX>Y?u?}k7q7HF8C zM*z|e9N4Loxj2A!dksiQgAz~+E_6RBGLa8ShNrki2$&Sg?JluwN+JOcdPeh@Q8E zSa9)P;^N8)w6gCNLyUGPs}aa4m>v#4g5R}LCAa5 zino8yRVWMgy*yShdz9=kjA%pszu~)oKv+NR04Yft1_r-|pfuaGicmk5oF|hzw-a{h z1~!TDg3qt`Cf+;`*e}s^4aFlKPIctNO6(Kweq68$D@2hiG2$^<_}1lqyzbE)a?kCe zMk>^_lJLJYncvBRKmSc4RU*Ja-gWLlG*-R@Trqq;QaW!|A>98XKJe5ri=vJxKZQ)& z!KK*h={eH)=7bU2>?hs5@pdiDWM2u3oWT`tAvj?{t;yw4UES>t@%Oi0m%jP-2M6WnaDRJ+E9rf^gcL}>sSikP4BIF*uDS0j#4x`} z%x__#1FoqN0pt$`-0mSaFeR_q0fI?N2VGkk>vQ8z>b=vENM#anU4Sxc83oYY(xu`} z*MrWD3=ecShpqa;xPan1WgZLE|J12JLT~@|sOsOzYG=k55Pzm+Or2nVl1$VQx8B8- zHo;z5gN)WL5~7a!4i80q;J@+)$U%yTin=pWGLr-pQ=4x2lgQc_i|(WbWJL>$KQ->` zGJz;vBDbq<)m|+!b+n3!L|;xKVPk?pqGE^m>|EImUU)T3@j&?>()C*6vw2bC-{5wC z+cymjM3-!>oBqxu&Uo1}q6~y=NEx2P%fMV6#*z*GWD^e2)Rw5IJz$1wEo0gzUlzIT65q2C?; zrBdhLUj5IHQgSph$9bpyRg<)Cc@ymZDz|by*=5Kcq^o$90u=;~Bx{Ina~gM(oJ{$f zDC&QITxGZXF;T$rNhue%(O%}`bjdqe4624p?cvW=?We8U04K`s^nf&u)1W#tJG)a= zr6`h<5z=z*bB$q$B43iYE1He7M6#}%f=MZfNjcdrbQ=~|W#454WYv^jG)M|M9Dlfd zS2^smaFKy9h7?e=NLnXxkO*bL(_8{DraE6%nJ+)FKYyFv90Uk$@;(tW8yhgFSJ2I_ z8ZddeY~b1*9i+7DjQK4?DxzjZCy&i6EGj(M9z5i+>R9VgeNOy$==vYqKTI2)k`kz_ zb0$SPiC(u!Om}#P+3v6wzY2PLTC`+$tUjEH%In3GY{insn?eUIK=raM*nk{W$n`^~ zZJX$`Gk0>^v^XLv_c>3OxpU-ZiE@JgZf7~gC5Kvfvo?O zGhiiipJxp`+9o2Jdj>c*9=uoHG@iLwAI75@B14k+j+=ERe7J!F#fZJ~4#-C9JW?wT zMVz_j0kC6c1FlqrMQQTC&@Z%1`nPa6hTFp+8}q^iKTZ|LO;45CXuu%@s{VM=Zrhwo zY;3@ka8-`q1!rd)3!0UxSx!{*rfL>00tLuwDEQE@3JVKC30T?!lEkMw7Y&UnOKyD^ z64o;Xcmktr&b_(bHyc}^jN8C8oP%a-p%wThvjmmyrsxty6P`eE1I^V!EyvRJ9lag*3VD)R)c|L^P^+Zi{_Lh+UI`pTj{5wfo9`3bHpfb833h*DE;3Y@4V&< zORuYw1ay{}4^twFPU(`F;XiP{v=I-Wxnxl3wimqz0w1l(<{YJ``h~a~f|ODY#Nd41L^MN=o_bYcTQpz2;EmwRlA3B)!^wio zWcWl<+3f0ma#@*McAv5eWPK=pUCknk>$yUfU0y_Isd~r^dH=h6*kPCwgflliXFc>W z>4@TRXu_|&K3BQc-TuLuozC;-y9eCXQ$NdX{_{2e90^D5RV;?lVTafmWc!R#UW0U* zw&F%Fr@;Rd2*4eOG#dE*vDKNd@VnOppt*naVa zTWG{XV%^~WA#r4j#Vu-TlUG2OPl8%5g@5|l|5)0U*moVy^De1gA(UrR!rG?@I~%O%+KPVy1U>l=&|Mzbm!&54!`CFibKqyB~WD?cZz;po2h&#Aj)X+M8tm?(6Rwjxl*A zu4c)$nzT;-y8*wuLo)oFUj6@LLjGk+ed+a|1p5vJi~YO1UqPoUq3Kri{w``Z=ux19 zHHnqogZ4T4Vbs-@7Qn1xU}o+Z92;X76`i)*)&&obj&%6lSRNdh5VSgS5`wokUA=LS z^X<$R2%mt!{g~nxpQ>lH_0oXM*NDhSR;lIS2kUTnGVC4&5PSRo4?tQSfCaF9lt*k! zTHf9g7MH5-n~hAi*P%oCs8+1sgih?QSx=O@l-*-s2vJ{2DnaBjEz7D!oh=;l*OtW{ zVb>fq?K9dk3~@6ZFIlwiZXCiIU+D*mfVVZ6+xx(`w!L=JfNs1UE_ zEozmhx`bL)P0yueV!g1bDQyIag#~QsxFhnpuIB(Nb3|MmEzpv#q>=%e7$;JIX?l*|Fsmc*U@VAl;>Jr z$g3>#Wv8CW%eq!uPJL?>&6}*QekVx`zn&Zz(%YbMjmNFA>1s~*&0jkE>%EM z0rb=L3@fPspgoce(!|hEJ_9HEn|l_WVfOX<9+l&*h?XFwmjP*d%cG-}snFiu-iXbD zhmcwTdd%f+Bm3g#CW6C={JvMp#eBb=7|-8y+cc)tpRvjX7Ck&$3oFaD?b9p{O~DQWH3}{QT7bIIq*L!zLNAZX1V!kgj>OanD@=^bCFxaWr`Qy;qhYsS?@$p>ml~s_Sgkt z;p$T=uG8JnpcpL=H0Vob=c<#F5{Nhk-{RNjbvqw!MQRW3!x}u5kWuE7kIGv<4}Y!I zM~(`kn+w02=s9(_N#Ok0Y&aEPx29Is-gslp=6yjvFkL4&?Hw8%q;WshJd24Na*rQF z<~Vo$|1r?6J}(P;Vj7phuzreMG|(?~oj`VKWM-5<7=R51IUGk-MM z49nScOCiX&5*Tk8Et&SZiEzPnXQJclY3|TaL5-U~)E!LSFm1-8TTCu1d%i27CM)aD zs97tEIt_APJi0WdiBsR3NCq37eyLoUCm#_W)>`CDgLG-$(STC&(4Ff=qXWP>!wh!u%12AG(F zCMvOMq$LaDw3S&(UcU5{Pwp_Zd*A(4(LDp+Ko}{ceO``~2Cl<$Wr%#DrUR}uJU5*G zynSq=MxWy~;GoixBdB&{M{saR&N-k@o<6@!N;~VqG?An$cA^Fvn&Xw~GhKPLjxx0; z%M6aonRKSjn84iO+V$vEk+G}zJYU=<_@J)cVWS6&GYV_P$OMa^g|-%oh}~UI`}<5d zz(DSD>UJ}Z+FRYF5Soy|$0FIh+i{2?X-<>v-9ZKNRK4k$SgpEONax z?Q&GM_RCQ%cxYu+xb}d=MdZIiRgV#xL|npxCiTv|P?BW>%BOBkSTA}^OJZUM@eR+m z`x62d-M)p{NyKZXaqZgo3toP-o1$u>qMuz(mh?**n<;9x z3k^0s-POwM0suw-5M+$oJHrTz=e>6?;1G>`af^dpC~zGM|ANlKx$+)$P{yq+KWmQK z-*DBV5OnI)+vr&{QmZq|9T*rIz5A9^Sy4d(7{K%d}%+sw(1IJ*Qh#nejMGDQK(02z@S{OA;0>o=gnabOp z@O0UF;aDc*%{J4pFvbGNSKH3fQ9jGJ%++H(lR@OlW|WGPkD}UMCPJo5E?s3T2LL1h z-q@Qqn1gfX9XIUO)*%X=%E{ZAUq-g# zc6!Qty}kpJGZe12!U8c(+PJ?pxv{a4>s#j7(^{9*uNqWdX(=DVDZy|wps}nE40o`& zpBkrUyAF0zxVpNE4qMvzceBIqvPKK*t92M;f4TG5EaQKbJB*u=gJ@EjOB|C2CYiy+ zUlLYd&t9TcbW5o^j!#Ls_Gw9yyixf?b@}yP*3*3=RaLdw{%$lx;;`(kC>7$gV$nis z+OALoe12A9xgtZJH8W994&M3Uf#N9?HsP%f_mdm$bN$0u;yqEmzEk=uJof9=ASyld z&o!@HpCD!Hj@m+P2!;urHXzpyd+~^~pXGh=;}o866mpr(ncpE<-vTU#8s}j>>@8x4 z=sGDmIZNc1^A^K?0HS1Z= zO`@PT;e?ij_Z>O{F-S#a%nf`oP3*!eUaJq86&1B5)jzcsd;a!hrnKlJlrw zxT$-6VtkOi_K?S(etswzIkEa_U3-nahx^VOR&tN|c>?=AKz?%E4OX|B%+8;1)Q)pF z{I&o(R-mrsxKM*k&8i$-tF?m}**jI^TUs`tbMx^*X^^Nla4US&SdZpM;oOp3_3iEL zRZ6CUt(Yo3V{dkZERAA$rTv~5zB(H4H^7$Ao*uO=>IA?!=koZ6$=Y%`^362 zmD`0N8bZQ4SqxO0^!pa}juEHY%16`|+(F^tdiE0!mBn={IhlqFtrxD78uXeMTDhEu z2dTRECH3Idj)fZ|HObs4rtuj=L$d4jZ7^8L-0PDT@{Z%Jc%d`4nVFeW{eUG`W8;`A z4SBn8=$cS)Tmx@?!z1&KEC$Cx8`rY?s^|;;ez(3kFW_V z`}u(C&JOlfmw877NcW_Vj@}=o0~zV_bNpPpF|W{dg#fSz4}uvPc9@2H`}vy#m%+SrwCda4a5ST-qJq@I^^k*aT}8x*N(0nCc3-U>WO0)E%{VtXGt)D^s0E=W*^+ z(~qJw0jqUm+%QO@M#^n;(DEUmwnRnuyEQGVsRSXaWn`JT?$eO3{C1%bnIm*cg2z3lATrgMZWZG+jv zpi?crr|LL$=o+G3s20obIvU&P(3|L&tH!McA=~-DTXtU1uyq*v&)|vo-c4OJmQI-N z=qi3sA?1Vwlrm(BGO|m}ypF*+!L2Vm(&Fu{(kOr2*VDMaq(J@Jl+LoW@3hW)_uMgW z_T{*A13E)XGls*hDE51!5E>elp?tNK{lQKQ z1UWx-(5ln-xCNoDF?)FEd9H*L*rV$-Gl4bGa}77aWxs;IKtE_Ltz`;<+T5ZGF=dww zV^n{Q6!C=YrfZ@Lb_y=J85-AWE;rVly?`5kd}j{FL1>U@B{f`w%3f$~|0Fs>!l^ae zh&XG||JE&d_VDU9_G-bhTc)*~Y3@r-lsq=l!3{aJ5g;0_rY9z6Csf!$Jmk8k2kDK% z7jTa@)gH`x%H8N4c={2mcBIxeySU806@h9LsvILTo$l{Oj@94ADetjjbOwgn`i{2f*ksrs z7#e232b$tIDF%jyT#ysv*rz5#UI4uqqeRrGD+(Gi!^f={+$Ylk5Lvx<*Q;vb7)ZS? z9;elErqrpm?6Drko0n@(q~#L?R?H-HbSB^FeQ^MJU_0Hs-Vsn*P8q_WeDVzb(Y#i` zz!S>(`O(9N)ntwIgHyyk@+Ttpqs0ndLAuCN!TMM*@ zc2O7baY%Y+&X!lq#W)@xl-a{wyDbP5$VHQclFKzra!|@e*{ErUGj(}lC-Pw-9Y%JF7(h~85#?K4g zfHk}#mha_$)OPm8Ll|{hL+ptbU_NY*e6_k@*JKy@w<@~7*AGAy{7L<|rpppE|N3%3 zZ<|q^DU;n|%D`^hXjAMR$X<7$#2uv0r`yB@Nq0He`}$Lhi^Z(0t*Dupo*u~xKT5A4 z&iBBYqb*NLN@COMKChtxZ9g^m)aagcuhL!leiKY!Ay{aR9+0F<%enFn6W@UT3R4WO z?=!u?nahadJhH4d4C|9qA(V3KtDT+gRX1u{n)Zn!%nFCYLL9y;BZed_*N_g?i ze>LTQGcxN;=)-^=C0Ca}5AQ1VzunH0xIP1}A(8m@s3n9mj{cipaNAjZ0gLGDxkidhjd5u2 z-po9v4kn=+NHUmwWRh|F1vl&xxF~;3C>&V(iwm$BE-Z}TGX4s^Xs|DU9I{}55+ze%f19PoMm zZi$y_6Lp!QvPD(3@W}_i57dH!8tdGgt$Xd#pg?IC!SSE#98{@=SudioylrE3@4E8j zWex3-=zksm=W(f)3z{dMTvG5>Z~^zfEuP;y6|B@ZX?j)7-~DG=`TvD;R>6yMcDMqy z%sBx+Zp?ovt0$s)&bxytW%`-<0T1>HD^iK-)!yg7Va@&i*gwAVAbg4bFMt1sw+fe| z=~Q2N9{9H!>3{hO)`d$~sv~zIE?oG_uYUN>-$voQ1+yeJ`H07A_b24P&R32FJ3_DlSxcmo*zB-r$`~k-HM8RvDS__P5io+uGYh!oqZxR#qb78Xc3FZhyt-8#l7egb)f3Lp!L;zov z*SJ)1X-8LT$x5obvsi_k1c+{S5OzsB8_T>PcP5`;0wvV&l$55Ar%1uQl<|zL5Y1|o zUhCcqpWnv)@8;sypViCW^udeSdTl|w%5Y3{&tv=qZKQOCEunXwkmY0b;UlY~MUl)3 zmAN9uftdgAsOU_+y(E~uheXOee;(1){7veiOSOT~ZBsTjwvdq6)gtDf8P%JSnP?=I zxWmQ%3?f=t8r zs+CX2v?K%Zgz!BAvK?Qq_6c?4O4xO1uHDW5Oo#(UpG zypzq|nSO)r@4t7Z&TDb0;_LcUk?NPZ%K~WyU&T|?&_(&<73|knRc%P=OH^Ikz2X0c z=nm0GnY0|AepR*Tx$k9~ZSndEWv7Hp>a>L2adrfkU0q*yYXg14MBg+XchuYJy{;;% zA@e^rYW{GJo}Xi-ShMr9l5Z=zkFyJD-figv}VT$+CnT+))P4=1^(O?wpx%-+Th50li;@Ssjl4d8j_ zQR}wzO~P|Lrk#h6;Rs zp`@(5MyPUhX$agJndMF}b-M5G%#2l3qNro6=xp^8x;5|Hb68`naKs-z^4dwwDSpDv z&b}HU50V9+(LeX&=L72-p}p&E^MWx$9Xp~e{t%NQmN~$?m;Z!@! zFQv=&G$|^2uIHDnjTcOa9co4l!w*vJOQCV*cQo9#9NQpcTl=otzI3w(zWD1-Q}T=b zvH*=73j(eq|Fgw)2@{JJb95avEPJ)xKV7Slyl9ST{ONFh0qb>_p1A$fcceC41b{7j z2fwwetyE%sc$lv4Y!N?3q~)HEpPx1D#B)@5`K=#=57s@jc@$Twcv`&cLp4U#a)*IL z>@%FEZ!K+ox(qsxr839M?Vg%FdC8tC`Cd6L??qL^fN!jRm0?s!ud29sWz~lv3js%` z-9BnF@0<7?a@-$uJKE9m3R+WGVbuv=pLhzyb`p_DCJE8VM6}oH$A{lI+bmEx`v&wl z8CdG>8`6rt?=Q|Dvh(-z`$RTY##_|rG`Ll#-8_8l&gd={hTKYq3N-oiXWiVxw@mPZ zvu%VM{?LKV4~zdCj1W13#t_aDnzbAw2$9p@$1OlA-}~aaqSt#_Y8Ug^YuiuBZON)0 z7HC`ZCN7HC^wQM@LP`Ha4XGHtpuxgN@cFP?hL9VKh6LA4k)23&&L0-bO$Syqk}IaD zj#z)f8}EgxbGf6o?@e*T^a_eiDj4b2nFIE0)6!n0r=+Eh;Zb!(;^?6aU|8%udwftA z8E^j~$bc12B>=vBnd8lZMJTDOt1CHvZnx`w^S%;vP(u!rr+^aOKDvHXn}3oD4QKzm zQX0RA@6R`cK$6-D(Cl3F#AhH2Ep_pAdoxj~TLf@yqnKI_$Nu_9y%SaAiGC|-rSqT> zLuYEbmaHNY@8A;D$pPHhZxvcJpzSWr~M$?~a^veHC7 zw%SZzQ8-XMEVgKsLWj8RjFqJx%`;UsuSKgmB2RD3ZhyM!xa74U>gGE|+^26fG0MfA z_)y$g>9$-~zk^=jx@84gH)S6;@5awOS|3fHKKaO|5wLA1FLwE=9exkLR z^!4>sddYLf@W=A#9NJvi@RUv zOV`vsHurfQ@UTnkzRY6Wk(2$HOavo$%O{4o`J`YVb>1=YzFAB3`=PdNk9DjryIXTp zQ~8YHko-bYC<-OGQ{PTTyyCQeH{KU?);ZuV=Q^ef$f4Y^zJc*o6>SIho~;$zb>I(!M!0c?CUE!9J^kp z_r$iVuC`7$50ZXqEU;+&IBSI-JGZlh0r#5Op3WD7n&c9PuAeEJsMYUhnTSb=k1CHy;eJxZB zPq+)=MZ>g-&cPK(+jO7qyv+M&yhpHz5KTqu-hxJoudsBg%=h4aYKG>-)q?(!!K4?rE1K7z zvJO^z3@0b2)c{FX2N4;aJk<)NEHil6{{B>7g7#n!X&toG^fnBh0eMwps#ZXKZRNNXV7-m~RAtOi&|IMty4 zbaBH3nhVjTa6GtY-6-hZRIq&Y_I#D6r+1K84#LOdM9&1WPI4tlt}G@r&Xj!ng_cYt zUv?gcuDCG>gjdY32l8B=v3>EX&?67HloF5DGDCJi@42iZ548rV`&5vp51lxpn{a^? zShsLHUvrYfZb_=Cgv45zuxyb@0 zceWDi@ydHrY!Q4~K!$drY`>EBvdZROeMoRoWfdrPQ@$_Mpr)e>c-dec==MdU#%NK- zGkk~u00Qclo^oAz&1edgVfU9}W>a*IFA9$C-0~@N&vLYCG{gx=O5nE~BJDOdQq8J5 z0x+CyinB(|iVfSZhp$D__ZsAS+J6;$Wmxrf!UHtxL<`SZ>z~+g@2~iPAN0&@zT?_h+g*&r?EZa z+d0*2Tw4&5&p?tKB&Uj1!dPHguusxA=Mk-k|3d4nwYaepjG0SCGS2%|?B1Xh2gGEe zx;GXX+GTRgr?S8@z5)%Yv!8M}nDlqRKbXB7ojtrNN_+oaua1XBG>Un-q8DL~^ENt* zVEG6Od30yFtpDY3BSK+$YpGy~O@3}m?i1D6@9Ax19>A4)9TxMbBLh#_(R}C8x7tfc zT^QS+-5S$S1*38eBZ8O-om{>wg<2QTe zPp9c#=$|UC@#iKjNqJtco%!R*+lc`f|qT z%e!!n3~m*{2eRU=fr0Y9As8z|xdp_8Hmo?$Ho0N4i4dg{!!ONJ%7MElURK8%38TZZ z_%J4>{ThJ8ICg9y>L?E97(DH>x@u2KB0JQh5oK;>V$6$aS{Y>gEH}&=iBwq#7#5L9 z9vViJAU=asXz}KLrYr1L5)7*nTur5+hY}sB=NR_+xfoAxQ$2Jh;zGkl;QLF}h1s~s zy|MwftvswY>hU6X*VYc@TI5E^EZI&Y+NEXI7*A1)fhusbJhBoQNUBuv5>gCa@7Kof zKBa*OKB*xMYAIRylsp1}a)r8U%Y#GN6&2r3m}5)(-@6a63)p=-KFi@6c@dk4%_L<5 za4#`jw6HMxA?ti05*Lo!OapuP>wB5vK$032!0U8U4s*z^^NXg@^KF}-qPW0$yQQ7` z_p-!%GJq3{eE+s9xFyr^Y?SVLwKK7?;7x(ugMO=peWqTM8w|tdtQ3^*6CQ5mK}T(J z+iWbCTSa&0E-?m=u^SJ7Y2et(3wtbrj@SWc?@r^=qzDE1vT2wIUG<^|Zh426?? zE?sJHwMvAgT(P<9)c>VkC$J|i!MKhs^)BeOR%S25>}y62zOSou?|5NKF>*u78M3p+ zZ6C@3?CNb;>m=tyKDws+O!aLTIk6dL8^V3p7aGW6*@1B*!!vxPC!E#1JS?+zIOJ;A zf^$tKFlk$4KfNyOj#grEe2Ii%pxSLm;LUpfQqjWrAZV+xbg#R%3#0*Uzhevy|6!5t zbX3mEg7Jy>aGzl-aya+UUU86y#G#|;f(nJ~;qDI0coHX%WVg2lwv(q*YMUwvZ1*!5}iDvx;|ru-y6yy2*pV{&qH=rsgq^BY-|J)rvJ4z*5&u>psy88IC1z=kp3^E-mTj5oPy8@V zLvztyuFmH^=bR{QU*7YV;oHhBEwM{qN5aKNp-dA4k6r%1nI5Nt>+bqXGacJibv_OW zkH}g!vWJq?P-IMS&^(Tv=O3J^4r($sR;vOyh3V-$N0M2Hc^$s*D`NCN-3SC&i#S#_ zDs_(i;?Dgl30*_mRPYPlTHe{jl(B^b(Yp1dHdDJ5w#TJ7l07jCs&--=to~e^4?X!} zx1447F=4E1BXUKWCKH8*8MoeyXqsnp-QeP(k>d1g(XOH>Q23JlDuckRPEi|EH7!-U zD_R))?3s#VBThBZ3i3wMos*V?MQ2#?bgVv1Y#CuB%Nm49Z?L0xV^4FVwCz26c%mQh zXu_qD;pBLanRo5e8=p=`Btqy6dA@5=HKh68XO{1dH9Gux7Sz!*!0y~|Ah|Z^TsP}) zkJ6F7T;=w)Mv$wOejbJRNV|jV#j4y~ciw}kDciZg*Vuor3 zkv1n0m!^<$+1PZx;lhHoc_G#+k+HSX?#dBUE)$*+sWur$kJHg?jb7(~)`x=w2hzKE z1_E*E5FhOAowe6YavvjNv}G7x-mkmGek@*o`gTns@vxy#MFZkND6bVDsFJTDP>H5f zdnZUVaSgarILGhE(nk=+I}hn++$ZkxCe3=X{Gu+X!XOVR+# zRa^cYGL)C|C#wb47K}hLp{a1f-p*SqLwUqc?^?n)1Nxnj?~C}3?01=CA{Nz}8{E}5 zoN?Y+MB87}hl^?Kr7h-p+X07^YQ`ID-bl!lgjN)-mCExyIwNQ#fZz)Q+(UC}}^NlNXI4sjMQ zSK-((kj!swmH}S^_#Z$j;0l(La+dIpQP&QYf@Nt|8bmjLqfJdz_Iv_KT)G5tEHpPD z2rMxkl9ZM%gx%Th!|slm^kbkkH4K{AcoX_Is^qwm@wF>Co5(|4=%G zj=2w4cWx=f9x7!HtbaCJ*n_xp-Mh^9C%kFrOAmsbCdb2XGKH@uRGqCgIE4ADttDE~ zNp{`zIby@K#3<5Z-!Kz(FPwUKT(oE0AX@LllI7ptly9rkj8{fVaojZ!4*-p;-p(6c zqPI{nJ){{4F0}|gCQcC&DI|zEx(AOgJkk??@8UKob7sb!K@;mng?ZIleBe|IPhg04 zC(f(?tF6V6#PpzZd`xt73P+RNL45pWYjo)N4taZv^Y~0fE!{Dac|cIYDhkplnW2>~ zX~q#-P`zNtKb#KnT+{L~c`&t#@A|{OCdz-~rUN$GJ;90~d$FOQ4B@wQ>;g*YE0=@E z1|gK}4q+Tb~?h>qX0wG-cPm*TTq@ zgkphd@7SBmafcb1XR1ntPuz}*62?q!(J3T^c-yTPix87=)xljF=1f09%p^-QJ+o)t z_n#vTO6%MVQ;yU3X!g<(tIvxa^LL85%8K7{@c;P8=cQ-8;CcYNG^xO)yDL&8^v(%S zGeK#%6Jrz)|9z42L8wCl5k+_CntGf%_s5374SJbLA(R6`2$YzCp5PFi8R6nAxx8^* zKXrheAxYKTJpT?C@xxvHxVEr@PX2)@{+j$7ZefwA=jdUESY?_SuEZVe0IcgRQoJN+ zV`DSAgm zCQ+;#8yUNlO}AA~1y~^SiV^&6z;MuTXYSjk+e)cTb&%|2BMaQ2(IvC@Rh2=aC#3Pc z-u%I8QTHRECdZiYD*)1>i5fHCwY*!^T6MNwsr|if7;%!idN%#0IEv0bmTecCaDq^d zD~sEHdA(ee{ZCC&R{Klf2%{Ge5Fij10^WLbm%lBp-l_D+XGn2u7dfz(R3K%(J+(Gd z7k}mqV_o9J*=+6Zu!YMn4z>km0ufE;F1p3n)#wSl_8M+yy)EIyqk%AC=N|_{Q^boJ)~cN;M=Ts;cbl)_zxi*^&8{G8wBQHnpie!NrW}Sa&w3l9s-OlbHhFbaXf}&PiPjihsdQDYmXdF$! z{08F>si;Th%}58nx0&BNcq?(z1v9J5TsI;1Y^U-&zaj& zi_*nBi8x_%+km5}^X-9s+)((GO$0a?25c@GzCOi_4e9H>PKa%W$FMsUqexyHA=@{yo zJ+@s6sv$logOSkWuMcBJpBS(3t(l|NDPr4j^R*}H@@o&D?Mfu5_N@~7O8Huq)rv4{ z)S7UZ`x8YH|Dg(a0nJ(|u{X@A+~)8DeL#iNz)XIvqjET-2DK{fT?P(i0s=yrPf-h| zKv;rGdoj}vR{O$rC*0n$b?F1hOb?vUpLz-xyDX$1*L_hTeQ0+_Hz{zT_*Pwg$fa+O z_8n|0%#_WKjEM2l59@{xqeYTe-~v+MxXykg5mX&aVkV?trR@mo?ha*W?yLh;n~EYM zBOp@c%d;D^;T9YI>C=*IpaWTl)AM|F$bw@5Q<)Kmuh|%-ci?~4I9aUks zuz{_aIikDgENFTJO|J8XnB$zCs#RMgnXn;lFC%?^tc=rYYAL?{OzX~Ia!=Mnz8fEl zNiM)@su+u4Q%Aqt1(l- zCym0P`XWv+ybBa^dlQ7^I9|92NN35OzLs&PEixpydHNT?-%Z)cfv6((xO3+j^FWQ0KX>|wA`v11D_%sVeRb%k4wqK@L}gQvqV={dBGFTB;G0Ht9F zM0%mhgOEf~$;b>9&qed1_|qrG5{UFh^adm=+q_-Qn$fbrQt4Uc0A~F6Pn|+sZU~%r zW8K8&Ir${jA78b9Sj0J%o(VU6`>OcX2mFI%U2XXo>y=^)vf{eX5#@BQMZNy_dsay& z$>Td6W_;m$0D>{6f6XQ85U zb_mz^1V-xYmFHCL)8VMRTQPMMqw|Eb9WvcFK4A`NT{CqOSVDwj;KwGayet{_D_i1P z4O>tzu}ZB$ae2N2*^Y_8sKl{C3a9|b3)qW{?{J>|(x;=}M7h`0RSk zR_1Yp4!bH43I3|C&S6tpQdR}A9*)tb=Y*UJ9nEsEk*x+OO%R@ru=9kC^f||t*xFi6 zgkJ2we#cD6KEIhe2@=I2H|eD}K3;9$<>bQxG&g;9RFFx;Zes42$g2l5Ae-jT~5+4&z+_=TSVEWNZgMwr-QH4D= zjo6O)<$Vg}ee!gLFT)mj3OIPZllM+1dS?3Sg5an9rb`Iy)0i}O?;p9rvT1MN`4p)9 zwYG6QZEqSy>mupvu7^>l6;GcMTliOwm5tk)J%4`bW%~Y*oP8X$MB9CvW>TwD?7X}7 zW4qfU5p5lv`J<C+I|GK17$HQmCg(arBLk zzed6;Jl5_ECs9ev#a3XgnNIO75=G}utsd^=g?9O|K_W_Wa>@d11=e;B2``HUB>^@U zxo21ZjG{J9eUJ6{!0hv6?k2|}lo@HilPE)GRGHlVA@;&i{N_g7!cFu`oY%YB5TT_``o5@M5t8;<}#a!?MiM*kSNeEz~@ah+Ko=nXs%w^HfkrG0l_*Nn? zULTZoJbwVXR1DK+RpVxF)4L*9nN_dy=#c83Rl7D;2y!|1VBuS&MOoCy1*v&5uOj@8 zk0;$H9EbIP*dZRiR~OyOBx-i!0K-c5YRms1g~w5(w8l{XUpf84_dPO?&OS?;(kacn zV}JES;i41&{sYFa)m6rrEvt-iU01Oi4~y>3Mu()18m+8HhEPof<-O!@H(t@vB)v*lNuy$S@L-`w-}Kbz({em9o5$Jakz;#CBw|e}50NcEPfp$rQ|ILI}=oQ%y z_5BKh%!iJSb|x?%%gJnWT3zV1AMA~UdC-C@fh>&WLo!cHjcaou*d+uVf7nGoZYsU7 z`ckGC*`X-3P}BK9dM+zvP^ud})l_^*4QMzPMPs{B>{R;Q>XDCIn!A2b2VkAR0C+Dm z{dcFo7r1}isTVP5?|%#lp#)ID{d9o0z{JE{^#L%%^;Uq)0!UA3AQdzIM;K!L0f3MZ z)e#H)-NVjD*1xF`krUOT#j>%pla`S1rlzKrG%?9JxcqXlaA%HiZu`@RD4Q3n48N7i z00LJ2Zu=@Oz`Cp#e-Zstr1i@``$Z-JZn+GSW2*@aG}9WH3vOKTSDEYy)?TkvwW5Z! z48#TceFBMNW^Q)q{{D4)-aoW$S=j*Wkak|e{Ew&9FXQW>@}OsIi~&FqwbHNwCGNu? zbsz3-qp5y1FHh8*ZP6F|n~(oENB=(_T0>XG9zDW!U^DJ!zWk?V;6LEHLLac+*Qt(t zqJ{Zm_xx|Z``3fkV}#)*4fo8q$yI@ef!F@=ZGZShB=yCBavyFLmLJdiAJ*<)pKOMJ zDC~vr2NHV-pDc?r~$pK@QSl|YIacWLFOVLnmV`e^+M$G>W_ z{I-sp@3CeOvvdj-N7392y&qC5C_UZRQidQ&41RUA$4p?zD_~Ig%0K_iz&PS3)E7os z8GmLLwDhh-KA;LCi6TyKpXfNqNlNrvbg&;Pr{mP3GL{I#sa-U@V~@lo+vl%T1C($^ zDJfb?YO%57fzb19<+gPcb8qN1?hm`*?-u?qKjRPu+anD?k(&LYEGxx07b&facujlp z5!%2)w;($3hIDxYq1O(!&PKmN8&HjKvCF8dJKreO+|2TL(=#;fVh09(zP`a>vv+MD zyNFNX{LEkN2a&r>fLL3P+}2WEm9_JYxNXYdE0q>Bau>YA84dYwX^aF%Xu614SzAv` z8U3Sn!jn?VlE~j?>c^Gv$Dko{3r*GNoeLqy45!hXCb4!u45&0&KUVFPQu)L~g;mE+ zoXVan@jDAFt!f+9(AR_K@=2S6!|NxrDb)7SSUILza)d4OW z;GV^1$YRj1b~n|=WFh0!E}|H*8n#|4NW4fK7CB2K)#($u`%MZjK~G8Tok3;5`e?zN z5K#IE<%we89Q)S$xkc3fj#IDoar`Hs!C%#PoDyphdZf%JkkLkU|jjhMViFBB% za4oc1Qu6iXwpHRcMQ}K$CQoNA5}_K%x{RiF-Iu;C&^wZ$ZQ8IrSd|$FkcZyx2%SV{0`v#u}63l}0-fIxALg={fNgqLv zQcs5jDr~~Kd8#@cbdZ6+OjgNCPxjf^$zU%_55o!-g z$sb&ukL?1^Ijg|SRp+oU$*qEympim&aAi1-dEXW^S3qgnFnwiZ1zi7E6XvRa;--j1 zq1En@q@AIWEV*A$#MH-5CwlV9SI2&>c&*R|75kAs7)h^)(dJ)%}2Y- zQ;9Md>$5H9rQx)xoWKwfD~#iF6-NQ4PyV)dkWettQDFjGS?6@L1YlAX#eGp87th(M z6F}Q@KUlkEt_JC@@5#M;8bafz3p*{OIC7&rS+@8Lz-#TTb#+tMi{)?K?ut0PbjeObM#fJ;*vDRokn!bY zKMNEp^Fj9TmZ{Of9^|rX_zvbDf`z|5ug@>Ms*ziyQC;Z|^mv-@O`7f7?T#H{E!Bz; z2F&Fq(@0heu@fD=3@T;PzYL%+yrHzmk3yhiCJ$w7PkI?x#u9*m9=-_|eVqgSWZ}R- z5<7Y2mjc84$6|@MNkNo30=V~cinOC=8EqN8V`KU)%&pxKC;ZBxb8cgmx_(Vfkv3Fm z>Qx$snsM&e$psmKl*_(p7X70_?XhS2!2lfX-Sq9-2=eLR;JVZL)gQsDe~oQ^oef#% zi!e_u;ISQf{naQ%*Z6pYR>7?`+lfB6qK<`@Crdt(JK=;|;bd6c4v$|YF`XvO357>6 zVVA1*9R9`!kFjxRk58edtD2E3cCVo)2JR}xEq(z3pG?&tRoW~n)vfZ#y}h>J&n@GJ ze7!?M%w=X1b_=;L3%-yj|R;1Gq&wq^niM6z!f3!5VU~FtGPO4Y-dgufvPlMs(d&_AK z=L~S!;ySZqAfemE+&cY8w1Hr(%q+OLm=jvp944HZOZvceI?sT%D;~O08z)pJ4D70O zI_Bj|TVrC#Gw?;8`myB?t^;3{w8=P-A`>A1>nd=TCjvgRQ1DsmG!$Mm92~7Qd1wzqj}` zRqKb|!9T3>NRMABMoX@6rmu?FBEM~}5)?qn&ErQqS5i^}BZJUEJAmQ=09M=Smz0(|rO^!`h%=7n z<)E2STmU|QcL#fZ44G?cYPHz?nui>r+%@>&;%zl&Y}h3pBn{#Gr)IQ z8!?ip`F2fDiS77{EbOZ`{M{4}Nja$RQOO@|?8&SZ&{k}H)Ltkb;i!=Dd+B+j^a#LXZDG@!=RH#X zDx~2#-vsJ@SjgViel14P1vGD5`iSmq)HA73!jsQvfzB^QX>$|TgtPZllFqbeukjcdE3Y7 zoNLR?$tgREYg3Bz=o)oun!+ge{MULmUrH6IY>KRlPLN%F6Dubgs*Kfo(2=SxQuNrU_!@{tn!>zzE#H6G+=X@zqNvQiw4)+cJ?E zW@1l$;dZd#uIKr<(!ULHIwRzZ=?4I(TFV$|%V$B9C2^7g9JXIg0EhLTF908Z;gt1I zF{4;TiBEBHa+$%3!u^I(nBv8;e&3}w*p20E^8(}WjOSev|VI?4^CK5 z-`yI~+b_RT_SW)YiV(iq3aIL*sv-|>5>sYqE#CI>z--HU-`_)_p zBrg$83(6#6>QqkY*P)`h0@XSBqA4}66)TOs7udKC(R5R3*2S4ME2iathPcXFbP4nF zWguQo>~fv!O>&i;;&h)F-nQ6l4zbsGpQ&kIEhZjSJvyqwOwAw3v6o0Gv&E!Xy$0V) zK&>v|k#Rg3OwY}A#iYQh3jC+E=Jx}B^0vz~ZG!jL&J5ShnIBvWq*szt* zUtkpEyZ(Ef@{bwxZ&AX{$f{q@mZ@%a^uT|W6Z-u#S7h!=Cv$GTy@vH_srO$O<8Pmt zF9t&5V)*uK>G>D0|EG%2FRS}2L5~vE1YA%$)9;qtiFglLmA6ZWIMbQGyq7f<_$C(`!HKD?7V{ zL(NZ)d3SzQ^@$`$0}2BD+7jQf|8>UzB!VX%1MS)+Q#&l6*N1Pz=8*jmQ^-|r_LDoL z>UzlME!^*WJ@RdxonLmq_NE7P^q))<*#B3T;LxlWYJ7xnKkQAR7yu65)`wzJn&t^F zYusE1)^3BmBrNP!l?Kox1j=t5MhRZPmhbg+HSWqeQx#N8sOBYJi}M^4`>x6B_=8e{ zl!#j`*);HO0F~lzkGw)+V@)ES!!ttG*Vlbb%-WB-22SJI?{ag}VT364FEm+Nef;~J z>({LYT;hw^-~JzEZygs^w||d6ARz*Rq9Bb@A_CG%BO#46h=jy2bTkG&KtV73WM@Z#Rtx!xii#~Sz5&$W`gZxpntsZQHdu2(vT?BZh4c_68oasU$UPOZ^=0RSzo*LdeH z=cS%G?u}}qp6gC0=bzDze;fYo3kS-$!mhqVQhLl`Z|!9eJYVzM7H?GKyC%EkcdBy+ zdH6?Gm%$MO%XqP#slpo~{Fi1H7TrB}dvnega?0FUSy*I&(44C-NA=g5e7O?gLjnSn zXV0bt^1M6&iM2yvVgo|M7XqjI-Go*ume?A#3oljnKh@dbYHg!;+afXUA>hd~TZhdxkRr+sc#qh2hXR&SdUX!|OsvfC`_4`k%dzo> zpKB4m2)MLvY2@T!nSe-lYEp7CGbiVOT~1}8b5yOs>CPa^Tb7*T^}}5P0;e_Dn`a)! za;+*Ow$N)xCf?t@OvWmNq#oRgDo{`z>@A^f($7p3^!$g4YLFkGMYWN=ap|j9mZ5vQ zFAR-D+Fis>l zM_uP2kkpxyLNm<@XsN-7^%{hvdkpTcrrp*0|HJ}UX&(2xET|nmQA8e4|NXX>rqG?Y zYVPfL#!5k^-X5ze)W%=VGQW|%0!i~4t`VbwjbvL7=G(Frv_c10uie^r#e? z@uw!px2^S3auLUqkv0%V9JY1#Fn8&!qO@wNfQI+DZG){T9B& zc8NwW8OqDsxsCp5u4+&Un5!N+3x!!gUAN408W5g5;ESR$z{MCqKy$KqJw8MjF9LWw z|CuJswQJYZ4SY2Q;^T!IJARLf%-@hsWOGJq7=d{WjLV=#r$%Rk$TwiT(8k6HwewgdhT9H%-oIW;D=>b9y7Ib5DV!it~#V-I9OSUNAT>nV4 zaRQIV;~HZV`vuTI=7Y_c=6T9S%@-LN`BOtd_H1jW+@Qn&%30dHtLXmmUnZ%D&5pSK zx7Fj#G zrZFn=@{SP}|37U}B_vR~XLC6nX4=;QS!CYUkNM&oLpJwF3W^P@)qxbJt(EDisU^Uk z*y(V}f&6__(`z6s(s8meK3(v&p>(Cf`{@Vu>C7yD7%zS}6qBTAFiKiB?T-4smj^hX z`chW`zy{18#r5$9sg0KAQ=^MLE{M%L8SPWEthM!B;>lovu6f%Kat^*6L!Rw&;v_yY zG;)>~&hp{in*)zS-6PTc0|yEOvVFhn$z3p%-}JlD`A*Q@Wu-g)yyI(~cPLdxizoPh z^gUZmVfz&I)l9z13{Gd^Ez(%IN$b7R?{hYX7HCRcDzAFendbP?;j@{1OtF%TdgeoQ zliY-kly5Yo@(&IhR|N`Q&Vu=buy8(JreZa;73-rf3L5TLs-z3I;p_m~J*u~hI6tql zGrp=ZE(;@0KTzKr8P4a^1+7PY7qAXGnq_Mc@tJ&5zj<@;X-aC-e^99ZF3tYqUlQ>Y z;2tz$nN3LWEvU}n9S2{#%&a)1Qi=$?mmO@k^47E~Vp`0%$uVo~sIN~+o}2PUu4?M- z7@7N1;ao;;$*G-Eo15(yK(d*vH@MQPdn*sr0J}&LaaB-B&X=*9se4nwO%#d6%zV_8 zL}~i1seW&$i#q90N|4FRXHr%lUT3qe&i44RgZ28I`}Fpc554;N9-6Lwz7P78kmhu< zakn;{b2M3reRUwcR>R8%QLa%E7n{iN@eP1_VkaL;Z4yz6NE_L}E6kHq8%$d${p1}T zji<+L-FogB4x00p(e94K zAD#7!w-Zd3R*2>3J>vQBsMoHYDks-Y4rpp>PQAq;mWA;+%uAe2O_jFzo=t8M)Yp3s zLyg3vEx#m-N(a!~q!D?TE>MGbCH&UO`0@E04`gQYBbHDOs0ixA^F$`%XmM@Gbg#M;0>>j+URqgW`TaASzqKQ; zV66Vfpa8jW&RYbI!008?-Zqyoc{>xuAb+-*w-o_uucI`dn|T&xUhxY2%=;)Mg$GWb zQsF$oX`!|U1O(5ez>!{xgg_`xEagbvbSNnxY!a(7LJxVJU} z+CLCiW8L-I0`e#W01wn7U_i`Jq6QQBaxNRuaXlwMFdCYx#n{)cnAa!2dNDFgwt-6tq;d+(M;2}%xz5V^Ram9ni64;a9w{tnm>ZjG?_)8A?aSTpoEQj7^xO*%l$b;0fTOtIO#4mZ}1jOSai_RRMe zIoQ~g$!6C^@5^A$6_=La{sg}Fl=Oovd1UJ^4M^NQZPWU==~{n7hQ`O`3nP~?!m{Og7C4Q z4A4>#x7O(a@kPPN13(6Rt3Bw&mO?>yc*eIoT+mamkV7jgtEDR=0}BcH@&iGCLa}t! zl_X0A7lrF6O%@J;+Z9bpp5aNpBmb%UD!5-I#I zJrCtKs%ABYbiJQr-s7CF{Pe`2F2-%8c#X3$hZ=68q;J(0k=C2Isj{Z?Z(G}s@pzR3 z1Q#XN>PL8an0iMicsyXnXH@;AKpksD=erc*))T(9WmCU7AiNZ>iqlEmzxygr^mKh& z5!&J_b4{sgrp{3cSdBg1Ya3G5b~#cd@+Q_s@*-ww6~{ku&A3NUyErcQXp!;lUxla- zi}nM-oiocdx?hV5V&6};D!m2hCD7kxfw8?vv3zl!M-mKb|BO#(SNmlkUGT-#>E>i0Rh$cYJQmH7 z<~GUQXW*6FEU7h%$VOz0pf+?_)AF$T z>V`Y&98G7&tS3t9dxtoIF;rY-W4FKY$NFqzZK{AY@5d@_yqK6et#RKfv4?AIv$wuK z|3J_ZoEHIQ$v^aO< zLx+=({-N_uhQD2U6JeHYsDn-fjpzzz(A6z~y^D+Or(4=V^Dr+V{vQ=NiZ%kD$HPZL z@2jkj7sK?bN=;=WoGQJubMh1+5JT7n(5>WMVFYcFJj*nI`^~={6`Y&)RThXW&w3#! z?6TJpJlus74IXOnBKk+9?5|KjxwyB3d>xvM-NQ}Y+hA=%Kh*8ryCfuLjZfWQV{3#~ zDR&JIV{3rGn-C~MwEM@c_WlDBfBad15Zn9G{T}w}nb%f=QW{^kHaq8|C_oGztYUCW z+4Dsn&B<`PTf=Qu@cd-?V&-bN=2S)cV*ed#-_wJ*ZDF^bl3DL;ze`Ak@kJ9ud^Nq# zqzU@AXv_Bx`jd_g2ccVYl{&e~sV3G}!RhYMLt=QPd-_!&1MHVM+1Zn3ZTB7~3)n}h z_D96^6g+awJx;I+ACm|L3>SLlzXg~dmwmCDm$6d#q?9W1Y|Zc?+6)e3)3h_H(G;*b z&K}zYsm>f*BUaCrcYZSRI4xb5=U%Ntr`;XyHLIO$an1vi6B9QiaQh}5`<$w=I_<#j zKXcf5xj5AHzr5%FHHErwG3-2e7B1L)x8LduoFiqytQB@B@rP_!B|BSbAg(gyO`s0@ zS(8sPnTkKl+ULSB6r(C=8@apK3AZ6!XAy2o(W_kQubm5Dc`4ldJ+4^a{9>lQ1_BU# zOD*2WxK%?B?uA`j_~3^Rcw`e_@$>4c^4IrLqc|=uEhUkghfye6W@}`!H z6{~g2+|7F;;LoB(7K3SkGs`(h*`x9`Sb(gb<;#SyIF~ADFll{!f*ZUOiZ=uXOiuiX zc)#6K6~j6k}Q)K6mp48Pd^{Fxs3Xq zeg+GKno;zx&Btt5Po&5I=7=@Dr}Ms>03NQc&8*}sJz0PF&?icj--~t*D zTgjrR-itP&>Keqs;sn%JR*YNn{)EWH$a)&y1h9Y0_w=%-C|8V!YE*iSj*r(*r#QA? z1{(LKEz)BjMc}H-HARwHL%Am-k zBngMev|hm46M~vNNj%?LghHgvmj(h(8pCa)Pl(Yvk6XA6{o?5AWUYnTe4RB~wCs`>Ou;m^(M z%yiaqe*XRt{qGBI7#%hX;A~>}Sqv&#HhoHSaq5+SIg4n>ldoUgj5?Z7g}e5@y?;C5 z!?$A6uIFxUHER(XkJt;B7Ky2stn?TPgDX@2tf>5xSxJ+yGON?7v@byx9pGZWFN@a2 z7yWdqU_X3vG|6lX>@;}9;AW${%QrLXY#C_`4<+@ZR{K=Ak z&?3^ya-FifyE_uF_^|}$PXxhBd$?U3yJx>0`HUEpHm{M<5 z;UA7;&DcJ=4J#tQm}6dGr=z>x`qX^}&$w#hr18`t7d`rnD&_o= zhn2*uOM+r$mdPWqNB@|aw{ZVGht(hl?euuIUR%3e0pqiGqi^*J8pj-eP`T6H=O#EE zNIlU3c$n;_&)6?;+1dReJGw|fhh_a=`^ta*2=m8{I!s~G+m_DUfLF3=gh^wY(EsCW z4b&N!wR}LQkJ7ud@6;qn5Frxu9yEXYZl7Bl_2IN^Z1R_OkTVVb%j_{80R zn9!0YzqOx9S&^i;@Ur|`7$0*$RLf1B@Mh!Gz1Y3hwJH!6dQat2K~Zrz5XihbJ;k#i zEB()F{SSEKPnt@i4;vCqE7H$&JK{v*=0CX#e|^o27vj5jiJkE$9GVp#p=~0kLUI-a zFXN+=kl31M|eulxHl!Te+K) zGuhx33XR4!1b4A2r-1#o7@k8!kB%4b>+Al~47iOTA(X;o*zn)yxVt;9sLkiE#eJpE ztE;2LJ<|v(ojUX>$1iToh1S2Sgm<*$nK~4~b$5CB zK|8DB&n!Q-eE2g-QZ^YW=KgcOzIuHtqqD$lIyhxj`kywQ{w2)wT%r$JI$J|xie9rN zG{`-2^L@aNx|H|{n8oHXi)QMpD? zQ&E|r!)=`e3iFOpCutu`mxImKE1Z~|mr0g73oey+WJcRGuf$GvqF9%3(+2^aA4~P^ zh3l%2%{g6d-q|JRpZG=rX+5YWd*yD!HMCJP!_hr=Lc(7Sip0ciIc|i;_A5r@^DI#( zW9aLGqPWxPOC}RoprQ#j?=T9JcHA$R!gjrb@rfUj27o0Rw^Y?GA-jCvxPh@5!98C< zlxv@lvFNLt?uMwU6MD&c4MM(2Qsjn@FGHDW*nU;2WE2vBPp?JqW-dRzZa}=GO=YiT zo|E#-SD_?ApCPILLDlP%{nk*xZ%1a)7Pp>n0Dia5`a50EUE`cP6e$bcRv0V@{8%`(xtL zVuyJx$ZVq)Z%NF9uFur5>~&Kw(DQ5ffP@M_l+k_oUs4UtE?I6x;|BaW3;%m{`g4f_ z{&3O4Qg;2O$ymEKChmP&ML_z5r1OJvsOS6qtlZ$L+aBB=Fa{u|B*=CeP~7jK#V_Rh zR@aH}iD;fk#BmrrU}pX<@Pnyp(8{M1hvFP)PJZrQe#Ha3SYbi&G`9@7oWPB8=~xf+ zJpFfHm)?2ir+z+BD0Fzd_REm=&yNAcNfa|&WxJ_-qyFoF%WfNPba1Bjrxv;&Vx(R# zwPXTkM@3zN^i;RuKt_NK$|@OXX|WkufO z8wCX#mM^8_xElM38|omKhOKj(c$?W81I`3CSpXn2TRB+gG21Z)O!3$FUZacZH1LRN z9#XP=U5Zu}iw#Rpvc&%7yY#PY3BFfs@Fs4@(L$dzn$SZ(rgDdt1*0Toy0#Fd^Lg z4uo18D~#W^b8~aESQo(jMS%F1LPse3t(d+=LF7$O%iAc;+{7DMB^ByELE?ne9+B1O zg-1+Ur6O$c>Ap&zWK}7Q+)8HMOb1z`Vm&Bq2$Kxn%)h-IegA|q?t)M}@(-0Dg~LTEZFdJ_nM(-{kTSE#|4FK4|tvNBO3s z;xYI)8!(^Tnv%X``9UF3apMPZ0Ph$Dyn)j-fbe3=)sgz>amTRV;2X)1_8?;ZRjN0o zsBWMUd$7e<@=Wjd(5g_@TcbWr#?y*sy+*wp0Xs)zz~&> znlXr}n}#1w-0&5u%~73d@Pd zYV5+Z0KaWl^z+d>+9JL*hQNoJdF*C%y=PW9^si3O<-O%;gQAY_aZ?Sm%gS2E@9~>t z`&2;!h~Iy7kb{-HCX*;%C=s@4^%tD~roS!-^%i(I{}XxFT^@kbY-19v%M{t4+5a-8_j~b=P~uxvbs@cob-NgB%%_m;0OV-WX4EOtUgp)0 z$(IfjI?T>%#}b9PDsX4wSTx7d)J-Q zo%*S3!zkxwOE4ML*&@>R70j&~IKB4dCD+yon~0`+8@~_a1rE^O-iI4(h||sqX1AASQc_YzyTP^D@ik*|?rZa$FQcFS?HO<+A?V~Lxo>L<; zBA{Q(=9*R0H^F~$UE#T#^fhEQf8+J>|AUfC=$p(iXA7{NC3wFw)Mgpe(t6>Q$u@tg*3?pJN3PT)`(*q<2NvY2dCG>86(?0S{UCUs0VW>Bb)3wqZ%$zE=3>?3fCwCV?=NR*^ z-DX3T7OM3=hYBIKp)`~UNsC+c&Y2_5VC_#p7~te7FkO1%Y-_?!_E!{w33`Sq%r^x_ zO55G)b92XAgrHGFm-!kj(<=v4=^~!$C9gT60q`yauN?fhNG=#0P)n|D={$LU3Am|d zmz1bj+0A_37!-@!402bTYdd4IKBtjK(O!pqZGQP^!reY*&f-WhqRA$oyNfC7e4{%r znIA|id(%DG>wb+35YfyE;oqdI@V|5V(y*2*MO<~f_;PZ#lrZZ>gwupOYIc)z^Lf!K zp-^4mUE(5`rD}@~Rcy9OZ#>!f6D$)8Uj=k?widOI*=s9^ zpRvkQhJCz{)^OW8S90lzgx%is;6Il1110B+r)2um2-(VmD@|Elv96LO=FX zUXv{x?{mp$;&u_-c(z&uGhm4t75ySf{A0w1w3_x*jcL$~XV-Qn7EykFzT^v9QMbPS z4^K=wb~0KC`)>~cY@j~04~G<7^8u`oy)WZ@-mQ_hOra{mhDLbThSEG!SJ%L<++SK= zhFkLSwVI648^%$y)O}}@!XvKm$8CVj+W7fxzp~I#Dh*PO3w4mBh@_HtX_|7YIQ6?| z!#%--aN)V?@It^(J~v;meVY*f`T#fy%-;Yop8DR$zs4P19%H4i-}I-T-ArO?78E0U z`0}Yw(b5q;gO-JAci5u0bY8xkvSMr>V7lJj&nph+0YpUE>C=`D04p`z{6et1zdIJt zX3G;0)H!U}&Q)l!BsTy_Uc;sKM(fjg5ve88=)nZBS>n;BCKW8wkA~8?)T!rusWf-n z3d8{h$@B`M9ImyeZ=ANk)^=$KErpyY?saHD-AKB*ez?bT88+SHWB%00_zAZ^=9aSj zkw^5{rE+qOoQ#Zl<^9oQ`>CX|n6$}A$a z_E_M|3waQnF#T!eEr2lTwb&#Q3Bc(AUNY;MG-m=sp!4d0g1mfiHjpgVM>(R@88mh@ zc|QOjuG1e^X1N~++Wzj`0t&0Dc!N%B&J+jU_(;odRIXTG4p!y~?C#!%R7@ zw%E?we$p?J6kb`e@$n%lyydRbZo*^Iip>bW49fyC;wc!9b*Bs4!-r+P76U1nuYxpfLc zjI)jI@xY4JJ6WzglCd>g%XL61zvR*4*P;Ld~-Dyvm>A zz?Hs}J!%(Lo4YlxmsvC{W4{N7Z*t93@EE!rhm2ARKW$aIyAb0_UrchN!J+1bm#F7P z%xrO_q(-h%+y&-z`f%^ljT5tx6;Ke1R||+(w*Ke&$^IMbxY+pmX}~PoHc#JCkm=2R zy~X|?Kdw(F*C72SqbqLxrKbDui&}e+Ew;O^$#|hM>Dn+8i3^l#vq;xX?KRHLwuGT9 zCVu`gJ4Hp+`1A9o3`|3X^#q-;XfUU>Z1BN>gh`xfSNLWwh8eeORxG!ioQ?>~x=c{#Z6SgY6}ehxb-=322&HSCpAg}&p_zUKEQ2*4+) z@xP<;FN#D@j8Bid$vh%a#cVzH`_;1X%6N3?=fQo3w5uq6S{FZYVhWNZ(}je-bxffr#@aA2v2Nfi995 zpzY#E-U@46)DMqdQTWr-!)^9jF8xQ;0zMA_Qpt{Ds^JnDtow^5)_)F}P&3e}V3@E@ z^YHrBaVUZG24ND99w1#v!8<~UGx+x1p%&7TgBYgurSXrznTd&s1w6iZqwU=BdQvjX7#ZIf~^K!zLa_H%nF8tqs!l+Q(m6Nw5jI z*r)(>n?&V&9l?{iPjqZ6acb1r1!bj2l7{_ovis#W$##5=4R9DgxiuSSbvAo%dSD)1jt053D+HP8kW!!nhxpPD09(}jU9yl6*Y zkxL>rH#e8`g|%3fy(4o`Q`2Lkx1V_k^?RdL*S`EG`*GqD{hDxhsE4Y^X?>WPjjQy! z*myM11*6#(E8STLe>`4#`Sf9XAc#R;p1<;DU`)*hR6cUpgJqR}siWwZ3*euSMXF4z z$mPN8WvkSMVmw!^UVI^YkInS66J^TVQ(I=W#_hm?ZZ!PSc<7yCwT@vVKw9)w?g8|8 zr{(n4AHZKyBq9s|XYA&SMvCqY_m=Vrn*+cFkIN6zUs@0i9?`?{$IsEFd z%;3;VSk>iBor-yzFKc(A&$f$o#fQrh(c96(tTQC->`Jop@}@r&78WjJs_Ea0%C9F@ zW{Us(R8#pU!LTqlPQF_B^=nKR&>fYzyfc)QDc=1!I8>*r+I+L-YvTwxCgODIPXy_| z*RKEfB@+7jcJP2*`Mt{}?ZGLIYj5|z6?<`og^THR`MMtiY{GP+Uy3ZzTi2SYSqj$fAb^cvTFpw6$9R!w-DvG~#w^ww5 zE-oOAZ{swP$rAf*MfztklL1(MZ%`{;JB^CCKh@zxU$rWWQbgn(S(dZv*>> z+pdoW0{+BBBzXOA!TSZuv3|X&`MV(7DOh&?{%Ax1317Qk9f|(llVyO&zsbCXy!M-`_~-RA&|#%#ulX?k?UA0u;e+F9 zgmr*qZU5dkwB9%d${kBx2GY{jcfpL`SNG+C#7&&P>dNBX|)Sn#3 z2gk6Ap?yUg2NX*6*l#!Wg-!9Mpw&_|^t3C35wg?IrQoIwGM+9?i@4{ri{V0WHG8|o zQ@@!au;L4kwHNoPC3?7F9pSSPII&!PIK2oCl=?eU<2qNXCgV>7q2+OU;>Y&mD97IV zj+>uAs~LZ1#(!1<$XWT^6=fU608R)PLMN<7IvP^mtFe8W=1%pl#yvKcFup`i)@xp% z^lOowz}DsQ+6_v6m(OO2TQv5*Wom{#mm?t8$zQ!v{{qP+6tRH^#559IBP>b5;abm* zZ*(4E)G~QHWOfd!%C(NDjW-w=><`wBPyN?_`6tx)zy9cf>oSLn&DHcE)uFyRgU~`Q zmH*ge0cOJ1*IV5_>A6!EW7PO2el?VuWu!AA258lQ`z>WRL0wCEM>8hk!ch~VMzTa} zTQEJp{{UJaJ3_{z*3X8^vOr z`#`Uljd8h%%f#(qePyL886S`~q44Ya5x-=Z{xuNN zj2VMpx;<`%Y2p`>*#oy6I*@Sk{H(>{=#KDvMV*qKZ&|F%Z8?QJzQgy{5qa^mVnaAa zADe9B_0OukX32SFy4mXO;UbhmUnjMJ;VImjai8QgD$TbnCOGbhBj8>8l{MN z8$!+tDN>Nr6a8lqz*>`P2Fg~nvRX}f&J30)Vlc)@VF+=!$Fa@Gz?ecl{hRp&*1zKa zdG)v+rb;ltfqVNbtg-XGk=`~AkP@VnG?oIjGm?k!x{J1d9QLD3+6ZS6tb0SB)JT54 zH2IrdKW6^ERIDniSmiGff-Q30F7gBIbzLl0@Ppeg(y=3>=pwCfjx6We_KaRDU*Bg-^FN$IkHq{dA7N+$vAGxid~y2vBW?CT{8Q4zLlI`;q%=1QR;RV)skv@QM^VxZP?B?=P6D} z)$m)tB(H&~SQYu!lQZ6=g=$u|c*Ldxm+yFCxATusv#MEbNs6i30_%wx&Z7;rHO?UY zA3Z#(amx8EZlW`GHTNBQZ&cB^d`a*1A6)S}R@Z<9K3AeldEj@hYROZVN9Hi3DR`$~ z`AEvT;mhDzE*|Q$ zArlL@zr=0aHhdXAJT<@FQM{IV7CwV zR7-WAq}S`k@}C<);udvDHvg4A_m|n?55_%s_WF}*ip${;oosVB1F8LvOvO9S;Smi{ zq9e;v3xg&1($d(yPmdZo7#R&92dP}$@u&n=OvCE5?IPY`{N;ISERSQKLn9Nb#`yyjkbcrD4c+f~L_I5P}Ee6*Ih zT=ed>sx&$q$c(_&bJ$NDBOJ$nklp{YR{mvQlvu!K8IpLl|H3)AR*`UE6CF_ zkpeOn_O<7#6RqyKQ}SKiv_i=Y z1gg6)?nPpUXf|@A<#4@eUpv81UEAB&y$CRV8WlXewtn&b>1@lfv3seP=*ud7@uzAN zx4?AguwynGBkbN0{16{g$$d82Hnzhb``saz+{$#A)mL!G=hg`V=Pab{oi3UYc{SbvYB5tw+rT-#nNYQ4re7FXhT1L?m2#g%${D6K|Xbu-8wE?u1pg7&(#bhhmZs^3RpWze6 z5fgnU?2ig(kA{cUWzOU!8Q|D+XWTHiMP+N?UMTa4_a=2l`b6*1Jh_$675Gfd^Flng z_u&2jcW!ZKt|*}$S^wEjq{(keW#ATWKiLnRxtv$H1O#X%LXYEm#_sy()7&)d4OxD& z(fM$PG5YX1uhE@SE&`#w5OMi<=>X2$(0dFQc+jn1-UXEvlRY8yjXLCVgZ3wolEs=g+YUue(#&zaQ3?@)yYQ%8X0{_Pt?88 zYb;bm6{|4EldT`?$>a1YKGrw*NIV>{nW(aulo)0%T~!Ot^X^(YVre3FdehH~v<2v# zZVLL}Q+X^q@(%DCHE+2WX<*rv7LAB0?*cmC-tD`W77s>uaE{#hRlV(?cmp`NCZ~yd z%O#yDA>+)pgaVv$X$fc@femnK4%55R{hpgKdkTkXt?B`;XFWic*{N#jX>klTHLx61 z^^c3slp7u7WswtKc$m+Qhfj%~x)NTO22{Zfa!UwABo!L$%;US)=$FokIIn%Bhps1B zfsuHGt(R$pOa^AuRI4fp-9(T=vC6}&!so8rBn zr#q9^ol^3%55^#qf|nikbG0Q-TK8E0@vKrcGK>%{!_D5c18`5Te`eboz?(pdr(S!ZORFmiDk^b+mzA8SOzi7A7a2p{=J1S=9Wxsl1~324`Hv`0m1wpA z=$*0w z-Cdf;si1*wrt?iqq0vRu0dh=nV`F1E-S4Q>R_r=5UVs&;&I(4I}<7JOHVdK+=zOeNxqQxb?2qS#H zIU#=M&tAul$1ssB>HRITYu=~JrATFDm*a;-xeW@0a)HUen0UJ0kcw?JIM)ocSHH2)X@ss&+(`(Pdp108rfZ3&b7+&s+1uO8ZhhSj zm<~!O1)u67(*&rE&DF%X;)Yktxm>R2H{!86xZcJtV?${(ZKV_P_>+#&xln;qG|B?lQeTN20^*6Kj_&BaO@!Ep39Z|oi#o8xIhuCLIjTj{ zwl4ST06=uV)6tA4rI4A!D(xN7|CRrpC%+BL^I_R}a6)oscc1aY)wWv|CSeNu9>0>^ z^KK~L*7@d$Re|NHpD%~<@W{qeJu!|^6{U+X-)nt+_A8}C)hHhzrvHo-^_q0q2LM(; zHo`DuF9ki{u-=A|sQ`NXZH_XR-3LSYEPNUE04HUK8gXt(ew=~jk@WLg%faAcDx`y)O68(q;~00@b*>)?WrrIi}{bci!Ka3s!XC#*`^ZVSzDZ&AJIm9o9$ zPBGyzI$vV9jEdCef*R`BFKi$D3{&wmG4wsnd;3SzO+Tr`n{#z)jDsTRrfIFzxy7Gc zVocZYiOQz&h>4mS?gD_QzrL*Td7UHhW(n520m3U@7QEZdZ&kYIV+sl$SwsR{#&yQl zRr7my1paz~jn}I%WKcIsJl3nm1fg(=$qd5j=Fa?7s!D@%O#E=plRV#dbO*^5ewWPA zZ1?-Qpt_3yWzzR9fUD|qK+rmss>w~nA23q(L^q?CrxE8LwqM)X*-fg%-Jy_IyS#`p z`ph*^P};pd%q!q5JK1xeninvOTH5(J+^EV`?}+)3BJ5{)lUD4uI{RKh&6j1MCFu=2 z%P`V3%|6Vew|_TL!s%UMjq?yK9KLcLLQAlpFE zhabYa>x>p0hd4XEd`*f=SL^i9Vb0>#^i>`cQYie{^z?L#xmw2TGJXFP-VwDec%!Ny z>+4KfKWmJ$(84I8AYO6S*&^oLve-5c6E{$NM&=8Z-ppD3r0McF3I2oaR8VN<*d^Ir zM21DN^Q&6c(n4TJh4moassoyT{EVr}*C1WUGinr2X~z{$&$y)qw)md@0J;U18VJaT zq11r&wu{^M!-Cb2WynX6&r~jXseavnOaaBF>v&DmYKTkf0!Cz%8w>?HW=)?~!fb<^ z(oGy4N@ATMsD=pxbk}gn2;KiumrL{ln4fj}3kZk1zbLR)NyL+aL+hV-F7!KKoK~~% z=yB(4=iamIK7f`e)sBRp9~hAN`~)?F4S{r;6&6~@tF+bZmM@a(+fw!O&^3}7zfvN; zZ`gsjxRU;SUbd>~VjO-R%UbnL%T|+bh$%&MuUfl&&yKi9#&8gMhlhuI8=Ll%5$pb+d#&l0s8%_0FwggUmF0s~Ufes^g0J=Y) z6~CAqp^gk*?CKq64#{pc1(+0*XLj$%4Nn()1>Q2Q{ZW}ovQ}+tU*MM)jwf_}T08i( zP_4OB7zsTOq5lX-6PmrWF1F)Uf3&mH^Z7H#Ag@gZt~AgI`RJx$FogZ5+@8bIdKKU| zpC?k?XE@Koba)hakntV{Ce32y<2vcdo`|A4=lVW?*lkvV!{K{^eus?izK8nuy}2Cv z+owBkq?V7S;}p2|>2}1vPFQ|Cp!37@k2zHr^@$2h1?qNPVo(tM2G>ncGNKXX?75pG z&L=#kL+9IpC%(QKiTBy|+$CC{l@dK-gjwU-T3uKlRzDewR&BHuCiKI^i^midJtlK; zmA_av@j|&V#hGj_DT2is*4G=8CbFS5>XW!18Hpl3SY2=BOy2nbcwWwm<{) z$J0!JP?`9orj(DAAdZp8!Yo{;n!TqS(rI_Zsi6kwkzb5pqVgqkXTK*nr}mo}LVab|qQ-)NpCkv(z++(mXNn=tLBd z9&3!OUo|2UlGOnXrrcNTld1|Oa?TQn+ngw!#`kDYzQ(Pt@#-@9;$tG-cY=nej(cV= zVSxhF_Lt-w30(WD{jpV5t=@8#+CVRzVtBVSH)KXPuNRbp%rt-nSrAe@enN6%B>Njq zp=Oc`CJ`helzU$auQw|~p9O0sfArgz5 zlW7wYJi$D^NoI`+cS2OT=^bEUOvqj@Hrey<$dy)uz(eSsCuA|Z#r8hRhPsDQDMAKQ zubhz=9-WtSN!3o5&FIOXJwQJfg}Z_|1?28C0*7g%_6yd}APdqjVKNm+*41qknMJp# z@6Pid_%fNOc#o!c!gp3*Ek5O&U%6@tt8Hs5BmVB2&R2ul&-oEgcajMfAhaKPo3XBu z*Ml2vuvo*aX!9h%9SWjNutlm7hYNcnQ`~hrmUU4P-Q5|5IZT;qprIc2_uUG(gFI!V?1z;VUNnlvGr^d~mjb$*~>aCXq=E zsx{9g#^CLz`TFPIkWIwbWd8nL-D@QjT?Xo9M>a{xhS6Bo8u95pGxXTa0l6-0BSP0l zwgSwE=_aeaYtMIMCv2uqIX2A|;}uYI%}J?>^r!tP+_QM}VA3AI3X)4$?5t0vd*??_ zta{N%d4dEU280YxO|S3T6wKrIej7(f!8vJjJBeE&RW|v~#Z-{4qR8S=S{6*?xwJ@0ZLLn;HO-B`4ZG47~p61aN$` zD|GE-!7=#owBr?&aYoR-r36i#wv1(D&VpVMtonZG z67jh76_`}u?N~dzyy_M#P^3W}yIPU;lk&PB*-)?XPvD5gCb5Md7>?l|ol<7`Kc7Mi z>Oi-Tm=n>_)8J6T;3lJm!%w3!{WzH;4=wRR0GX^x?-@&(`B`|Xn;vnqOyvo9XmGau zMfeY($#O|n**GcBY=#veI@KTII(;{rgPZ2E4=X)VBWJON3;Pd=-~#(a3X^~{xHPFqTJHAaSsE-%rcF7) z%>*&qf*I46p>qt%cOo}xVs1_9xSAevZ?xPM23J4UpFCkxTfX-~D(leEf_xu~Y#WdLiqfpg<~@FFBc+!{IOJwKoF< z`xzEEn!S!(bPlmH0Y$dh)=YzMqs?@A`z-zTbCJcHw)!IrSH<-e@jNLJiGbGih<%>$ zN;+w0tK~4TdB=xMV!~hfmdxeL8Gq~2D%7h@M9v1eOoN!R=0Q@d((KlCTvccYHDod@ zXtM!%{x$t*#PhTFhUU)MbNV~ty9f3i-5;|wibJV+JKsbcQ=yKlhtP9I)^z9e)!P<{ z_(=CeHjM-SYLM9we#a@=y!``^e6%1_OVZtxJ|kIAe z(YWt5lpuylMogg_@%m|nd^(T{Zy7b?PkAXkCU~yyC37}@@Prj+yyo>h@8>+zjkRmBD4)$_C$xe4 zR4ZO;ykUsg`%8z}7iy~X85%a=p_qd&IXb04qKg#E=Wk~(`Ks*dfQvND!y2mLcUT14 zg9YH+10M>f7HZ@$`=H9nN(=i*0jo``WLvbD(sZLewBIEXD4C783>(0CX0U*iAZ?Xv z)5;F-R3N((`(jy)}d zXyFiKVR&(86@|V00VH^(Df0E7)fb8g9Y%GuK0BQ=V~ZWq77MfqP4|2GJ=ORluyA#j z9Ce&3Uz{szgy< zO!)TAy7AKmv$|)JfAxG^YryClfS|)^^Xmr$d*%!SfdxhdG!&4 zMu-{N(s_l1QYJjg!q4tri@MX@Z6jpw8(?%XXnn|nsk5B68CSaD|3_%9P$nx`%gzZT1}&vE;07aG}> zTe)dFkdqQCJ+;cU9tokA2L-|&jY`ftI zOMTSXm_3w{M05u?dArT?$!nk;$G)k4Du1PgK{O~%*PqF0ZoA31_4j~`iee`Q>qvgq1C;P~y@bIxY zs>CZT1{`>qX`gxZZJY3vIA?Dzr92!TY(Kg8bmBw zem6?TPx~SN{l?n4`V+=qgUbtVJ_We2BAsvu<6DWK+t(pwuzRjU3Ce8*EeXOx4K@_6 zhY)#1Wrpi*DHj0-u6sX`USyhT))gejX2$mWusSBK3gtgKkJ=&6OhFW|n0b{Uv5kWL z4V%^K#p_^D2(WiD$M@9+4W7vK?S_B(9hOE zNs*^EEKXHhM;0jhV0z1J$0sI|?T24n(i_h?%4}#BoOC*aJukk;0~YerHgS=mp+wZW zJb)|H5F52XlKoaG#=&6i{;i2cO7{*t#;v}An@Yl&hF?e&8EME5IZLgJ(28sF3$9lM zgE~UE1$N;zF}>Kbo5tm$m$wJIy)I;(i=FpA8T13b>Z@#at|FY2xSW}o1bEs1fHMjA zYl{CwQlL)Sw%w`0S%K031b6Xb!=&zvSsi1xZC!ZAq=zB2?b@X5$B?3q`PfJ5omX>y zC@qgQd%IH#dsS^=&YdZzpE9cgM@E~ertjNnwMNL!CxpSTSAhkWmv{OSWt#A&G5NNy zxTVzY(>mIZWiah+*{95n|FzTtpiRhiKEF1Y4Bo&KKr89*cK3-t@kIXtoxpeL1TyRS z&Q4#~y~gSZ^Y5Wb^%kIcdRV)rt*b`5-8T(6@RRSsgpg;RYS*}ZkgC1SmWOvV>jLs$l7LCad#tB3?wu}!juO7Z10u-w zX$e&P_Y)8xs8l&8LTWyxsLJjfrEJT%-h#D1tjh8S1D5*cz>A;Bw@V3#F!)ZLVR4ek z;~NhcQSod`nOPPi#(^%wo*~{GMqWmmW=KD72E!IWJ!eCPj43aJ_iN(5DHe}2PhC*2 zH0|q#=#8Bw9X72`R?1D#QLg!ybV5lVuB0*y?%Rq00$}SCZyu8w!O1#z-^x>Epi9WV zDQ{xB0KW{?F7}Vi1?$Z+mmfWjuLCNr4-vbW-I+NMvWq!+V&=$%9_>QjE&%3dh?svnScL91CK~ zBn+Pfpfs!fJgN-)46F@7>zP_w7Y`2x5%&h!VZMMn#tE8w$xGau#lnQ;6>THv3$hY? z{UF{{b$$J9Hd)lg6>ibCK-lpgz3-fN#XM8ON>M4)w2n6ob5r9Zds*fqT}0~98MJje z&z`Yv^QUo`Wp|@LP#xTmNe^(26Ft2OciCT*nGVJ>f~RtcZLTg-dyX(;7kD9c>5wpS zr4csjg^vP<%0EysHBWqCg7(a9w+){1ZG$JM!rrK*^}4nm7|Ph@@FfFmACnDpii>CJ zi`rMDJIi)E0eh**lb)WQ+0#!d?k+d}z^sQOaDN=z#AEkas^HwalWxRRq`D9MVT_w3 zQyFGu&{6--+hAd2yN?7Tchjc~hYCobj{`o*EB z{n#~h9!e3JK0aOsQUwYv&|fa^HUqj@^L|7goBuALyV3NfxOI_u=b!hUk||}VU^v&~DFyGh0HZ3a z=7C$JtKzVzq|CJ;BaC`s_p50R07PXhx(~R@T{I}=7}%%$4=%ilG4qBgN!SNIaYJGM zYayQEV^w3#YUpfQdJ;(i#ij0jL4nIH_HFB_tEJeRjN*oan92Rz(w z;YvoxcSqlk@Fie^#%S4>jxRj-)yQWe?>(*Kke*SkPxa(==C5M9flze0 zzKfM!_Tz(J%-IV=ik!tx$bFM71 zoe_p9W4ecJm-t@0F2Nce9}SSlr%xx>bT}PL({#V)XEy)He^ zy3~@mtv{QYn@`nrMv~j}ykRq}YQ9DdvdC9uToe-ejt4ZfoTzp3O<`bd&f)kwl`IXF zZ~dqOTt1WlK`_%40$@Cl{HSWH7QaI$ucYOar7C>(p3wS97~QyOv2e)PTj3XcAYOoj``7 z%HR!tmGvQMee(L{6CzN)1bgG=n#-KH|Mm`pTojhS!&b+bIBk^*{prh`6TnJ$7A`^Y zEmK3+yC)NJXPa-_ehq#j4iwO6yE~)_rj4`@$TK`7Z^ky(at$2tUX3(`o(RNjQFRZd zPh5RGJ^bEyBQA`x-{AWb)|$$Y&q(-CH9hoZ^6vr{Y9qSIe*4x>Z}KlyaG^o=X>#+s zk*L_2fE9xRRcjyl6pJQ!>SB>CKQ?~dS-yck)ig#%?I2J>zOx=K6_bH2=a9U^_LtcFUI1Ydt{hb52PUTAO0z@uhT zdu=&gw%7IUFLt`jIls}n?z+5ADzuVtu8 z{VoJfqNOiR5y^ZNev|f}9V@KYp&P(#%8JKQr9pje0IX@KQ#8|VukYB@ruckbiidji zo^R?-z;ZYc!YV)i!h=pwla|6KT)YZ7UY3DMvdpWh17@tvD2dmm4**&J!NzJYLW-V) znS19wKJ>dQFsjRKzxj}yn{h8wuTp9wN`v=Inie5KxRQn}Z(v&bKV;S4eI5Js)5+3% z9N{6B`&AKu9$CY0@pnnz^KsI> za3eT_kAfx1*{`tvCAoe@~VZ44-g7)cNWflYw&G1aL_#sVjBJl zM8l=PHM+!DcfJGwoJY6O)x1O(z)>tYTo!h_ICtkXjc07yjyglzAlN@S?!Px1nDgk? zu{yD7Wl$Okx}G~PD$i*5m3D|%b%B%lq-m^+TTXi8kBqwt%hlobVHt|Ne;3O=$JifP zCs)>RQrA7;G?Oz;K=t)h3#j?eS9ieYYZ<7N^Ra#JX|1~M_i7^&(P(Yfer4TC`}LLq zj&kj;l%Uv4g5O;Z1(|lv&M=EkNslcY$1+tYDM}2>f3L4 zj^yd@22j>X7HMFR)-msTg0(Eg8qW}X%y&Ay?QYS4xaH(2&$3YJxO{_nL>GInVIfAq zga8=`>+Y8c7XW;3ffkOjdqL`#ynW#!@K z_H{2$|CAj=U-by`*rFi;{sZv_72h3A^rIuIQ>YVqk{ogQ#4zDanQHN|`!@AL(;(RI z`woL|##Z$(05wrOkBgzfZb1DsOVK$FlK}AcS=n7nOBkXo8B=E07vIk%PviS1>3!C* zeOr$YLQD)1O)%ar#kqv6m8i0YXO@&avDW_%sg6SX*5K7I}@WPbRP23wvv_aXp(R&n0jY+ zZM-Kyc_|HqI(e*PsBjU-kV~O*R%4%iyR`=Xcp*lYf^5;-BR=;f6FQTsWlyz&dfVI$3{i83Pb5UHCw`QPX|Z&Itc+z8B?_0eaoMKl z@|vLQ`%nezGMF6FJ>9Cb$B8E{7)w-BTe~N+-{S+AVS=R+?#4)=lu1~ldyi&d$8~eX)QTcwh;f6@CO9w z!@pqH-A6OhI$C*KB3R&04_R^zGNG|mD;stB6wPJb@^z= z4{^(1|KI&al$S^RpMd$_;=eShOtJK7D+}v66k%dBZY*YIiGnX)PRRv=XbPXq%Xz$3 zb>&9rEEuQi8xDqnG6_DFhmw{|&*fx~o&QjTOC;I?PGv=?)dG$!XdE!|;1J)I1A_K9ePM-P%-Y83NU`iq64aRTaq@;w}`a;a> z%}m(>@33PxyhqFwbkB*&W!oDbo9HUOcyK#%pZ3;#`h?n}F1BU^v*cehWCP_GXO|P@ zrFZ|H01Js^7@+C52S)E6TVMPUIpmEIpE1c4P<6*G@a==`FN`M=d;(XLYKj@4QCs(v=f1F7&Gr z?a}Sjp113oSJAH{>*4JvhY`0y3Gj9R7BZ?TTO(VLoa;#c;(4E*377V~z=TQto6lSp*ojj8#GITb$Mw;p1Q$ z9qBiiSfGk?AU2cHvjrXO^+xd(O}JBAulFa<;U2*1tR*uOfKS<#x`^1PxtBHr^FDRv^9Q~_RO+E@GbbFZ; z`UIfM`P|`fbIc5Y_$$NGGQZ}rvpse?#N_o=1B08o?YMM1f;ak(x5mu@!PScvKa;}p zf5l!v^c3E~qM#!yW}JUxQ3ob5;BC#km(t%W-5YREJi9mgSNc6+%*RcfNgR@*V+O^s z8MI2^0T~sKNH)M%-Kse_9uV-vM=8zAQ`Bqjj$HDQ%b?jqW8Qvq0x;A3A#EI|LQ&GVZo^bv zorM5qB+ivbAng^?QivsjPD5a9QHAfp`#LL8{b|R0Kd_JFJcZWmi}Uo*2OlX@zLCrf z!Y-csULJH5?C`JidBFMh+;J|&6#JXU4WyJgQ*{b%dUcc0c=^ZYEnz~xo8x8CJ4)yl z(Zod9_hbHe!O0x+PzH0ed&sA;=BUchv37IRwNZOzs-#5wGTqk5+Y@Q2SMy7EvOq|c7B2V6`NkVn5IfAs8gOO_=09vMb@Z&ezhxT9^obs$HbEVB6-E9-1i zZ?2xFFyd!x2(I1pkW$B{Au3Y0g(@H{<_oU?bYfDU*st8F1>JJ=hZwY`x;q@y_^<}$ zy4K&*-2B{0LQXtIexf5W=?l6Foz(gyRUW>>fM@I>>^XLDU$whM$1CkzHC@PQ!L>UL z#ITn%(9vXVvYyY9wy`JiZHjInbV3h4#FvxCk32^w7Ln`!na+;&C1O z0jPbyA(s)PZ(r)?ds-Bqnjy_p!WMGe^mTbpDHK}2#p{0Mm5?Ul z`O?!MV)g zjbyucgx+)hPqx;7o*!HoaQ7yv1B0LMy0c|4?_e^mx0==8C-x#@K3jzH?8HkLUEtU1 z)XS>`AVG5MXK#0}Q7#7~EZUZnL%9bdWa&<849ve2q#$HsVvR9Hv*!rJKwZWBE2bR< zru&Hx(QyJn@cqd|?8D9uLyWbuztJ{s$cakq1JETM=)mYg5mQ ziLlLT2TWvnmt?&5+I{g^R~)s(X{yVs2z@Nci|j zX@62AHmd+aJmtTTGuYf=drqf_xpo%X83?0quWbW=s`+!?TU|^u2?VE__CpcDn zA&1y=B5N7~dv=v7>p3dxH+NL|AAK$1sf>`WjuWzI_`Fxb6ZW@K@xSgh1B)@Yp_8%$ z;|sYakib$1=#iHRw~-&WZTgkdYq!u3T_sT#d+oPz2uSE$oF3F+vX_*EqIAP29-n*pyC5k$r=?G2MV+LT{qlu0cC5IIRtyq>4B)LVyK$dLP1fdVAjk)P~p71v-%kb3LAuP|VvN`i% z(qq|FJF+Jg{$on#`-OY(2OmxXfR!bn5@yV>n`H`;f`qpLvDpW4APLsjad93Aw@!cg zsu|_O!i?QKMt%xAb1r3H5)xikcfZ^3k|O*?mhJ_&z#LFuR!#d1r1nf+^ko2cNS5jr ziSwU2Y5d;+8K^VnN_3oR7_C8|z!(z#GMRbo)F(TsIs?M!?vU}VWeh2zL?=^g z_XgI7^-5DUgQMRc$$CI2aZzr6vU5q>hvvNpxkL)@hOF1r6djz@ z@5>&r zFKmtl=Ge_Vc#l;&aFMbieA5HHBIE)M!%6lZ#{TQ+`R^Z<41jG&@0IJgNnwVH0p=M? zI~J>ec#R;}KoAygPpZsI@i0T$prGLArOA(c zbpP)!@LwC4F1q-g;o_m#<>=bgc&UM6jqXnf|@50)IF*Eaex3?A< zGxjqV-8E)C@}hvK4jYkJ7{sl(388PMVJuI>RPH$P#fZ&9qc8<(&Z zyTR1mdz{FCw^5UdWSV9;{*R;hKcDnJ&uu#Ocg^AQF>EOkC`4PuW+U4)i^p9w3|Te0 zNA6jAoM`0Q^`SW&f@boz(KJ=GjQA{G{WIRfj>m|M98*axutD`uSKYj#}l^v?#2RCsp-)~O2$7rrSJP@+S1bM)Woc`VOc--LphJo7nNH{tuFYcUCfO$mD>REED>P5KK z!}#BP7|sa5@S*LGatdGp9@#^hqZw2pac7g02>LvZKF-voJ=Le*N31NL<>a+aL!sac zl#j2;?eG4>0zwL&Woj>CHzXEXW-Q760b#rUp6ou+6kkcqz7qrgULaj~^fQfr+Ihgs zHzF1ISPYggRe|4Y#=e&r9d4{IYa813Y2>v~JqhTH9^sNt-T#}5(A`%AjJ|-Al@zb% z_E9J&kxxC(uI8z1em|u^Y)Up3(s$N=Mfv=1{l$NMKL{R%pEwYTLw+`RYxOLz_*}wo ziNt_d*ESD+Gc6pp_*&J*+rXT2rxi>0zJ1I?inxDTp4%%(HqYz;J?sM&&D+1-p-=bF z@A2o2R}9s!)75g{*FQRR?Z*1&{lZlCu7Bnwby5KMfQDTz6j(B|tak$EA!%dpaUDlO zlDgf##rVYTONLXrnlLrAMR?`k;ro4Y-HpA@_oHDT?uS|OtGkfxhZ7a&G)ep=zjkJJ z#C&r%6kI)ap8AG7zlTl{kVQLELY;)qr>;l-%PR{N((az~T|)C8t+;RRfjk-vr(qA_ zKwX=D?o1}Q2W!k25x-hlxuKiezNlS3!wN0A*(~8Z7@rPfgu*N*>HtJ1esPiB)w96} z@IWJhUe5@*=6w4$Q0i!J8+&pBG_rE0?H@^c%w_!>k#z+HLeKWzYMBOoiI1N;Uh6Z8 zzP`R5ocM$@&df3C^()YXNL*Qw2>Y`Y63(WXT`|Ip=?$(Gd$i81winVg?tfJu{RxD3 z^3Q>tbgY=btXLrT0_OEr`;A=F)P5e`@AWT78E{epJ*=%6z)e=sxr#^XCj zff8A_0wC!%q5dms@EzNfpYHRnG@4!H&-l|1#|Rz$S4J}3PsO*uqiPtFum zP_l>R{&R}rhV-Yp3HfghY8*^B^%x%C=T}ebwXf$ao0$9cZ(d>fLmrDa_);dFx8l_8 zjdi=#6;`P8no2r5mm@cG$UiPa9bvK7P!lBVY=lfYjEm(d+ZR;{l#+*E4Ue6F1rIef z_2-NX$D-5Ob;X*=oHBta4hHvYCe(-bzVEV%$=K7tNtBP2nH`YQteel$nsFUNad+La z$BKWqa*#LZJ|ulPvhy5kZv-cswt+;T1I;ZDk%sr(di2~68#`6@+O04Q*2kJIRxHh@ zs-D#^#K&uSFRa1dBR=3I006uqFl~lx$Jvw5@5z82^|&P~U+{lLa;vX74rO5bzEd#{ zrZ!{TjIAxvm|L#3)|hC<73ga{pE;e(krRgF{MLO*oop{fP4?iCA)Wh=eTRLs9?Jt- zm21ZPZVTH=z$=C@S#bqMdk=#{@U`5cP7sGhf4X`7=FUb85O-Q_QqK+o#Xboq?TH|F zKY8ousu)fs0haW+%;tGzHJij^A#Z4CHSq=5hd{Pu%xoWW`VKHuS!)8_&`cbipnLM< ziO0!RNchm)+-`A2|Na{DI;iT zbd-I3dU^#=#;9FBob=4DhkCuh{(kfBcN>(yAfNR$q;q_wJjA3O^P@=$KCS#3_nw<3IbzyvYMDY z5{FfJ{4Ap^BiucxdsZn{+3ZI0etWOf>^E;RG>jo)puys<|B`_4u#RhLUeZ44y79hx zLJt2xW5~#T*zT)+Z@DZgJ`Ywd$WH4s53Y4>cd0)aE73cMCpVN1db&u`hAQ00Hmq9DL$R!n7B;9l_meDK$C)3GZ;nCNuxSCTDU@l|#&g%GgH7AA&h5exj0n-=Wn%t6ep0?)EV+u!Xu41O{(-kBE})W< z2`5zmGO5`q*uFx@Vva*yt?jI$S9;11f!+{)>Ep;qjOgQ{?|-_ zYGhgSxG~Q=!jI5wkgph8PB4se2wy7xV{L$j(F%mHsrv_tl(ubh^X#BU@l$`fLG3x3 zIemMD1((XW8+LETx*l$1oB;F9wbBga&?;#d#$vSM477ku)Y;cli9e9Q4Yp7^>)X9) z0z@ZWfW5i@Wz9N}{_}Xox%0uf5e#+dU>t0arJX8QfS0f$6NF0xUVjl=oi&4BH()Sm zYFOozC3^mIiDfql%fRXc4~Qa)x>@Z_!Qr7weFh49#F&5@A0W%+;W0UR!uJ#R(@-1%S0?q3O|Br5eI zhSyZW$S-noc`7-DdcCsRk?l%e64#BxvEA{ezo|Hk-EN)1Tu0s9pf7eA==TOzOb

|xXHWP5;pG{6(SXj6m6GqED$!J)$1q}I`hMi2f>{{?p$(kA` zS$l3CS&R5@Y0){g^+oJuRNHDz(})O*^or|$If+!c#VbZeG^bZ6{9+Uamr_J5YwWY7 z6dP_@!YbS^2}I^NT|?P;$Qh@mTEp%+Uj!U2gc(Q}cx)b#Pg+c+iv%FB%-J}T*a`uk zJs-LHVa84G?$6DySd!zU!xRVY9s@7!_k{v8=)J(~M!^2ilUln_(!m8?Qno};r6p!) z>R<#tAQ;Up`>-z>&_>-Etm`PA?rwD-KfcTB8&S%>r0+cfA?_@?37y|Oh!wA!LEgaz zW?S7j$nlv{k8{t1YYbG&S_87+UP&!Ao=h_Y$_c#W|W?*(_jwWvsNcUGI+wu=GO zdTCb0osu)N%``#=fc)qst_CG)Q-pyC&nv_(xW~ftDGR%E0yB@Aw)PrU(b4g7Enx0Y zv!PJw|8rCB&hyni{IiXVubq)SAj9iiz2k~WIe$it6QqQ3nCr_thX!G&BVkvhsze;2 z_{Bs?HiUN$bwVp2)D6!vUrA%Nn-58^2NrCHKxG1FEmr5w6JE_$JiZ}w=HY7%_4QbF z9!VvmfXwjc*s|L@yv?#aNx!w2GZsY4SjOPk!q<*DUV6e=U!-?^R7uTE*KQ#nlike8 z(6S(%x*(gnW^XT`JRlRQZAnaJWp18Z@A1QgrD{=|O#0K++!vyH=alNj52Yk|l5o{8lULH}YOXH3O& zy0I3@J>$HfN|c3LQeP8Xyxf+xs`8EhPu=)~o#DT9xc}7&{P`n!;hKk>jg7MUj)=?hx9L+V9=DEh*a?ZJ_?Wb}x0O*T z)F|q8J4Zc&hJxv9cDATPW4Ug*>l()fkaRzlYJrr8>RUT_q-+k*&Y}9CE44s?ECJbK z>HEZ|r*CdYjY|!}9Ul$y+G+8aM@VmMYw-3&<69?^Kl=PNC+Ce8n-NDiDaV(7&)xV_ z#0}@>da8+mTgdp+!bL&QI$vA96@T>Am0wDhRMfuL=>#4QPhXh$y%bNxGNQDXI|Jtj z=Z&L1?PnRD;i!a42pI+ASCmizuTm!%)@>52za8e;nMh*}Y1-*iL!i(84|ewdcaxLs zuheNM;vT1*j_9`x<2rq@BYAYc*Uf~6>5P7lD*b#BnPj^RioYAkarDSxjN<`*)1wmj zzp?yUzf}cfUA65p)=zQ1XL z9W;HLbqWN@vL3eglr_-bTjaD;WoG9N$7V4 zi;k2PdNqrPE)Qb+CK${J;kRXf>9u!#X@;t+Ui`tP`{TLRT(tslM9tI06L_j?Yg?}M z{8E@*ndImC+GGeM*CHJ@xIElk60YUb7}nSwCGwTyKgO#t&no-qeJg=;(LN4ZzyS?y zebh32%Be;#)EOPD{5Q*q|Buu+Gm<6BC$$_u0DH{IN^~2sbyFEW?UC_X4JWh&#qG|K zP`fQ0ZD1Hh$gYN;oDg^)SI@n-a2^1<1?DL6gJ=L|EU)ywoEaTO_Kj<*dge1Q7upui z$`}|J?6@sUzj*OtIU7ghW}!2qOX6z1bCIgdLOfB-R%>G4%_9W;5wI>mE_1EPR;9T` z`$zMgQXmPo4WJ zf9KfS>;zywc+9+gTYO70W_#_-m8;!^+9l*3{jxk~i&r7I2Pf0JzW`0-Hu&LRs0Nhq zE!BV(*7Hq3hbSIouvzNLq*bQ-#GUEe^YF1zegrq?eF_Ia1mv?@h{I=KXh>QOOD4~T zLdOku%sx4h-kMbK1_j4Ah!W2n>`7%y#YSiYg}e$}d9GaWp_A+CcI|iJ*p;gg>XA2n z!Py+!T0H$;MC+W7T#0_=@x*ujtoaQXlBkIe?hgM^Do{Y+$V~;$e26IU+{AvGykLe7;YmgVq#Y9XrbD`&u3HJLL@nUcZ4umW74`BdxnsuC zM_zCGvhSHZIHQD7!d?a}Gzct>zs3JebFE>}%<);Fs&OUV+Oi zL{~mV0_+sPkN~blGx(X`(!~wZFQ>G(IVtMC?l#>(lmvQs=OruOikb!T()i7Teen?C zb@uk!D@f0|aAE%5i|@D02zL?tN;_;{(7_gttL7!AiB?4ix6cpSL zGIyr;%Us?^gK~D?KUFe79p~cv#iz+h?(>lMWw=I|9v3^yLmo@;%1efyeLOZlrzgC?g_m?3y}%HFtx6sPp|$%IOV?%d&UO?ir!hQun#sB4*GzF;9G4|XI(&( z6DiU{78EilWhm(WRw7K&73*%;TP^Qf>LsyzdHzdBDAsu$pa&ZAI4-=Jox6EXkc5ZR z_nnc#X#qe0UQt}0&_?gx%m>_Zro$nZ#X7V#4zr{lFO0&LyShJlZD5sZPJ2+$-G9w( zP)1$nAFPyJLu~pYpIkkswzi!n$aQcAr|uPTwf8<5sMCu4&5^{-x%c@92H(sNq6Xca zf&wr=s>GI-I`pgaZ0r=tD-q^BFz!LPJy9Viz zbgDYE)<6B;nqeNp;Hj*YuH26G?n~q`S+|ZhQN-eV{g&UoMX+<&<^=@QFVj(*uHZ%| z5#8Lf`e6?PbweTDl*ADf*nZ=(qD*Z$PDE~%Gki*=+X_$Q^EQHAr$L^VQwlVZArjCc z+Q&Rv)zF}{^QJ<&@ENQ9AFa0x>Cj4E&Xk2lE*6hyXDiSq!|05Jr$koZla(HC;>}K- zf&2h&-xD@H)OPJ*Ho;e&>%ICpBI-q%9!J9f4UgSf+qGj+n?3jjkL71!Lf|C&6k6Rx zmii!(pSIT`MkS0!3bOGS=65qk_N!vDRwedJ#?7$AY0t@$aD;rMSeOc(rtwc&{EtH% zcUovv-f8}#>>k20J>DIZx3|o>=pGtwarlRK4&cCuDZ44$Eq_exDc5_Cx|$B8hB@{L z)_hA94GR8msz^s029)*d46`m%7&hysqKW|Avd$34tORsxWzr zf(5ohpkH78kg>8Z0&<{oMl?4$xhw=}C_NXBRxpgshIs7%8F{{+n2hd&8TscpPINAM zRx~z``Ssk?0)}D^wC(scBr*I*clh4_DxClNfAm0b&ijWZG-w!-=;^jsQc_Hc=;oT3 zN(Z>miH&(bt8!IcVQvvZcakiG-59QGr28^azE^&lx#&L@Zosa7Zim)*(ea9*-2M$< zJ0?5dd2^+~^0W&|Iy)FvU$}lT*We-z&?&N0PH+scVAiH^*tLB*4YJ zbfyr(#*mfE1gf2*7v!pF#54sK~J?~+s2Tlip-01!epxEOD#d8t~79}uNBSUiZrBk)!?&XJExS&H2-PY1V6 z<98h^%1&9r|J z9H(Lgn2+*zk(Uq4g3EB=)gES~kJU6tW|EPzH)dl-4gQuwzq_+W&%-EQI<+_++;IxN zeSl7IgE5T~lg1(eYOkP(Zk6M*U)c8_<67xBxiGe0F%X+-Pu^}!<7e1jrVHCuvIdEA zPtRJJOVX0ezkMshllLJu^a>;2S5d`ujr>jG39wh;?R>7&t`=l_LkL0qKlh~i1E59+ zRt)Z?wsQ27j)5iZk7vKx5BFJ&ck%!-Q~u#9a0y89g8@36L+lQPbq!lf3lYH*IJ0+c zR%ZQxrW*_#c$lsB=6{UY{q<3M{_1(z+=>&7r|d59P3bN%(4`_VzOrxcByG&wS(bH` zK%^HQe2D6_6fP?{Izg6XfME#3-$~Gb1zZ0F7ZP7nKxhiS{c~x$d>~$EL|N416t^ss zfVy5*Bjdda`6`YokkUV%$Jlh7xtMU}es6u9#l7Vak?Fn@T z%Cf1Zq{Am`2#}0Dcn^A`k&`dw_X)RMz_a!&um3*FGoo65y({)L^Y!LVdjHu4CA%tb84Y5xz8>p@%ur?w|4NfZAF3iGvzP*yuD3O z=g*4Tp$pE9c(#F{$lwx1i@cm1X@1A4r@-(!o5L}m=dAhZV+TwvU^`2qp&C3Sd2~h6 z3WAjvON*v;n- z;)Cc2XlB|N)NEpUh}r&Dk)TboK7bNvR`Mct@XG&s8vW#devH3tzS9PN`Vt?nte6kY&Jx!ujFsux zj>XBbM?sBuQiZrXF>I_-fHYSCLb^?~>Mj_zY)!tU`&0(r530ojxc z5=nhq>v~qFIoKt#gp;v<2CN`!fswaAaXUTT?9Gipolp4LI^tEp0IJ%>ZUh5@<+(IWn`F#6uHuln3Nci6`D2OwV4E$FAJa)#*`NP@AI}#Q@Z5L<{ z!a>a2@n^3_uL8ekh6lSVvj(UffWj#S9J=gzmS2Y8L=S+tC}b z1YmKk=B1RC-8HEV_Ch*oc_6ru`McB z7}<~EmZJ_#zncZp#!t z2JjE4=UsNgs#m<1!B~P;NfM4t(_nz5tQa}Ue?d@5VOiVLNPLQ@?6$}rFjrRAUY@Gn z+S$CjGq-q6=P@?JviGYjz&=c2FiVYwONaX-9Bli&Gj+0ZrL{fWL1g#m6kidt7}4!E zIAXoK{$-`3t$Q=81;`dsCm!fGXP%(~=f3@;wt}*)m zuMQwwWCN@=3()nbbB^yVW*BeH8}>&w@MP@Gv&qKmG5 z=RJ;9X;@VQSeww%p}58_2Ejiz}y4;Hldv*Ws)}D97@} z|3?wJ#2kH1QoldF#sW2;tGZY>jU=IqF`{ew{PA1eb>!GLb9J*oj*p686FhThyt2|# zVfTMwae1m3zacabFDidw(@QO`%OSuwnI*NY^fBOMdwMNWqo9Qv0Vo)RJUsw|l$|OS zUVivPYGMt={BC(*tyFaExYgF3=yf?#nm=^8T)@da)nsI`UYvu@1%gz(&GXE*JTM$ZMepkM`}) zjbF5^nQb~r^WK2dH-AttmW@%8NzXM?ZMLAVD znjgc%8JX$lTdnucOJ^&lMtb}EE89}nsUHjLe~8E58n2(0Fs*pyrRXiQuDA{C!sWS} z1=w+E&E&IG%=UJRE4ZNfTAzW3*XOR$AStN_3^(#-e5*uGm~LHE0fFD{q8bQ-WfNIq zkF%e&HHY6SGluh-2HvX#EbWjI|Fsf|&T5Xh5awZAp>|oG*pKiT+Rdwk<6{lyDf6Jt zR(yQJ^ts9O@vzzTu?%_&#ZG-taG^c{21Y;{_54d1sXLK zRrG4S)}hEtmt%U5wmz+tIr}5oxQYty!9(A3msoOsKHe`SQ03yK@$nA?xO%U3Oe$;j zxsj%$!{8%}#K}quGpjDpX8xqngSZ&+0EB(+<%P$3#If4L7oN%vkF&eI!i zkFxcmL#*8gMY5RLW#>c6DunAwbrxZW2aSHZsmHR@MGt!ppgA6(on()^rUYvgOzbw_*=@oAyA$ZX+{Z@v%yu%!zo zopn%luRbp^t+C<^*3g=*ZJns<8nYM4?t4*4pqRiGFQU!nSy$8VnP`47Fyv^c4f8rT zVx07;QQK3^?ap@Y&#OmrdG&X1zGhvSsXeT4@61PRdI%q!@kL8krfhyM)^(Wq!E%xE z%B<0^($MIT;`zLtV%V%TH-u+5;H=z!3l+QLeR@LVC_Fpv$+TPVCdQk&hVbPNp;mbP zI#={+9<@^e6uQk=ppC#TXN~o&X7k%V{%RAHGtEwifn?ul+TLJ!!aC_H|7^B>!{oZA++_cdTFRlWI^vhq`W|8M;QsId6 zIs2W4fv3l|t9>!`a6+^`vdgZ%qwp_ShE{`_`0SJd#blQ6qgk2W8U0cXxLly8F-_-@^C4&*i@N`F#HKcRYIb-fQhO=Nxm4F{O=>gYsL*bCh@d zi|Bu>R;tZ;9H%Hh98~>QNrXVG)o^#DN9Ln7S6xCbr)C$n5u`qc`wmT&*82@216c7G zM>)3UAZpL#$3BQlra9s)gxBb1Rd{}svgk!<`aQOSXomH0XaC_25>$0_gSC3LA>MCw z@7(9JifN(KS`Vh;;YE#(Z@=H>WFQZ2xAHz-HyU-KqbJ-*B`{Q-z>Z6~U`WFu1x7A_ zwX|O9w?#fs4PV@)EH#?cz)6bE$AObF_&47_i5;Gc zogCrl*#_c=w6UXb^Ki3rw-#{m=w94`dnF|A*HyF&&k2<#USI>|2ZJX{f!BLXVm~3( zvI_hZx(y2Ff{j*$es6;)H8-8GesjYk@hW&OBgTP4#lwfUMg*jwmvXdB=R5$2U}0W$ z=SPO>_wlHD@JaSMK`*RZ84qt8i$=1d(rH~rq1Gu{31ls6>Qa>wzJnZ0)IJeeWQIUJ zd)b-O2M41-rRE|wko@LQWU90`+24jKXzwMO4f<*9dt2tac&{6R8)!E|k~x_}zdb{y zkO%C^nl7#sgDC}k#i5RVyP?NJ1vm9?*wxQRQk`%F{%Q&N1cxRSqt?&ya#w{sl`&W;{dnHZJFcu+G)&U80 z#kizct<4GHmA+R89OhqXt)e20IeChrk*(T;O0wi)9}}FhVhfjLMhj_Tf=9ks=Tb9` zdw6mJ857f*Zt;6IA;e7v%b#VBe-wqMJe)iDSTaS(ovzvqE7ksb(Aha~>4FwBkZC2I ztS92bhY!Z4dNO$d0a9#!+4F=pQ3S?|Pp@ct5G(uD*^YN0rbVqE!y+N`8i$vgP&>`{ zPikDif3YE{%JDq-*_Hp9(}~GJwBMLKfA=HFJX8G+|&6m2K#}5Xy7&$nE=xR4x zS|37ZHJDWOf||K3-7cm)kfg%D^#Q&OT2K4j)9JjxQWHy^<;kPX<9S+9^#x$g>+{Qb z0uJ@3JIYw{k9-Ozd1-u^hK|BytfTlOq@1h?`XltL-`Nq!1Y^3RE2S?`2`sYkUt7s1 zI4v-oM6LA2Vm-49{^gz^?M$uY;8lfOgO=B1Zzo=ORQ$a*V8fj)0g&7}s-UiGHa{BP zIPe^nBw_uGjGpIPc%Y(}`XNI)c2>U8%fg!TKtfN=_hg&}X2QspFT_}t?`-*43AD3PlHk;+%)h*ntyF(G23c1m%HGyg?id9 zHc0?Z@~dC13K%(w5|qrgrLTqRtQ`HW=jH@|u11SGkt=7G^)|SBZO3C(O4NN`jeQ!e zXcadoubo(7DeT%lkV2`DXn&q5KO*m0U;YSmg{n>EnegLkxQPZQ@DQ3sZOQ*)t?kI_ z-QjiB4`cQtEjbjZ&^Z|lR25n4b!++Rc$>i_v2=Z5TWi#Jt;p;^7VTFTVTKhb6msW? zyEcv9LXJ__DE8B-Gb?0Win%(9&|!M~69psbNBiUNyK`43(MHb)>ip~=YgU!pFE4OR zRJ;QA;FVv4mOtlf1=2X{y#x&QX2Z}Oi=N_zxYeu%q@kHwD?8bYFp^yp#))?!h?Q$= zYu>npf=oa4=6oZjrh?g_y*zD>QXohSLaRPavbPU#&Jo3&*gU-xhICHi>K1@Wu%9J+k#KZ9hYesWm3ro}5X=a=8--Dm%Sst|81^29Sx6%_=R$ce5 zbw91`YS*HAJ$YIsF0_TyCJLRKCkBn>r z?3~vgEZ`g+*_5D2zLQhZ^pbsTacI}Lg$L4tGQxt0*=lp*Q4MxOlT&$Jm$rVtDF@w} zYcdOB;pvahHB2aaJZ26)>v~$)udI_dIqcil9$>bXXSQWJ0oQX~#`yz=1BXCs(8t|_ zy%brzv=8JAA3G7PgAVSABE&7dT`gnJnHd=g*UB2mdImjO;2wX&X-)Ad+WvVI1A(U1QIJM zO1$cjU(9Qr>^DDX`P?QnLipw276M$3e;zx!#H z>;>c_Y;Q3#-K)i~mLv?E?DtjNp{(AGYg>q|% zT&b+936)39OLt&f#swVp%0sRS&i|**MWz3YNZRr0Vs?$T-zk@%Y1E99aNPIh#uj{Lu~fIlKBm z$~Fou2LhxY5ZkSwP@U%k(97$(0tJRcFEFnngHv*Pwa<1tqDnT$%8QiUVJoVNcBc7& z*YW*Mm2J_jmfJ-2!bS#j1C_jn3_~PK%obPnE2V~Y^a-}cn2#x@Y$6Vv|Cie9YA8D0 z+iWy{R?ui#a@&&?*RL$?#8aNHmaXZM*$C*oQ_~Wmi6jk8nY$d0n=2UC7HznmY$87d z_#>s%cD)A16MV(QIo_cX-i+o8p84z(9>=;qhl|}&G28Q6M`-YQWjz$NQaa}TnF8K>NLr5Ix;c9Kxt`$r!7BI8N0r&st6DrC%NPm5na9Mq zTj4n^jtMgM&MUEeN?W&4Q&z1w1(}@e5!%ip8?`YoIsI*)3u;7Y(R43??;zIW1rVozRrY?JZjuVcGtr zQ()$6V%t<%VspHpkMQ-K&)OjtC{~R_bJ-m9-MlvA&^@}Tp zloi)qR9@WDP2L-*E4vZ?E25xZU#{bDPuE8zqNy=+@Dm6%K5Tan=VdXw5v;IN&kNyR z^~3#j3IGPSC)R7LQ*V5eW<=2_Ll~qd(>{5&nR1^DVmN{q8sFLYw$s={v89)LkMW$@ zjELN~d9mlvCTuwlV%es*;gzjU4%Bc`ghX-Tq8cdy$vZ_whVnCNSL+r`Gt4{ z^|RHTD^Jhp!v11jV;4Ga>fhDTh&?jAxyf9=^V;@yk+RlC(1uHM*6b>KUwpjse#bHX z-mieoeSpQR*Vc+$IJ3#Nv#}WF!twCN-Ak8;pm-R!D8||;RNwp6oYOd#esNxHy;%wg z)@i*f38qhg!TlyZ*J7981_wFDjEqtJ^ftwy8JD9u;Nd$0s;HM7yNz<{jGE2f7ALmf zT<-Zyu3^*ZRSr_0dw*g^(AcE7=IbAve082g6od@pFccH|xkYMIDw+Zj%vsIqu?xPQ zF_`mEGJ+FUi|s|WtaR3$%xSyjTl}R$PNT^;JW;XV3ijISfp-`vEFN~c3H)$=4uiF8 zZ7rlN6sNc{Xdt8)UnV&P-+;+Y)|C2zxjwZvchreoYP^8J+St>Bh9YsAv z!bdKzY)nfKIbtJb%|ElZ(#V0p=OA)t6LLX-`~75Ep51WH=ffd4Mi!Tr=l;mNSqS9_ z*}+!HX0p033sO(|cf1h&n0%?qygoiPu_QyNZj!Ag%g7L^v?+h;zi*U?z7r-!o36st zB(<8@ijZcmZh*?2?#BAiS6;k3e4s(D7Ryj_n9_2XGUkG@E{WWOmss#?-yys~!kl_#w{dI`i4`Mle7kkyR4#97(ad z8BGVa&_8{0tD1b+W0n9Yp~tyWubqJc22!WOH$rq<9d%U^I#I;f6NRQ+vHzfcnXh%6 zE$qlusZy4S;ScM^X~y6`-yzJB;^F%F%RonpbP?bAD`!-)x3)~EhM6rrgOtF0#r`#wOpj2UnJ@#xaPV z1Ih2=>ap-yb?Vs5q{PPFKu#PAgZ27*CP!>Lf>Ya;behe6b_`cJY*$XNYKuejt4j6e z`6;QW*t(=3W05+Y{Re~emg!5&%bgd5E?dtVqqqY92W^>zRx!L`!5i3OIai6+=HV^w zj(bnn=d!0CsxV~g&R`W?n*hd4^uv*_?YAdUIU`3#hUNI}$tu%+eDI#0ZUiTy0%og%{D&l8)vM4pXnHO>zjEbN|JeNO#dk)OR&Z5!w0@~R3RNT7#EZ&s5RsQyoE ziEPdd6b;Yev~gnjKm>#FaC<~`9tk2Gr!g=M$ciaMOF z#QgN(0?K&NKZfN$YGY9T5ZAfkxV}Mv)vTUV!VR8A&&Cr-fr0@D&Bx67?HTtoWa@!I zUC*Zj$?{h5mgF@UXh|2z@PsbaBcn&xV!s1T`pGsg5k7vr&1@Xfc(1v0<$)!;7A?P{ zcqPiDmyY3UyVLy5v@VOD7VX5s zrwK+vzQWpbIdM`4iV6PP>IbX?;w#gkNcUS=n24|#oblkYi&iDnq={bat2Ki%v<;{ax)-&^gNPlj9VB*# z?}tt=`|Bs>KQ3~eDP6DTVR7syz%Uyn3`P_VRC1@?S`{)Qe5q-v$RYgI7NJTAM<^<2Nsh&R2-8_vbd(xsWDt|mIgmghmj_H4bslvFvR>8h=Y$1!MdbbJB`UWy zthBMXua+=&=*bgZN{8UO;tCY#aPjbr>KntU`AA>QXfq$E9)xSp4TXJ7z4fd|l-#+Zx_twtr9+ivs$GU!l zp_vRZE7aEu^1`qRerq~>*E7Ry;Ay{K!Z~)1MavX&USKdqObtI_f$vPP%jsHyeAZT> zpz$IrBNeCsfA+OoZ{y>q1Zs(=IOF!td*UdPMEkXOj9MNmxYHa48br z{w9q}*&zt4<(3e;yz?wGu3pC_e-Zq>%Iw?%hJK+$jXy>_)tk-*B9%y+Sk8_Lw4p~tWG~den62&UUi@iJ^ z;4SA)^TasvKW(?_pJLcuOrLB&Fy!6>95so8)KxR`%K8jW+X}BFfq>!BkK!sW30BtD zxAKC+!^6r=z;pfg0S-)U0BgOQc1%0pJm$CdER9m_C7qMYRUHi=EIWtDMXpBqKZ`M8 z9_w*i>Tp9Z4a6;2wUkl+mZSckPd&;B255AiIln`TT7!{`i4`Keiz1+5Bj2xEDresk zNp(FJyuAQybT__icTWL3{+C}v$Et+j;fGLb`;(OoU-2(ic|y(3uRKqhp5Lul?xghp zTg8OFuh)g4E4Q>~Oi%~_el29*zmWI8+=}Ark~9)j`fU`Xw+g554V}sTF9i30|LvXq z4qnuJz@YV59h}Y|$^89=AW4`N46f4`Go!%@{es4ys{OHg_jE?NQu3*K`x!t6)zJ53zV!GQ<+DeAh!G4TpR za5r|~rxS^A=V&C{#s8SW{{JpJ2ouI$p#W=3a1zFW@_2^^^{wQUfAIx&m!}fzEb&nK zgRi&cJC&qdOq<1l5t17sank)yF0NaAe!FOonRLIBDsp-;a+FSXs#&s~@~>Kp|Ho?G z-6H?HSwu92@(w#ul(jugOwI;LxB$fJA4lG6$ zwm|>5%$Qm`Lu+7Q&|6vW2biHUii!p&(5W+|TZ@os=XCUT-23Dtjfg$%z;5!Y_<88A zM$kLIZ1$!tgg-8Py&yV0AQ);_`4o0-WymF&A3H&}q9l1Qy0Ns+5A_+!*j~S;n4X@# z9r-?Sc18<=k&gl$9v07UbDi5fiu8-+$~NK2bippV-qzc;Sw^v@4X# zg+Leh(vP~jSt(9eU{M2D^wbK~<2xt73}>q}j+KiTuSgU2fJDG+;}SOYaKZIv1Lb&=*o5+@Lt%Wi`Ghh@^~ zj+%$(pQ^O((n)FW>x;Za{@rOkA?S%Hi$O_%MePl5sV9sKOkT1!5C6r>mF|!EgpfuY zbjmU@8LdE}oCB}&%{rOMWzUO7T(?gDp903xQ+l}}5>pF;$tCt*I81ok4O1I+l$>QQ zBw5f}k5`kB)bs;Y_ACk~TKUny;McYVa%~b(kk1$Rz0*ZO?$$@Nds=)p1JmKxXZC6Ci>V}5@6e%+{! z*3rf&oO8@5$oJPP$Iq~3rs4@5OF_s#iqo1=stOE7-cm9QDZ)Q|sD0BQpjqf>xKk;v z!vYWU$)ngCfW&-At<832_0^8!EvFI~gx6Agh@m($P6e<;jOR+3^6p>T9MuoR7ahdil8S!_X~sXm#u_ zEY!DLjIPwb!=F};FI-Huovzv3SPNP&DcNyi#E|Aa=_wykaXr&DA8S!cZnB*!(oaR! zY41S%u99lI0R^5oJPRfG9m_~qy|!MLdL}05G!SnH&03osiV?hq%MUF zSKkSX$SY;`z^Y@J>X4NMyCc}g4FhSU}Aza#(1H|?K40mW>pMlx{krS*@6@#abr=9NV*bG)2fiB? z#wGKH$3VOiZBfdK9Y2*VPWNyL6g6 z%#%fQZ|U}ICO(H#yW$E3xzc&VG4sN-Dt?$2=!OaokR;4M<{S55Ld9-{pJuE_y$7Kp zs1t1@uj9UXftiapq!7p$c}c@4ATgR>h~0!(P0=d2)ng;rxzlKvN*S)15o@$JC?(HLQ`TUjO0 z8N2+r#zVhkiQMXb_JwLa5cb^U#z3 z{+T;*1gtpO924ax!0z^oILt0er<*P>zf#W*1y9s~4PYVy#=)6qSo6D+0q_;Qw41XC zL;xIYgyOuPKZ9m3rcFDF_1%)?SWV>VZjL!!iGY9aF>A0=8BDD@QuG@lKe$78j_PkK z0w0objq#J$ti;gU`NH?@%j{=qKVB07L2aEjk)*sbIgnPR zRTs+Ssv6@Z#$z|-93LN-H#`BMhy;ukPHFN3C;nStdN{^rMS(r{{dn z4puq{ZwBf@8ya3fyaW}Q~ zk2{JbG7)r2I_|W$XDS%Cf>?G$S9e|l@{HD;)Hk@e<;lyX9R&Nmj!+2S<9OTB)S;(v zWNCc#>_WR81pMg&--qSw9FVRL_)7f{&oum-b%^-^vHtxXWm^4Df?EMhk;2 zNTo+x_DB*${6g^D->rt`FcpS!bdp6Y?Q-W@4S*17y3p{10D}Y_*Ftd9+RCb3gwXXR zp!OUZy(&iS942J-{~$kk)9Gi8sLzn4mQBM7Wsh&(*p)Rmagh<^bz zi%hY}_=(}o_S-V4pScikL^wvY-2jdSDaQp8W+5PXd<1R7zK~G@8aU^pN({Hooi0vc zrDy-39|RTl<$~jJR^M5kauusV+YA7{E@&z9o|hO)90#yX-{gNB8xe0i+PsYi+U;5{ zQCqj(pwyq~RRIxVX({uA#k$3i_w)S)A*5xcx&vg0TovNVX>8rqwfCVoIf8#W9|fl$ z&Wlbsez+CuNnnZD6%=h6wG+8|6Btr#yShSC$90rw8uP;5Vk@7E9d}-xfI~2lr&y3D zqo#nt>e8H0|F<)3mLzE&7w>v*lSb1^Hvk&zLOVFXgh1C*UKb85ZZt?CD}5 z3rko5U9Afj-9pzM8k$1mwhTZUB@C4B_t@!+=!u=4Hei4NRx0{~uY0IEV=QPd(HPec{*E-C~gCA}CyW)|}7=Gox)8_e`kc4e{-*U_< zAw4KVtB%jmGIVS5x*&U^d_t$T{HB0>dGM^rVuGu(ieu?G2Xe#fc!G~VW=(E)E$1!R zON2m1ze*CKfmZZW@YU<~2ud%Ts>Q?Zoh`fLEkeuVxGP=Vva-Gcj`8Wmgq`DZmWUsv z`cw*qIeb1(S|ox)qKKn{MYYiG-5;Kr&2q?7!Tj~eG}P*Mh~Z2Vm$9v0XVAuvUPS>u+A)|NYEO3mtfh7wHHUca&)17+ClVfU!X? zHz$Q^9k4PcjH(jRtWD=Q-D@+GXZvbWgx0I2#l>$eEsL~tb;TVVDitC~M{wNXKd%)y zU!b-(G>ODreMG}{hM9+<@8$_S<6^rRVL9s9@^Mp3Z3xKA?*F{WlF3xeO(WemD1h&vw}gDHC+12!7D3T265;@P=sO0Zao{ zx=!Io5?s6YvT88gj}_lF`e})1IsNHX1&K*B>0J2J1D1_)+U1wOF+xW=HJFt+jgc#e z+cZHYoA`GUnNB3woQbUW6*nKtH+`nO4F7yEqhVjc5CGSRgciIjVhuR(`}N}(W-VfC zbA~Y(#1w2ebj7dL?WITyY%2a-X5VvLx*avbzp@!(>hFJxA7UfHtTcV*?K{e-WiRl@ z`f)btOE<~yD&kkQ8*<%<@E>H-|9s|HvMjfxVd|Xg(|LFooK3D+GcaJ(0~2I6D0Z0_jb_#o7-(tuc=6cMv*K=VNycs z`CcVy3_~k8>ZRPDH<tDAFLE+pYe~6VJ^ReS_I67{M>wO;GZmxWO_>6uiIg0Y(U6hHb$@N?%?z~o%3Vpde^(BeA`*uSeJ1YtP5T4jg@zOj$S*so-g z|0pk~*ve-)O{qy0pjD|Rsp7eOr>F#JOlXq=2)snSB2a~U37itsS`B@~gH&6TwNb6> zAMZ6xQ?)D!ykhD4KI|2L5ctfKq5Q&(^^ePY4<=QCudY*Kt_8srzKpku*a&rY^30*#oxw=3Qu=Y^F()Yt%cuQ=eXaG77DoLA!Ltj(8_hb)alkrp1vHsK81FK|7pjA1@dPF@3Sz3 ziM9PYSFuh3#idZG+S%E?n(=rD57(q4q2?kE2{dw+A$`1b=RdVCPxpEYwDQ8j!fufi zmhSBAY%N0C#VRO_+heTxN|~`V=*6&^m&T-=Y?Ouut%D?ymvbe&VwAFuJ%;kixBrlo z{@s=FxB6+hqIWCg%kKlXw-ZiNFxz8{8^=UbGxT<-wrsAv$u!-VR-?BpnXFWh0+a4r zP?}BEIxF=iaMH1ql&I51GZ)AdUQIC5s#d&GR<32}N#v9R_!KQNGO~J+#K)+)K;X07 zUfykv-iKJDuA-9LoxpxeS?Cc2)}tK2SFYIB>RZAqhn;V}kHo~0ni;jVzk_5-M{#wg z20|S9E9*izgA<l+0U=)MC!ikMwd*Q zzrj4_viS#FLHdXXFh$Lp3hGb&uX5t)ytcK|u69DnC1PGmJ1w(M96{KAlvE5j>tS!Y zAr`%k)W;Bk*DfdGiwbg<9>lFn7j-W1QGm2 z#T_6gQtXNwCpV=ZBV^yttS;Mx&5V2ZqL1q+EiB6MM|zYg^rCBj8MQsO&Rz*TIt{1= z0gLxS<~Bs@{BZrv*cu{)z*V9*u}*Cog8!w!VEL`R-ZpLqa!$53Pvrt|c^z(Y`QSxrl=TcuNO%bu-^bMUA4yWm?Koy$%6GlTMJ5C@5(`{?1w+ zTeK1Lelf82LYWHI3;$=b8?nuqf=p@p-9|-&X6DUrrcu6}^PW{avQs$Z|8O8B!)R3_VB*X}X3q|8 zux(M9+O#`~A$24VPn;-prCiW$IboGg*G9dx_m{**^((0x_b|vWlnc$9IEuJL5=w3c ze;C*weP>H6Dd~9>WoOr=LuIGo+3`BL0aGqlW!ts}SiIQ zXVz^+d_ zJn`W0l+gn#J=p-@)v7iB^-NaHXJ;&NB#U79LrzMUA8;S(ov|YDvbdB={fL%7q-@7b z<3V>Oq-?1{?NLD>CVli)I+5-ku_73LF*_{|Pimu^`{#O$Pt6Kp?KuvRXf0N3jCEnT zUY1=QdqC!Ug9fIjNeOEXN~g+gNT)K*p-S|wE_H@1Yja76<^BHX$=aj-I4N#Oid#t! zPuQ_&Rf=KgtW;fU=`Oxqlq>UEpG&+0HEo@JQ7d9K5Q+rdvXE(`jIeA5=DcN#4#mL4 z)e3Vs&VLsa?tDg) z_+`wADyH6V$x)>#;OCD_F`mDrHy7P zrd=}LS#H5=EYx>6zxGhg@KDHWzHpAeyi_~#pVY~dpb;n9ZKP%c*IrEIr`Oha;#YI$ zI{z-sB=P&w=EN`wr}8cFn^eRbGRSL<9;aP7wsq?UBQ^W#uMAG_x$WG%m0%_ z@PA8RqKXGr&uG4v`z^VI^u?Wi{#Sa?#M-}eE9&2_JYupR6pW@<|Ef3Y3modjkCwYE zw$HyOfq~P7GQ*>z3gWJC@pF@F%;FJKSqn$-UxTI;*!ZDPp3&7xvcqao2(k5L@OjsD zFs<4{;crk~Wy{m6f*l$v`fW}IEVmPHTGN!SxPNj%$A-XE4uf_~>IO^D{y5>|Uv*yH z6A2=<#v!q%Z)c%5ENkc6eLDz~QdWy6&{{gqgo_w09UWg2epUNi%LUQwCI#_9I;-s{2#%)4#) zBuhIvS3D;kB2hHJL!nozS~N{>&Z9H~kvR@EL=boBpDg)8-qsWEttLvpLV#!b2ost2 zIha@^kfY~Q%oY|T(QayD6aWaJMal{a>%&mnA3uJKtn=%O6?{O=2@8vOU8Sg~80Vlz zN=!^_6ev!eleaC=GFWk19~SMhM&f@6Ngc#$vSRe& z-|0gmxcb)SMD{@i^U!tRo?P|C3~e-rwy(tD!%twq?YN@F5}3X=8s5fp@%b68yqb0k zIE}+HN=-xAW*r>A$a>xE0?1dgZbcjfQh!2Tm6g7DDst&Wl*+xq7QY|W+1-<4h7l42 zhEqQ>GR7vde_}(2r^ACeTx4nSY-EXk#$n3)rTNoKbgoTC(H4tXg2doxAB!K{#O1o{DD1uhx7D}H^=K#8xr;^L-1d?T~>B*8&=SN6L z%j@K2U0rpjKT58uY-d;+9+NU^o-vnuE8nZm5<#m^fVrTP!(mi+BCl$zwvHkW_h_yP zO_ppfpgh=|#S&h(v%97R%&rrKzkiiPxT)y$;z{MF#5pAjwl3m{=$E#YIiAiFv`!-~ zbv0yH9v9AoP&k{vJ3jAx7NiBkArhgHULI;B|9Eu<0S-)M1)Q43@Dp2S3cww(MCplP z8S2zhhuW<#%PqO1+KL+*W+~7%jH&$UU{1y;tMnHU7xx2$J|{9R2(DJwh08A{x7W;V zmCImB`%h+O=4!{-nA>M>P6?M!_b>*hw`283ug60>l*#@VX6{Uxj__!A1MLfa_|8Ve~=`@iZjAoVQDIx^6;Wr8UR)tLmz|HdWVH zD-1NY{%HRAL4eF%w)Nj&X+-M}y0w&Xhjdam^>;3=>H@q`ZgvS>o`2xIUe2Hs=7ugx zANyYW)6ta`s%mNLiV0lNusa`T>jequowd1Mi>>7*ZC^b^^|=b@p_x?Q9M4m6 zoOXya{whQ!E35eC`AB4*C^)xaRW3SeulCRO78!MqjVP%pfX<8H{(<>aT>(pXGj@f#bjb#8sQy+bYJ*|mNTr+#U6~MqK;|bDESj4qKwg&O_ zwbgQX{?o>nPArB)=by&X#J{J(+dM9mHKAeAP}M5Sb*4XAl=en$`tzw-AN6$i?ITMa z&HZTIe&DIv4CB^k{ecO9xf2af_K+*PsaZ5ig5;U@rsJ~yuI9P(8TP=fmE?gQ@i|UN znN2NMA*U!5z*@DB^jdwAFjJW~8lCbl2(xQP*bH0!PQ~rrHWjZ@=j$@!x4(8{(I%q5 zyXu?0xlVFm=s)ANoA-EA!Zu8fI$YO(foyesMIf!`njs;fhM&~m+I<|p$=0-`eLGw6 zuO<3Fe|nt3*J;Ko1^NAr6RCQXZ}};-aS(^JQqlsns(qN*#77yJ84FY2{t2hklm+cu zhKpe)Gl1zB+z$=sFH+IVls3x@wkR6*4i|1Z%gK?s^A!GX8uA+*<$Ex=)xrGw_t%8; z_|!VkIj0)ae)P{F9lQfGCGC0~hzQ6@TjyG%qj!QYjRK2j_LMUa>9(p)uRY64pOp8_ zIkZfGyC6=u)Chil6T`IKvtV3dY$Cl8>Ui?pjQkIv8+IlP#|Pl!3xlCUT#|bD8)_ta zcK?d2jJyfOME zQy6Qm&{fgS^gJIeod?Q`>y^Yaeu;i38#o6cMXHu>G9FI|H(#GY(GKi9vqc0T{a}&2 zO5Z5{;gE21e3isRO3h*+-BEEn>y3UGrr<%}2n#tYyGjCYzw;AA*PoyC=-#-6ecYpl zyiBmqI)9)nPZP`cm^*#dn?3T6kc_`Ja0&hT`z!S44`6U;VKf;ota8u~RFy3gyQ81F zW-BYWRg6M54f{I6(!`^pvJqfgwBR@djl&S^o@Jv;Po&Mw#H`lkWyr}4GR0kuvafuG zVmZb+6X!gML2ufb{f@`BgjILx0ZcO{s_cFAu$7*ffeP#qe4-X+p+m zT4X__WhAlOm*|MD8LxgOlWkcu*5@~sYo3dP9NyXacB0zeuli)hF*UjB@K5%@!r5O$ z2DjFW&>adRMxF6lpj|Le5N;m;|kSbcRsSQ6+5`L zC1ojl_lR(kER+3gaA=fznk`Eb=IMch=z;e80mJH4>RtYa6~MD|vkfOdYu_D9#mxBq zA9-kvNNd&4#lL?mQ@K1K zE^J)T%u(V(t&$JUh4yA@sO-fF8Q(lxAH6QF<`?Ty+K5ZVtnuXQC3>3%ByI#U;>6xs>c0c;M<1?CZOd=yTf)_pqLyYI2AE_KWH{t8fiat4QK%x_bN?E## zPLOQI`HKzxSlq|a=XH^{ct*`em-O+#VI#cPFj8x(S-;kp`$JxZw08a8RECTBf0)qy zpRJC~2?M9%AHNzC{PIhE6vnQI@>+Qffxg~$=>FH&#B>rE%V(5e<|Pq;NO9~P3Ibex5|Gi?}INdb#Mv-f=RZLYkG7a9GD-Y_EF&P>dE-!U_7@QeEKdOB$ zNF-m6sfO-UZxpRrDd>HcSK46+Lw6pJb`Veq6Bt*U3`XERdV>?JB|PL}Dp}G#Y`;KB zT~rw_`;n{#FCi=;LU_FjLPHs?5E6rD_rgAQqoq=Z89Ai(k4UQkNfMJanBRiFBCo%) z+2`^JT;3wVPC*|JfPO9|r^ZlEJZ>RTFvp_M#PVo^<*cKw7W*jT7EQ%mY>S=qAvnu( zJ(GLiH(hP+4xrD^g+)av2`A&kO3VAGYXX~Ldclp@Zbjk`i>Pt0 z=$PYVjXXH(8>v)6B?ThclK~iDFBRxav_H4F(9v54zsz!? zn2~g}>yzsPm9l}nrL~_JYn8->H#B=Hb!rJK^nFDWy7)}1-U;G&6o`Mlynk)$)}LQ` zk3#k|{CUpyP%f^Nt4L^aZn|{7BGSs!yVi~vfgJofm(}Ynn%voP9Ge?6sFMX`k9p$z@&M=rs^!ki4;k^K zLZDBfz1QfGA|4OmU61(H$78G)Xx?N=^Ur$2Dkgp!+LWd!Kii7}bfRZp-8o(+^t!#L zonz_pZQtX5xf+hIWQfrnSq|SwvtU;6-3V+M#gO!Rdo`> zFd*vfBXKggeL7KHW(a;#Ylaj!%o6iM_zA_bO)HXd)7Vcsv?fBILf&j%jsbOWc+l+FcPGk!w+nK+aBH{tC#da%p}ckdP&r zqvMjlpIr!y@GhzVNvDSml~%1cM#$|n=|6;!5aD{w{gzmNtmCJOgZ_SQ{!iQ(L){kE zGfKOfJKrAAoYg#1|Y`%CVlh^#vYj$eJ;ygj5!9s($dq9@z%ky5DF0Cu8F#T zwL5f`sDPZb%%#|6wHN;^i}12U*8Q!nZU*AqTEy3{PFbdgvUw37bbGyZ(!EQv<*&bt zM^iCFT9O4sG-|>Q(6C|d-#B(A?(>&{Ug||PUFVUgfv?8+k(}zm&WUv$ z#%;6l|DTitGxaA3YjFyX%(29FB?1~>=soi>b+z=8hJTC^(Ut%9sUAOai#}##7xe zG=5?}J0F6LvSh()dynyy|BYJt11AQF9jw~S3%1xvp-N~gp;K_kLhzuIEo5h)c{aBS zbK_4J3~tAt_CCl8!u8dxtX+%_kLk$A>F!p# zJ2thYyK`^qoBKWAx#!$-)$Eo(_KX==1SGt<9Z1>zAD5#iKOp|%2O!_H9h&1qeQ3SgdIG2 z{QV;xuYO8}7GK(Ppyep}QqmKJqu z@zxZ|c@n;6RDd`IJS@yDlV+k~ZWLK)*v7@ zxYQQowKJpmf!uLDJWGa9MqNEV-`&;^-}N zc`FyW5AtRHBXNukw%!qW)zyWG`lGxe;myta zs5=SoFeCO@_!Lc|$>LP+c}sM2UG)U|1>X0>GxNSUUHUV&3S8Io-xY4?jyn^VTxJ-2 zN4efSLy!AH!6zoR2lXlN#Mj)v?6~=iXbfB1?J!$K`+St)o6T8&p3QN$-#ak@{C{cz z)OsAM$8s6?6SStfGr)FGD`2`UrK|pO*`AbEo3P&e6%4^PSy^e_{IzKd@Wx+)J zkHzWF$dIbo(V|1f6(Ydi_`bKM%=gh*4LWL@y`&^%1IZmg{npY<|qOW7hLpMDLPG@bdcd6U8-!#=S;0#igiA z?e)VB)p|2J*_ebZ31-;6^#`7^62U?k!b~i%rv3n^kkJ7RJV@kxBo$wce-cRqI;v;QZn+ z+sfI=DPERD_$)DTjYO(Et;WH)=RN$efIC4We!1aPbW{7r1V^5<1eRe9DZgB595Uwu&#~T~a$b*6$9y8NYUQZWydnSp3l0xqNugB7F z+qK}QMpesxQ^T?C(dG*r$_l4RQ@W+<7Yn)%*M3<4lyo|i(l|eRB~lynbFkJ!;7EHX zAJ5%#wMaW^)kV;R={jv13RP1sevkh00l)o=IoNU94#mL%2x&&`daY`#ctIoLBdEy6 zi^y2_p2rY9d9q)ye%C74*~BRz$`8xahX@yeS6@1Ynytn>fWI$*}7-KCab3{A`wr?U5!<4|?UGL^V%fd6k&%?o?G+v;^u-Mbl zGoSw<>-4KQ|pMPe4_+N z)v&LQGH&i+7d2h(``1TbuqXF9BS%`9XN8>mGWprgS7p~IUFX?0Z%yJ?`ILRE93P&d zTV7rUOgOq!z16%T>3-?xE7JJVYHGU7vWFRq z!X@E*8DYyQ-Umu%W@aVv`?03!U6FL+R_xR*DJV1>YV){_hdUm_P^cNXEXNQ7=_|aP z2fU7tAF~=jhqJ=vjYLpdXj1j?Ryruj({PV{TvMxe)IKkU-%;t*E?2;n%llvQXTR97 zAd@Eu?v9Cj85?}U6LF8O-{=?~*RV1b8%kt8n_;^_Xkvp1?SFjQ`GLceqd3V$d=6wH z5Dwkmf{J8f4LW77O~-FD)f#pvO;@#f2Gt9~hN*V2fUWp|@<35ZY4&4*dsstzAT&3d z7`E%0FJL{BA1)>l+Un?@*9EL3pr+KIp}`O6Vdr9Ts(jy}5@ZIkeO1wWiwPT586RK^v&OECWMWir5KDx4 z(U}aS!~_RB#&J1(T`PSA?Bl_3&$V$k^zV) zQPwsWtjSIOBNjep-^d@615UvhsUl ztTTsEwLBd6dEl&MwaE^49svOr-4WO3t}Q@ji)(!g_{2P#y)lM>-mt|77A= zJO8rmv4mi1P{c_)wx&X ztJf$32+y*O*BSL`4%-+E;vKW)x?`cNaZ_tAPerE+^{*(am=GHXgpq0f0Ca zu&tHS-beLUX|9I}(LMZep)$QC_joV8zPa28&9(X_NCnK3V-^Gvq<;0$B14xRZipHwujOBY)K*=s(ByblI8o;St+u4a;qzh_g zXqsG4+%44P!T;oO;(71O{PP$ZDJksZpn_9mPB^1O*ui?9w6ScUaCCgE3-|#w^-Y^M zEo7}3wm%50r4I-`LTa6qtxyzQ>$^mE(BKU=W9VRxZVAyXikEGQJuDVjiLV&_bf`QO zXhS`9{&1z8&rb9P1Ho($ccbw5#YuVGrfn+FGiU4EyJPd@x3FeLA!R(u8H8>GcfCMx_-S6T{%pJ51?^ zhGq}$T|UzbXI15`J02o!^~CdFvr+N)4#fNI82a{s?$<|83rlTt#=fs z8E<)Z9UxnWBJK^4gQPyQgO6c`twIM?ILDp1+MD}E-`L_zIWeTYIfFur3gyn93wxd~ zHjaJPb}hQ1z&?fK=6+T6!Lu^X$&5#!ue14>i>VFEimHmM0UAW zJ16kF`)IWZN{f(9#?7H9ie3bI|3vgI^k?B>FQ2p zd1WQ2Oq7CiQu;_pCq|(L1|c54F*MmpICysJ(D_tE6(R@t`f*?}y(bZ`7lKOmc7N(s zquKY`i>sTHT4TEn`c>xnJeWG^eFD{o$(+)A{LODAAW&}+JXjxkb{ztwz70=}JTZa| zZ(#S%mg{}EPejmFoo_en_DxA$U1MoDtH*xpZ~%V?@yb(Ju-+)rFk!$O`|CyDbdyZA z0_QJcQOL}Z7=5ae5wI~NWogt|wBSSgd3D=#0h5E?fZ$sp<)M6jl!Q6{o%+&f(tRGY zOEfksjeVjk5#Vw#-0&|VyP(j&(I=S(zyKznG;lags;poPwlpVG$arIqtS-(Q$9wxg zLhNP|_f`iHV=N1Z$7!6_*ct47hXiPgY~5T!&rsAIZk6up>MB8KL*UY*mbw}C@2FEp zIQmMZ6@+TE0IU~rY^=Fb{Rp$3IB?)2Z4i-6vKqQ{&raBBtgM;UC87uC*T+M(2nxRf z$p8G~vyfuZeNQEL^;ZBt?Di`=+?ttgS3N)VGZf|p>j11swV!d{lD{l0q{sT7mBnaB zV;Ofn2Kg~mEKAslFaF)56%Cu7ZxUf>Db311P&OxokfEMIwGV3V*@T2^r&1MEOStlB}L+>6iF0!}JqDh7nT(N@&^*2Bdv}URL$hDtzhF-IXsx?Y5Pkh9x z!KfOTxak$9<+IqKAPFKyFWv!xa|yxr8>sE2qDo4UQIKcY{5y^_4uSMlLd;6vWW(>^ zzbp2>z5#Xl`BP|DteOnxZIfeM_?znkIi`YwXW*2A6*7DK>Q&1ZAcbtT$ofJ@Eg2*_{him ze7Wdet?*EgjI?xSdHJY09l*N{t@v*t(l%72>`F$yCD*wm@e}-dpNQ(RqVNlJC{lmG zThs)P=#FCeVl}g#UjSe*>AB&G@0V%`C1u^h;}gV`!!XRFZIi2O?|A$^>cah4By72? ztRRm?r)I-*QL`yd*khb=eRDH1Po=0UKgkE>?Y(0L@QN~HWA;}+ z+OjA7C^W(4PL_MSyrQD3=5Q5Ah?Av#yW{Smzl@4>W`Kx7w-x>uBmgCwO>6j}{t3t!dQ%^C=6 zbl%w5$e&QRuts>9(5M zVy}noSEmgSE*w99a>PFyYH|SrjnZSIPe84B8&3~_gnDbu!ySJ*gSN^cIxU~6uiM zVO8BeK0~dv@p%5UJ*sv|paa1a;p^R9By;V5m{GO_F zSF-~BX8XLpjm(^X8Log*QF_{x>gsa3!RXpF2Nx>x*~NnG2@@&j6=g<;bbJTvuPp@K zt^yE5S0}qKKCsQuCfn5%;i}-0pCZq@Btmzv&M~R&nq)|9(8uvYyKOwR63?3B41wL5 z8fx$34^24y6a!~Y5!cWnPqnXa<(#4~Yd0_U=Z6hqJ`YPf#bB`*vn2n5# zN*8O21nRs_hI{P4dumV8CZ7gDtQf-fqAe*NQFLGAR!Bwh`HX>tad4l~qGph=;MQcjAW$P3`{3SQGv z4KK58wJprEp-dn(W^|l;s|}~t7B*0b+g80*NhK8T`8*s)?R?R#>4haLq5z>qxO>L6 z_I&&J_^Y@M%g(7~6B%}?&SfEGD~^KnPviYY9sPzRgisUp+402Km=VMonD^Pp37zuO zvDB1rs<-;xei=8W2^W3nZF>(p@Ndap12xfAl@Ad*Yct`PpZ7#!vBO4rXzAYU^t*zh za#tQx4t4I{b;*-S)%+x=N7KXLrh>rEs;iR$NhPRFQ^O*-r4^VfIh3hsFXeJ_bM;)* zIAoQVw@wv(CrT5)NowAAaJTkN`;b9OfVM!7;mg7Nd;Dw#M(ShbgFWvnxkmiZtuz~) zk!q9SByU2g5%DuxYapk1fVdEO~_D4btpQ(U>N^I3Sh{POuIk7&yE?#r+RN(tmE9U^Ji3Ypgj{Vj%-$STaIW z^aCX)#`E3`7I8nDW+$dvjw?mwRBS|ix%r9Ef=vWNq^f8c+M0st(DEZ~Kt0ErB`+_) zKfOMRyq@10N%h85D}oChT5oH!RbXR^7TPNeYh9@O0KfqF{@u-qG z*@m%DZULNQz7MANL4(K_=}5ijLB`pixb~iNh?$D|5hlHX>}REDI9+B%LP_r;`3MXK zZMnBpS|7Nk;cq(|V_I#31m{6eQqyqmTh44+P2HXd@t|JcDi&(-qOP^QDKZ;$Id)|2 zqT8x&I~D_h#*4As1=57^Mq;5UyiZI9lB!UiaGr^i^TaNz2u~db?D}8999H}7D#KC) z*GE4N&rh19X!NA=hMY6(3tj^eNW1q~oGpILMfOvuJD~1TII^6U`qyP7H^%htYg3YL zJHJYy$?flrI#d-!H)IftUNG}_LCmZ7)ECITYt+=m$^uM>bcqDko>$q-O`P*JdXhb;Pfm2-+8P{dw%PaVw@4XgXv!%qHTTd0)m8NVgCTGVUex#%WPSW9(>ak zOr|6{hK_Se&%5)zKW905hjE;}&3ju5&l4_B;`oO&l$0#2HWX>%Lt2;tS8O}C7XVRn zEW~4k<8-4p2Zt@`cD5NwYlYt65XmazVuupT2Ao_B#Ip6-jj~NYBicFPa{V^V_yMo* z&RF%23ZYnt7~G4b7qUp+)ELT{5FDMD_yD?pTm+aH-*v4a3jK0Nm+Y_TiQ<|WJ#q*j zmr&5eCTF&JwT^fA94MyXX7BO(ml^Dz+E{DXiVE#P9tHiEz#RH`(EK#4D}K&&^p)kYgUpr>2+C440uCDJl=IdoSVy8uHr{gw@OrB%OzFw(34#EgZfzT@cZ zzjJ#^*mbYC;(^4Vd|8?rsV6KFSLj}SLEN3dK&-w95fyp5)khHp4Aa;~W zD>Sp}H(mjAu9+oPMQ@!m5t*~LbY%Bjs%y9HG;CEhw{%*+spV``-=&$_Q)jAHqwmKN z;5lSSZ%5?fjc+$7wWcbaKhR_>Rw4s9@6KE+9ciR0y*$CT`suqd7=1X^0^$2GkXCt$ zFPbSHQyXg-eOU7b(q)*)cb8kzthxVA3uZv04*G_ow)M|T*G=c8Q-e(Bd;2a`B-;=! z(q#Hm)n5lLZADNq>Z>U?X7Iti1w;g{B(XNVd}_OlnM16!pY&t-%G{}xS%w2L`m@h% zy0h&IaQ?V6fXF>HT;rEQtc@3p8(Ldi_kW%%n`Qi%3gvcmL&c8H1{uHp@)I5RM@L@~ zR$g_cvkg|eRg@eY2o@tfN-T(D)yN!$uqw?F>1=-=rtHzn+o0^>jMYkQ`zxFGf_Kw8$~{0f??5g+Z{)k!#{$R+i;MTi;qTTul#6ax4w^FK`r|&I4w+Tgz4z?;@0T(k5xAc8=tMsOM#*YJ#8F*CL!5&L zXp?C~GiuY2KK?9O60)#8NiS0uOtT56Wjo4}*8wS^o1+899c=@PnK;c$j) zXPu8i_jhAVZ+1!!*y7abfbxtto(7!Z4wgH(atbQQo8Wo=Jee1E zbZ3Ow*TcCtdBVIOJuyytr$I*mS0G}-D`NPxVDU|%`%PJLt^BuocJDOJfy2IRW-get zmiKYsm$+*?jSnp99PCS$b^)G%F*zD{v8$9>vys|hKgSh}8RHNAla zwzpJW`|iUK z3JaOd*u5PLo-h?yDOyJ_4yMbz-|{@JP-Q0DOH@{=`_Q$MrbZ61mjx%YuxQ+dG=Uy* zQ{!Mjc-gB-y zF~|(7n5T$=T!$lK*UpX`j{bK#={5&QCza**>GPdHS5K|Z#P+=c#^L>i%@H#3`v%3d zudh{id4_>s+VTC@e(9Z$@8ZZg9L~PHrha8H?3u#I6!oKDK2EQ(;e3#5!Z1Qj10rQr z;Ix&9nOppUc|6V`SqLJ{Y1-278a^@0)MH`Y-6Z1KIVEEL*Vx9(*#s@}z5tmgRCd%v$w zu3rm;CX(FKqgJ9te@dvS#YMX5r#fq>t0yT*%LFF5n`UK}w?fB;hZn$Pjaf|O=X#wm zYE-_Vdm~$1sUaI34rl?;Z+u}b%et_KbE`TxRkx6g_d3#}YH+cVvbNR@AUypJrt@6; zv@BT<#ib=G?ZNd&N(kfiMPy3grTfT4^tl_h%eCa?Z5M07bN`LYd+T#M%i_{9D7$>8 z@JKpkX?I^sA+hz{Mw4=P!2zZ66W+g(vi})BY&Iog)sY%+#-i(fIf4(LD_>W%Y1GO! zl5wiIpAe9eSm(0bb!bumiN{>58Zl9I3hS~i$wEZDEn?fOxwf3Ag16Pf?OmT|9mRfG z`X(bS{(%o&>B##miNhoMI0N?lLH3teO!D)KZcKtp`3V~~;R;BC=O)~ylgGtVEP3X{ zH{Z)PONwdx+}L&vO-VKyr>?;iCS6f9B0RO)ahQGi?crxe6rZ11$g5x+fLJ|=-=``e zyjLSIK{co&8beMfT0RUWf;d@h#5B3dgdC?+=vAxdyr z+32OADf9Yu+OdmGs)at^)%uWud|jzF(3%wkPXtwEB$fXmKH{U<9aYN(MY%09)dMx zV?(oGQ^huDP(Lstx^4g9S3xegnE}R9@0%TqXFpdKY_?pF zC|rGP=yCV_hx$Zk?k+bhaMWf-oI%I?B+r=YvC!0$*J10|v6qO1rT}-GTX@6)D2U2~ zfsg7vUaCqND>04J`L#JfsfTv-_`q|*V`3tEtnxB?{CxV@*SfVWLpyc&g4}g0ya2Cb zXmpeYS;E{^aQvo~ap|}=|Dw;WXtie;Q_gSSMw#lq6Y@zgzls(vcFfu0R%81S4OTtV#Te~8Cfp*&FvZbNd}6eDLBvf%;+(3_lV`T+LS4_kHk z8V0T#xD5@7tUUpiW;06{TI2Yfl8b}$djrd0Cz1!9!iyR`p{T=}_U{?K#!d8ipSgHj zw-2wPMw%+4T%JltW7=+SZaSc7AI-H0F?)EPE|#=Ky%mp{a{K>@(>0G~=KWA_c&X9B zWQN8vB81F-rB{v5VJFeh*s%L1okt)#Ds*|FO~!f5EjUZ3zY9|tdHCdzA^6HcjgH9I zv=W@}q^}DET>AOI{cIZ1J2Wt0dL11yA)@I0{FMGc(66%X*J2@E3_rPihB?KstW@&}hG&b)O?E`@VV+EIev`dgshq7p^IQ9x)K}IC0Uunu z>vkJhNIBE47mMXZ*AP28iM)??IA@*f_`8g6X>rD9z1~X2x_r4YVjS_UHA-HM-kiS= z;30ae;Ewksr^f@bTZ8D+=v+N$T07dP$?+@wbU1hPES>^L+89ZIRf6geq@~DJEwS{BJ4%7@N%rUjZ^-3QwDfTXeeVleen} zZH8^;%3pI^&FE^GoQm@tO`q+?HeH4XqBBk22zQs4lPe(jba(w?-hS|$_Tb_gQNgnW zLVnQOUPF~fFe*fbc*>!RT$8zI9L!kxtXZYc25AUH34+Oh_ z>qhmz^9>IF&7JS* zO8YF@nDMT0cvn&T$1H>=FT*kK^j0(qS+jVgSyh#nch5o2mq7K>0}0sWO$G-4MhdRI zhvjRqe4VYCCy1)tQB;PhXGG2d@#_m^ER7fqjGy(}9|Vl<#%OzqgSgpvH(lE0sPN?W zCdA3juXd{T$_lr5Rp)wDM)@UPQvO)*ot^Z^TB{YYAHD6!uoRerlBuDQScQPgN5)H@7gFUJO%P91;z-r7g7cv*>KFNwvdu)SR~O$MkBdw!zOSyvCU-TLnhO zxMJxnrgmhNpO))xu%V8?{+89Syk{w5k5*|fyzG)c>=nVZD7T^o$X$R+2CkH|^G~7tqMigUu7hqC=kBKK zLy-!1f}E5O$EQ3i!J}65TbxlZlIdLpMt>aO>t0fPa81}aW%=l~DrO#hxY79cuZ$9>_K6o~idfR14YOu|z;$W~MPZ3HgxIP>n z&3s+#>BkvoGmc&9c`5+=TihaMy~@e^FrjLBay3Pu?*dBct>Bns&cv^`4-gty9+taT zmj^o@b%_Qy-(JNuow@Y$SQx08(>E5G zGO^+@dfAvF3^Pc9a$ld$T_+jQc~Sex+z~t~x?Co&nO*5oG*F0#Qyfn<)5KR8(T(kx z)Tnre`JW48t@MR+a_qZZb-x9O91=oBITsO!Zo_ppZwvWEP!#%T z8i_P|o2CIK*|Auwte-O9JD(@u)Ge?A(*H|dpe`?}zsKCm^L(jWs^*5g$qgQZEBHh# zs?UfJv*JnJy}LUR^aDvzI{ko5S6r9cA;LgubGejY)7Mu6kx`zj*F)1YJe`R;Rw?RC zC^Me^)^U+76Y9B{Frbn1?RdJDY+PgsRa?+nu?z&P`1#Vz24`s*7^b0jWbX;ofs|`= z@$EHw|7(qbm9#V{!(z|^?k8fM&-y4VTMFrV+aLmoHr1DD;?ouV>a{u**DW0YF0Z+9hnr1 zcTo207H{78vwgszAW}^IJj1~K6#EdKz+q6k6kmSL@eRR_i+$^paowQDYotD5ZE)w2*;~5E zmbYcC-DZ5=^XXh$094r-|2EGMxXt60%p3#$e|=F1_~Dy8@998q3arlC>B$QcdtC%e^|J2(wKQ%jXujM|w~E8as7B zMc>#wT5a8KHy_6nO*Ot}_U%Rh6o28vF~0+fAkl9pkt+}S%>})0R=tZ<7gGFg#$SCa znu!3LVvN>UQC69yZcQXnA(@|mEg=v>5%!-si2sSx7|1qX>~@(g)e&nc)H2hj-yl%e zP7#veSCSjPr{q}knVddU;|9S%q7XF;lO|1+RROy^Jx=cnA7++xk)8Aj3U&)gdPU&w zR|%d+u_OdEaY`vD;P-i^W!2QE&7Jj4>m_*YUTPu9DqZfcb;((HNcfSm6a_Z107rU` zB-x$t1KF|WW)%L{#AcW&KkwdwDQhX|d!tFqwPWR}1~6?^eyS!ui<92EgZ(MHurWfF zS`*A&s3_p@9NWZLMoQ5M3271({awFK0TPOK?@X_o$mBJN?u1``?k zRe44%BHt#Cxu< zf6J<#B+5>Ud;p$cH*5UXg#V*OUFHt2%du#YTSY&i`N9(`WzZR zp}hR}vZwzMdAXYage1?R?JeJQ*(%Gt{A&8J{S~@ac27o_;1;UN|x>NA(5~>qp+8*@0j#ePn!1P?o;iZE##LfdK78<{Q`%tRh#cn2>CX(7Z>h>FJu0{7<;{VrX7D$lE>D7P#~; z`}p6)2{LpZ8Xe*K;LWi3L`3L=*g}*VppHdz>~(*v6Nv~7#mN(>wF$xO?(FFasqxs| zMu=*AFZQMW3{s;zL<2~vd-$w25=s|Cgvv@^cVL#*MC&^Ngv@xCa|msgK9{O(VG$te zS@7zYX_$Wj26bt$GDDX7hpIYJuBX4>j9A%@jHJyvIO$cpl)r%CZf#bvI7(!wIWOr% zGlE3r;Yno&MI^`uZC-3_^pu`23Mt=h2iLp;h+9p~fiK@Gk$^XWL&CR##Hi&7kh*^P zRbv8#OK1L~r+W8JM)W3C`Yn+)5+^wbdk*KA)9laOWi*N$tKst(Sk0dtEp1O1EPr|B z`4@2FC7=ho;z1OxGo50u&QJqu`Ll}4luCE!uw~La&=FlOOq+O>;g1EJNW#H6p3gBQ z7aat~)n zMSG_-Y2SQkZi0xoIFj;#1~$Ft zer3?f(WOxkfETSt84_9D%(o=iuLR|NbPKTqYX-54)ZRb7;;#8`0{=q~QP#ll!B{1B+?s~wI^PSP0Bi5m8-geY=3<&G2_ zuCnkR4@xilVjci~vY3m&G=Lz3+7-`C5^kdyh&M0p$EJSVdyVUi;lG;=J#fuxtYLW+ zfAIc3zVXmEIkj!P!I-=BME?bY&yG|Nh2lFm)#X8-f_QL(gD7tKB}i1$f|O{%wjJGKTQA8-K}rV&3Svcc_=%3*LTcq zo$~fD8nEZV=9)wO{WVavQF*f;-K9yrLF{b$(&`5Z%a$#UcSd1fYC`FMBTEhg28SQK zB-2Crqmw8gXxc{;^V#lw1|VwjdKeO^ot#Xu>FVxY3s@&}xjZ~PGB!3v4oO}DIx!SO z5+Gw?bTcRJzH3ISyN?e};3Kq%$UO))@CJ1KNXvKrGHbARQ_-zg_r4HKGR4u#%X&PsBR3$4E&22M(los54=EXy92V)L zc6e7%V8wWoojrxrJ?aSMsu&{-Go1B$T^Zt_??VKKQjo+v8g`oaNW^sZysU2$Y88!csUJNNAYWv0x*z5^78J%dF(T-hHYHP`Dm`FnMP>7L zcBkJn2ndkd;VzJwpa_$wf1OSNRH%vY=K;4b^6w2>lVYZXZ(5xGY1e6dXi9`Ed>5nX z&j)XWxajI^r)xOl0No<;Al4_?`&PdvqN*~+gZRO!Z$))fUy7n)arHI572>Pi|Ld6R zza}@CfsZNK#=X^zQGPo0pgC z-LX-#`W4@+>b*#RQ~x8;cYobLLB+WH@PB?7#6`*C?W=DN@^XzpfdBR3!}t<|$VSsN zd_)vAo6+%{7zWc8ZQW~sqng1^N}eG0LF@VdY2<%e6HV_jh83xoYgX$j14v&YA1nZI%RTtq zV^jmj{05AGdAT-4CAICS3H72`@jo>bi2_xmPRf6!h3xns6G{bZKy1);1(_=wJ148t zk09&yeCL0g*#A8u@HeU$KD?J6n&z!65kxr-H>&v&Yw?Sj35~+9T!voD++JolPzUOm| z**2PgO?p`y^PemJZ;gna-p+hf#Vc)WnD0Ru9*y@f#(^5UDCU*efnIOcrI4@ zNer8Y6zzx(gnrOehxUO2)WX#q4Jg6G&k9h$%W-<(Slv_+lEs=A{WsAhx^$LbAIrY`n~KI z>V~J|2l)7pw&o`0s(wgFxyBR|d?4{;U zObO^>oq>;|mF_L_e57Bz_uI+(%SZeECl2jiLDY$$q+#S1zuSq1Zj4cwcYVRK3-(kA zU&?+uspsoE&7B%XGQSe-gQ1i)MN&5HA``}=Jfm0ZL`ebgLi(vDQ}=`W8U$7wy3*}SDOGD znPp~WWrd`QU&d7vU6UctCz%$psOh}#M2%)sC{7%InD&X=nfsN{l@Gg9?H0&UVf2QH zB2N`JbC33*>d{ZHf;2~>|6cB2o+ha&XbbhbU{c^WHp#%B(_!7GZ|XdV%wX2XXfNb8 zsV9cM!C8DP@A_KPI5Re7M)>?sv*RxFkeO>(1AAN@Id;{@KAz)2Ngh*qdWMf#p2Wh_ zg`{1O+x0iyIeG_NI|^6Hcu8Q({P447-ChSoNhW~xF`q7H_9f_{%k}Fm-;(S)B6>?t z^84BKnY(}2btqv#E)Nsfk7!Z1uJEbe`;VB}BCcf227DBGJsrQ=1{^qzTngO9qMTD@ ztaCNmTqcwWr~hr&{$Ki<{S)*RZ!f`^r z-}eiwGvnv>aa3)`L;PWG@SNkDhh9u?7TBFbCZcbfWnof_MZGt^(`cGyc>2r5`u`KR z#(cwvX!cmJ+#gIN`Mu7Y)TAmr{Bv``_gjEfEBsWhbUUloAY{`SdHVitxkQUFzAXF& zQqkIO>Qhok=K&QR{VQ&{q=~tS0rORrg^LH?J?aGXZaX41w(Y2~msPXUzgsxaS>0!7 z!@NK+9*tI+7rfM6G!@udtVRqylLQX!%;q5TdoHiDKL5fk`sb9dvH0+=)-?xRxo5hA zFW3i)78t4X=FpEffOEO6(D^0{uuW`l4VZ!OY_BAnh)0mt-KJ z);H{aJP0PrfwtKP!f~9WxPCL)f6pJG^h0?Lo1AdJA8(O^tCiK&?H*RFh+{FYQO|G* z89OLg^5Rw5VvJn=>Rfj;m?NLWgWql2;IFsYl!1B%Uu*fYFCXNxrD{*xg?>X-hCQp@a93^`qa zT7O%Z-}Efi0L897=+$M8Lx=fWJmds828eDmJVcXPdPKB50}dwA8uw+<$>OidFClwv znL^Lr{g?fD4>(Gr{!sVq+4#Rd^~1-nF6chNyLUQr~hH? zFc?ck6Ze1EPEv{790SL_?rT)!wS-64(G%g|z*lB8VtIT<7$1~j@rOogRdkCz622P! zy=r?XAt=-fCEfEwB}j`Dh-Oqm(Dql5VsUBb4-qx4Kb)10FF`d|lf*G_lkzFNOH6Ef z-}JS$dIm?vV%xibl{(f46qej4QDfR}W9f#7m%5wLO!L0@+{bd>j=sO!Q>yMLJ=pJ= z8a@_wfX>CrKlGFOB1WxmoqB%?yPi1IhbX6B^^Fr@$oht}=Q>8B<8Z)yyh;;)qJ4Ll z-f`|*JM=G#ypC(HT#rgRT6DY@yiaLbiyC$t^tPue^ahfJ=36M?rV1x%aW7dauH-At zDz10oP#3@XzJ!(%gN_!;tF9Dz=5b*kG(6Me!N9MCgh^ZpfuQjY!uNXQBs{C zAEGuL>WQSPzr7*|<7c|2&X|kNgPbBZQ7Q<-(s!b|DNS=jL#26vulGE|H+t#k9V@Yk3cAyk1#ElzMlC2Tyw?|dGd(Hzg_j2^ElDo0*}ZsUTy4*Jzxk!@>9Xl$H)EIT z^;;qs3Of(5O_JPRmWkJRVcvE`9M^I+iYSWq#h*M>Exc$@8Le#{#Ehq05y7lF)Fzr!cC$0{_ z=gxPqVAtPq%iJmEN(}7y3&)f1uZ?axEu8J!dg@bUS`}qslZ~77z?Zh=ck6nXJ;v+N zwd*gkSTN1jQ;*=6IRl=|MY*+FmQBzp6{|Ht6T-YXx1IKSf~v`)4{n>E-v8p`m1TH! zy9e7&E^iSP3?|xFG|=gBR3HqUZIWucy-#WJk*w^OA3s6;)EPD9R+~8daa`Y$%aEO% z{trWInFi#9yq;&0XOl3`boI?1?SqBAA4H808Oa_5`w)|@Y{wj!-98iC4wIpjL{-NF zLpc`n-uqa} zBb(|>v_Cj07Gj1M>n-}*?lphkn_N`zjWdSFcG23jFJ1LJUIeacQ;ZjB5K*|hN?dsG zHkHqFM|sTgt@_S~_s#F6wdk#`nO0x>+#=H!fd4Ti|-OT_)eDC1#o_mhs{hr4^M&_OU?!ER} zYp?jNwI?)|B4x(uU;-m2W;uwmuS0(GW~-0qWd(M>zTQ(c^b&H}^BP&NELbbzgP}&M z=&I-y`0ZCzq&VJYsus{?YE|)9E?{*NXrIoNjl`7o3ANVLpDQ;gu3ddo9Q)Fc58Kw^{-32mzJ;p&=j+3?|927On*%c4( zCn^}n=dyJyWNRva_r0aicg z7!!~jbTVssY+xY3%}rdrpKRK|mE-o}i1y^_&@pnqv<*wzM|cauvV=^}z8I((kg>5os86UF%>A=EPM_DO)?R^-WybmLYs{tFW0N9 zCO@{#ca^b!GDxZQEvvjHf1xqVlPaI+>!$n39(G36VQ-Q4HJ;-Q$%K99CvK-l@FQoH z8aGW|LXW_cZEv?4XIJ(Rj7M76wY?cf_m89NVZiOuUsg~Hc8E&nXR9)H9rN>I9J|F^ ze6Zn>CD+BVvJ`@&Im@?UOv+xG>_)4yCr98Viwd^OGgZ^(mYo5sK&|4@Ns{WzUOa}= z9fTVfvv#%M(f*uzez$*Xjz?wf)TV(*&af+-YnoS^;&jj9-0yo&E6cew%{3vva^n|k1FbRM zdQ&);F50KM^j4+$Jxkj0oxUk6csIiYxi&hPU9p~)!HpkKJ-M5M`{>al)#$yo5g5=R z!Jv?LyMv>?V*DVWe0jb+1cP(zWT50NaIqG%(bPhZ-pldoO3aDBs@|T#&3b& zfVoOtr1qCT$!5NMle3&PUv8%gLOhrq4O-@mC zH5&)Y-Uv;br(7hA$Mqt7DPjx0;A#f5_c_bYlG=4QBq?^RBxB5{y+sbKG`LNuGn^eA zZMYrBKapouU%MR^M&7Os4_lpP4`N6zGDWq0 zh{T=00!uZbyyms4N z1e)7C>^O+{rqck(a5w;jBKBvVZHNyWr7et%c*lVancSX?WoLWY-V)BY3z=W&T8Xn= zf0)d95_Z|MZ{>=y(_5P^K7l)CvL2ri?kEAf{uI({+2vb4xw=hT%n=hz+Q-hN2LtNU z;3I^$L-nfpB@T!8^N6~k0w(nj~G@QXM!`6I45F;;k|q2AsABm ziL0j}7`naV(1pVZ50R?`M>F)3Z??O9WpZL*x8e-ATKdY1OdGcKYDKMOjRlJjGPF?= zrHK!WoKKwQKa0Xb8m46hgU97ZxfNu~jg@2g^mmtD3>~A8>p9_lWct3R`JN6T^3SBq1hup+iRrGiJO8(09XK$T&4+{Q6ws0T=5`U5+F zv+czA5VyTzC>v>>6p5${oW|oqXu3bz57RzL&Xi^Rv=E5PkpuLQWGvnu7_lUnk8^oO zOk}ME(R4)#jI+dbVWbyZFuJp*3~6l*=|s6w9yb(eM;5u_ZaT3coYfCvq652|`BzBo zdzl#*`I7$305qrS{#;3RhE^2|Yh-BZ&Z(@1?c{-7J1CaIaQ`uYor1KPGS^sc6%Ta> z+m9J$@pi$QXWT}94r(ocf%wt!uHY^@eq@fD6!cC|koq{LYLpZ#!HxfUE5NYE&$7bG zRbKRv7HKHF9l5dQP$SUgC3k8%)v8H$hq68UDU$h>v`qZbVNP{UwofyKiy&nAa5)t( zE_s$ge&dn!B4USefjYU$1*tDXQaNup9)`ar%ye001`1k)-1d9gXFkh_b>SAakwTf% zL}Oy0uo8Z`6e9KFAc6Z#;-ts=Dab1A&F)?Nwd7f%t%*M?I|^Nw(o*Xsg%9(}^ZPcH zMq<~@xsIE>PfgmgjB^jlU?TLmekuh9yP@0Lc2q3$8)e<@cfUEDVyXr&svc#OzJ07l zOx?*FluC3dKW|Hrpo#Rh@CnbUz>1HTQ~CTD>fvEDYiCiM?WuD0-tMB0@scA)4k87| zVtPs$u!%P#!!g|fGdl6MZLrUiJ>!80ZI^ME0@~NZp9}`nwKv{mG%S%chW5(h6fA$c z1N-(E3Sx|P8%`T4-j^-N@tAVq3FC08QL&G!neM8nXPR1OVZjW&yr+ash#|#IH@nWtR z61XP2c}8VQZyphZ=fQ)R*e*jJMlAsaw`@4gYIV1ol?GnmD=x@X$LVR^len&*k~ZP_ zwb_O#tm~s!31s|jAF7fAs%SwTx|6XfsDIGrwz?roVs4WbJh^orch;mg!sUo5JN$Xa zytyjQ2xrUAqO7aI=ejmd9a#2b1govI7@@Jf@01yXda>VI!1i%ca7)(KV4du$SZlpJ z()*|Sq07=@>O(cHyUCoqV^40AvJwm?yI2HXB=?uEej=kLfA92#`)2u9Zz3}`r(6!r z0=ZSz0TIMeb(Cawg{OdRp5qpt zgq)j679MsYPuiM~+R_OsJZcM57A#pSh79@S<>hSzid2`aX(Yzlk4gIOI(<3Y5H9QJ zRTZ28>2}Fv#Y|+r47l^nlKh`aQNj0;3o{ksvLS0W(|%y4{Pkc7F{=Z+kdjVFKp{$) zY0ofwoFjHGVnq-BQ+Oes=Te7b&z&ecN;xGGIf-F) zEecU_Zr1k+t~}GcL8DnBw;K1&s}%)Q1vS$uX5%$PdpD$)c6?3`%UBwIaBz4qC<~=8 z8E5BmOeRqYK@?OIQuP_}1s|15j2J8#+q!2oM>$m$t4S6hv;1WsGsfizOEp8TfK^^Hw<{ z`js_RV;|4g!37nC;tc0du7xUa>@2sgB4_P2#EQMGtupt`XE?oO3vY1iW37n&6z&&j zulUUO-cT_%PUrOKN``6N=zZ|OP%+L>Z7SvYMvtwz(VR`vC{i^eAaU3Ou z7DM<)aM1=NrKgR%&Cy;~QWu8)`imXL=5oT3cClbf7kBfqtQuvF+2yTVU$qMoi55J! zi7ae2MaX45l;NlK^V2zf^y#D02e!e{?7(et=%>g z1*I3^?4Kwi=n9xmK2}ABf&+ux(%{%mPpVHv@fcbZ3fgU%6)U#)IEPPmmbF1Af!a=% zrAK_-tp*EHHN_UiqyBBN-Fo6MU7dEwK%Sg_=j?8|oSiHy558zBXWz2Jme6+9X-qpS zuCdd*(BAm{ngc|QCemT7Y+f`870_j9p? zKZ$~R)XF>`It!yV8Uz3r0AlXAYN`#}rJe1|YkE@Q5a_w?dYZCvh|mh*(gP%-e=aFm zwl^m}Q1;1Nk&`|V+{NJoDdWf>#kG9>_Nkp+dTzXGsrhZ&4ILYq+_NosGM1tfHZNEq zNEny0>b_BjJ#FK9?`R)z@=#+anW5VK85d$b5x%)>`p?h}ttT8+S_1uWd5B0-Xt>p5 zkFWmW6{;=8NznDFEdj<0G%P4KP3iT5!mrJVfnhMeg_-4vYScQxNQq_pn?G0qk8`7A zZS>_x@s?o+VEJG7=xT$Krmf=c3NP)a9aCX>ESVkE*1i#%SD61o;!k$8*)l7oag3aTbzXIg7Jlq}FSt z1#a(S4jd_82&lH$9&S_Rl=pX+DXq6Jo7+cYcyC2|KNH++3@?fsQ zw6ZGxJxhPy)?!=anxlK$GeidE@q*;tV>=n=t^&BGd0WDxq9Ixer7i9-Tsiwqs@~ejf|0xdbek~{MA`3^VXu*cB6*-u6FBA z)YHN=1U)CSEZr3B(!3bynXp-12WanN4J6g{JrlAkZ@*0;Qj+f{74?2@ga<+oL4IE6 zIYPB5*H8degfU^kT8YX{s$hAOSv$G=VTK1PRnB%1tSa}JAiW~qddCM(?wC%mP<5v} zt_~H@f7@v|?5^D#lk6+8yemRrU~PP+bm)3`ilV`!`GS|5X5hGiLP{uRm%^>Ga04a2 zu8bf9P2fMCdNtuJUe^ zL0~`_c8*VTR4aj{k2a58-YsK#H~fBb)096DT_vJw6n6m)d2t+8vqL*M@4h!W^nlb! zXi>S#>2P3o{Mx|^2H=!WR@LlpPabV|b;^3%bn^+QQ%t04Yisc*JL`sbAZiY>g1U8s z!=E@#-_g>pqG1u*ss>z%r4J+o>x0+V%&ZN9JWU*mff0B}fQ&Cr`~L{!oDvN`3r>)^?Qgi_~qnwSq79 z2bt=^N4G^?QR?n=Nkcc~D-@gunn2zTHPfp@`9v&A&#PAZb0Fe?H*@H@V(Fr9{C=sG z?m=gzmO@_aM&-er`a_&6(u*qvU3iwLNFKv()Ge^P@qWMd!EDeQc7y26sW7m+YUvzT zPS**mdzZeds?7m?)w~yvWf$vcnT_G#X3LJ)W1weMps*TlT=~LB+ya!E6ZuSgT9TX4 zdOB0R7yj~m@3)nx5zCrHXY<>GvWxTtLK;HhhQm&jonNaxj|3yq#*{?n)vCY_X8O?v z+HD@;y~m5YuFK{{^Y+^;C0^6bfrwkKNFn6FYB%y(#d?7~Sw#;39t`^#1N9}UR`iJ0 z=17~*-?`}XJS(8zcCtlp-($L#Lgez{A)CoKo|R=hk-dL^08vh?soN`t0T4JqeD<_s zR|#P!rL#KBHTl%)&zk4bHEbta%GF8^OLxZ0S(9pgfcQK**0Rx6Qp2LoPgPain=`H; zU87}5m#kekzJI*#4Qr|m46|bN=ZyZ`WF^N7$hdQ7)Dkm+%e3O&_&c%NSxKHoNZVDZ zn#(mCZ?+5i7D2~=XGQ5aP6>OVT4JGTzeWo_NqfE3I?_NdvKwjgJg^bE?aNxpR&kO~ zLKt$gwIiw_L9GcFm~S2#(sDn>pR~(yeL9c=G_hmSy-+Koy)~8D0^I(iKav`khBaQO zhS=D6%}dM55-dMx~bTVSo6Nf5a;|4N26X&zz)8CwL)F50==yF3l#w;mrP zIE`qX#LT}X^83&`>M+h>T4r2(v{KL}Gif$nEQE_A-7d|E;s+uC@K-XPNjTji>y@wP zNDW`DBfyYN%6cK>7>}NS#e12!${kvfTk@};OB-J}1!4j^Z9=P+fd;1I4_zrblq{Lx zHnIDV7(aM;=Hr|Z$Urb)o|nhRmfhGX0J|xFgDhu-_(md*|C03P%BlN) zKid`hP|hPR6%tL;GAf=M=m@|2)uajE9aw7Yv{QD`s+3(?u%o*RJ6RT2t!5t%Cr<6- z=3eJq9s>(6Q9q>T~_{i09VbVK8?3po}ua5tvytpZB(aj>Z#XVz(JEI&h#B!y}+;LY)7_s}# zv^zPxXY@sbEicRQrfm%SoUe8{cePi00p^qPtl{ygX1&B}C2&kAkcSB=pka)k9~nPb zODa%sSTEhq-ss@yenqd8sX~zrv2ZF2Hqgd49(X6PR>`rfcc0n4CR-yfU&DB;2cj;l zc8r{?w-!UY1%TqF&zRqOBCwt~G+YAs#RdH%-@W2gSkxTf%z%G!Vntj!g~bkJt2@v3 zxw99(f|g}k}w=3Eoa`a70?8<~HKQeF+MuZ5T|c_}0-KY1rMvJ}b>Q41tHTGZ0R+cDiTm!iE;FC@h$Px4y?A(ui4_kIleZzMfY8ry$Jy+u4rN}zp1MK6Gw!S|Ra4rf zpf*g)QV?{x(qWyVoj|!{KGx+~%;?n2it?;+_?u@y*P#|qkQU)U^PjMpv6T<3u||3s zcD8LCEM-=c2lIwGqeL^g%W14uq9VAJK$IWfcrlWG z`UCzO*29CxXIQwOGMuioL2kA&ZdS}^kre0_LTppu?i^Ev3xU_`g_b9iHwsYxc(>YF zwZ^r|nm){+7v7H196*8VFm5BeA1!-g4t97n^0RFXyeK#6Qty?)EkZm;mbBDtG!!y| zI;i@Z!3nFB@(LN)v(GyfH!ebcArzBwV`_6WEpD+bV)0w(6d77|jbA*jY6~%Sj)A&v0LX;KSLp`*LHkS>G1#~Q>wewTk|H>vJ4Y2r>hs#? zpIiA4D^=9tYErTUp1U1_qvtkoPBO=dQjqkNVkJ)d2X^l+yjC+wI!q3EFk3@%iQ%Vs z(|-|k;rST~W~x7tK(+u)sJL2Dzv^h#WR9W&cF^ev7XW7iF>X-%<`EjPlUGdta$VoK zPKch+u<{BAUgfTaJ7)xX?sJkq&}Z{&d(?Ciy?!7klblbLL_eow{!113G6Ul<;CZlE z>nX~jDd$To=U108w_Htix!wqXvH9VJxhVUWy^EYml4&~9<@@wo#J3F^p0jj8#m1pL z3O%XtH==meEz@8$y7n^(#ce-bG|I=Av38l}zb5gobpj~8q~~n!&o<=A4XH_DrxtO( z5>rSo?bEevZ4N@qn;3vZ_6JH%qrD1M=+*<8qCb6^XFle;EJ$5(A$V5U zA0M0mR2~HRN;D6rw{`w&9%mEy!3g}+gqUDFd%Hi>Xo1HTOrW~CO*{CbT-?WHWig~f zoa#c=A#+m;X!iK=lYf1Ht`SNlY6HlNVzJycqf90AW3{?h8`s~rDK~X(?qSe)_4$ER zucLuyO-qT+zZ99jR1PONNTQsXtG6;*0KGOI49wXgah&5ctm%I1qCNmz55QaW^1kkH zDPAjQcwhDDZu@WeawoKQE6ybA^O;2;i6tNL2Ok~v+-2#PXhyo{1)${8-kza*0_;Cw zhLH~nj6u~Df2okZ?^+hN6{zasY*I~C?Y4`wSnjsfg%s)8dAKM!rcu9IwxdXryFF==YfN|y? z#TU;=;e2R$#M)M-2}p(fon>gI8ap9(c2<}wC4}8L$KQFbL5%cVegOtC0FeMUhlJwow~}bU76m02KgMN4f@Dr z05r%T2YcS+Cc@;1T5bYR{2R3)9Q`4{C9rNbS&MNQ*s|&r;qWqQRt%*@R1}mgs9VNP z@NI+k@VJnF|L^J!(g85@eb2MPl2EMMSUQb*nCw*40De*UdY1@o;1#1WGe?Fob)Vo% zX_Y2_imTP%yL_9u%F!3^^-vj8y-&Nctgou`edjaW;*Wj$7i(xTQf+)>qV$yjSRvj9 z1CxC?K@kYUD<~uq`t^+7Q~;YzwX~$PHK~3R*IK{fU;2)S155KsBNc4Ag#21FrO3eB zT$$g97C&u}L>YiqmR1)+bL?}d+J<;Xlj?l{iMyHvM5P(Ya=Pq#o~c&3t^0|AWGM+G zvjs@%d-f__Mi&mcEAE>ObB5YYH}j0&j{8Am~ec@rY&=;-RJT~YO2J(^7o+fn1y=ISaX7UoKG zXdG38WWd)j_a0BsTNWCcp*(u{w;}n7CLlyiHjo1(i>(?QEISSrmEV(}tItGzF^mnI zpdgm1GJilWWF+E=?Sk!`lr>I1#&QaV;&e>$wH0V34Zhh*SlO4~H~bv$ORx8(2_O4m z3{g_zB7RNWB^1}K(`AUs$!{)sotOGSf}j+RaUW~nVBGPH#Bv{aV@m8(&w!H0Fmz2^Kj}`{5OlI9>N88R8|u1Rn~t=x$*~ z89N1+o6^_`(|p#&(iS}K=$owI8B;VLVA4ALuu++j-La>fsdAmQdZFG)-Fl`Ej;b&b zdhpBlqeU62byJE)s~sr68`kU7LUVeO=-pafX|7ETHR^kSs!;NQf)rRIwxN>=(AUW6 zMN^~l(5~d-9my#6g(;(zWuM2c{uRFDkmsSNhb7s#FimG;Ab9q|-Vx|;+uxTOYb~u( zW}+X1rB5xmAL5>F9o&Cj9!V6J77<|PniCYJSF9Vzw%kPxq$(Xqo~gI+29SeGu< zp(o@5l9COY$Zt10K0a?Y_dZYEM>qhoDHSEF^&N$6hp|}k^04+@PhROj%VPV|nvsFw zD~05JqL%0Qx$l^Cnua(JS51up0T@IjiU^ZkE@35Te->SMs0_o>W>!~@9Mt5mqN2|Bt50??XrRkItxG%xkd z*0*Nc8(t`DJr`mVd2$4}jGn9TJxeAj2Q`YY(th07B(C*_Sm}h6;EfyUXKOkl!4%2g~(k9M@*M zuYt53UlRY>^VEFc%%u6(;dI9JfGsdZ5W~XNE7`4}3`iIoC5CabmmnRuT^Z-8*p1oA z6KD|dt%QCwPl{ZCw6rI&{dU7~VJrYe#@1ZvG(3J+<$sL>U=0!aPTo9fW`S)-UTaOf zUX|Jj-+66UJI5YeEjdu`#)4j;_nqqP(T#~m1a_T?eA_#H4aTW)AByJpH>W2H;HM8A zS2mjqj_DoN@U)M5VGYMf5t1<=_2cz|i5{!k_9>Y2T)_7N5Gv}pH7^1tNZhrSER02q z0zVaD4cFHGJ5BR0$2^mRuX}TK6pK&TMRwc91V6k12aLMhdn+LZa)RBj0}W*oALKnA zsq1X7Z-ZsNS1_89)$7ID&W(OU)cb(EsStoz=UVBR^94?oHMMD5Z;HPh%GX!j6>tbp z@?iB@eO#bT<}VZY9nk!bBl0i30`(+hXo6<(niagpFrBEg$|z{T#J)#mCiBXA1&z`fB~}{x8n@GcFUkV;@evz_SFthnCbR}uV>W3)SS8n zKK3<=E74wF^@~KAcbByx*|Y_g=cNakqrhvaEZ&+^)Auh8;tXxT-gyw}V5k5rrm_hW*TIjg)B{?R`}4bg*PT zF+t+Jp*YE*{xt+FKOzF3tlEGgR2QR(&js8C%CV2-SV(2@+&o6>n4&^3Qb_8y3 zS`UzQM;h@umL52TShz2!ln#cxNE9RVz}WVCUCd**>G{a(sSAX<4J_tcV7>c2kOjbS5o`rFnk# zImjy7(81q(W4v1K&VJW=tZ@Z$;*b4PVb?-631OGdGcG>6vce^ydonI`^^=y3w4CKs z`?~rX*7syHl(KM^5we<>^+yJ>4rqtCSroR)O|>)~%!D1Tk$qwTTlc>A_Yf<#93h5D zrFHtT!*`i{&pTkKf+V_uf=fQS@5S)9ugoM6hnf5MFr0#_cN(TA3G_Ca8XH%}>GV)^ z^)-j>4U;{o_Yo;m>32zno_Um$FL_P)W~mpywdod0J!HE^!B4ceIs#h-@}Zf|M}-Qq zX>$s5tCq)+L2H_ycD^deD*KL=>)(rUc$VeA*eDLtARIdh=ke~(P=1jUay}_LN^0vn zPld)=UYz0k3-8I@@1!6K25D3j6Sb9ul?-j#Qk^72!u@%YKb-D)B~I>n2YR~i9ln@U zHDqr0O}7Z7VDiivg>W7nzyjXpVPaZc_9uFE{WRa;)<}5Q?5>ifCBH&US>j@J#g3QY zzHWaz!YkM0kSYu^6ikC?qLu#Wl3|Q0UIj``svdL^XKHBKcx^uppnX04@9=z%k9p6=%d1l5Cd|PDLAp$S@CD-l`SA87 zNW*gWRpjWYD6jR76vV9gDb6wV|8-o(2%O1y!ip#2*!!QXVMd ziR#Lyc{dK&+(vWVXD2iRpS*S7OT}(5Zor(J>I?S-D!S`8-xE^Yj=fSMY%91jL@DFr z`N}=7Ls4QgXLNvg-SX8Jf;y5|ylUPYwhHkc?pe6oT;%IjM75F-q&e2Xi|AFbpTdR4 z0@kup=QXs}{~T?B1C$(ZXEe_NZR1;lUjvtdd7?qt%%ys}OZ|4cspS-7n^Neo;cZs` z2XOP}#OG;=Tw5&N331`jV%zIGXwSEb^$WvbycK-zBodCLt_aS-E%`(oo82m3&f2B4 z*^(_kn8(B-6inXohN)LXb>dMuFG>b6cTmW(ZZI3VE(Hp8C~(j4XCRA?VbKyf&@g+d zpAw)&P#a;o(Nr5n@-5$*Dl{r$SC@a5NYHA3WAgRJMBVFgOad=U$Z|RV1m*GmT<$?q zNWDHxm?_$hI`&C{#tMK?{sw7hnOt*O%ebt?nVz^m{1o~0SwU>g+6d1pbC=2OoNJ~7 znQ3Ej!$&h+v6Bk$1C9NDWEGEq7ITJk!ubsl;_;!J(p(!UC6!q_JAFl5y(0`clUdk% z0fx;83lbB@F)_4iHPY0XgU)QRIj9jQMJh6JXzwNF7fLC~wITM}4^+hX!{4y9oi;6P zNw*iMCyDx6XNnKY9&BkJIMPzCw_(z3nVUGU!}knj(&DVs4f02)kk1SB=V-h1qY)go zy&D>=mWxqPe~*{t`jXNp`ubo?iYv*-2OLIeJ}H*N4<3+p2~PU~wMuQT-SNHv34>wl zdrv_FQ~2|7Hui(n=B9%c4%++8(U!(}L?!1&e~ziyPIj_0nQ%#SUdtar(~~6*XKy7^ z0#-RARPFU2i54(Snv}RS{Kjq~zvs*X-LR6dPd=rjpDw*jQe|yR6K=UHq9hcBt(A1M z--A(|l_Tr}2AzD~L9xc?j?;lR%)NHAZ%kd8^z~<(eQ9Tp)mkq2>AG&m!Y$@}n{djd zjz{>)nNZQ|)!YhWu1FEWUifc(mn0yz@XRCwtu(i(g0S(Qu%ru5Of7B8OwManGRNfU zQ)Q}zV@o}$%76!u2?-NY46EW$)|@z&ZpM)e1ltJ!tTx>DY6x5h@+Gxpe=%_Vywefr zQ*fQZbJmlR0vx8bOb^J1vr@d(7jf6xrlqjt4OERFN!M^Wa~FLuH9@$<7jfrLjI}f0 zR_`8-IIs|m3NZZW1wcR`%y->wIft%3fyU{A#B}o*K^=%GTa7%xZC$4s1~O(|VmMFu zM0FT}V)?pc4ziL^ip=U8RJpd7e8fP>xhCR_kd@>1`bhOYY6=)H&cA6qj?pX+4V z1-s#p>%eK`RqzwQUTNI$jbNN#_5i00=cA+O=S`WdLiA89x7K$kRs#qQLRpDgFt3=@ zT~dZ?`?UM+tH6(PKRnK7Op&MqHEnO5W0;(cK1W9gyB#^=nSE9R+e8#0Nb^Y8jhrT7!RWsnIzr}W3yTH46G7c_>~XS3+qvV21XG7>0ekm8#OcGUkavXeEHL$FD+ktz)1Y&FaZh`jvQAABKjMIXl(1A5O5c(;g z{uz&ikmCz&_d||f8~bP58y*f+&A0R}<&UAM0K<}2{ZpL1?co>k?e2oH9J65v=j46lm)v zE6nFl2PE&rMH|Jp!N8>g(PbWgi^0EF9iLDuD#)*L)MVR`L540^gaR2I$uECpbk0*n zhbr-*;kQYzFrGa8U6uTgpBqV2v2abndY=ZDo#oMfLqy;!IrPbJ_nFJxvlA6+kF`fG zNK64)(Q(|^^my5{1%187Xxb1tf@p+j^h^Kl{a;`}?0SRxKSL>Dxj7=^HY`t$PF_#1 zPAmAR5Q7Sx`(*LG$B%by>We!wQeqr_+L#}!0JaL~9u)eJ>_*7h0suBS?e?a;0zUPp z>+>S$y-Dwzibtx>8{`T93RHj%!=O5zRi^HjjFM*u}GG5?sxlBhV z^!AD)rOvZDjtHg_FWhdGJJ%>9uZ&lj%a-n;WcBBfIIr~=$l?*KA_(;ehmwi0B_*tCKEbToM~bG0bm2U)}M)_s3WVN&RH zZ}0VrBO{PjCpao`X)ts{<}bOP?{9v8M=nAj{`gGhoS>B7is=2;1;Sql5+X7W``n$x zG{1bTxqPu1(M5KY`bV4j=fPj8J=hI z0v1ywtl=~%XXkD`XQ=f+UdWb#Rm^&w20I<~`$?)&ok;LCCQI~6d}kXQ&|t2GlVeYE zbbW{{<+W}h_h~<#Y%NuhvfeSw*~kY5m93A@HDQ1W2~h>5lmnjjcQ@PU*#x|1_iNC} z+y{$tn@Z;)9lts$j#uYLSOB^mipQTAwjB2|9iue?&`W^VzLMx9Y zdx6ydoxtoAi!$P=86}laCZPLS#YbbE^fgoBAlG>1%%@OphoQfVp#Utsjtdw;g=6o& zn}u`g6ExZo!K3EWUAdw`nBe@|jWHgxoV8z6n*F_J3wBV>AZeKvGJ+Z$jzdu1adgo+ooic~p zpkcUfqt)F()v>OC`$b^u-z)iB9siK}1^SB|T04DxUqQf(m_O8>cr-VFPf-F5MdR-2 z!*9&#v+&?21TasTE;Vf-y7MEgz|M?cx}>!;af`00f(*^lzYRPY zhrLa(tJY0t_?2hfS30SOeoKU4e{Rf{nb3=ym9}URwoY7pzy6Gr|+EeFJJ$k*`q&*qI>J87&XeAwfXq{ zjsa+79n+Gs^CO~vuUK@#6LHS!|NHgv;!qODmwCa&tKWX-YkXfY^Ghgn5J}<~%zbuR z?xc&JFtYkt9MZQKINve+&D7OEQqywbw~a@;&vMTYsNfQH@ywU&lURL1LHyBghHMc6 zziViulMOWIo4Y^Dk5;>3qCOO##0itWJ(7K9`vSKjja|Wf>9{;CYdGKb641Zm&a*N8 z8_!0oxy*)3UoP5eoaf(*Y6Hkl9rMBL9Oo2tBh&>MD0Jmf#eOUKcGzg3>$X$}arZ8* zSYNC(U296XkHn^#Rf&Bt85HZ*U@p*4z^YG9rPxdrf`9F;|D6HvkY^-nU-)mGy9(yF zwW1`P?8gsl?}wn32hqD=%*~qhF-;r?5$D2?TgX2&Tkj1?T`Cu+!qSXkE3}ff8OYLP z+Sm2*!Tc2q1DhCMfz@jX0fc?DTWT4gzTEW)ewIha!9b%xms58UvbZ7B*5D)k5ati9 zGw(&0R<|3N{-I2{KY=YQho~M$eeqt}TeVHC?Ex~S9D392rMgDL z`WC%@Bzi7;Z+m+oDEoPjzOycEf9sr0S(!dF93lDr7ShlYm@=O&)HXjVEM>15af9qHES=f;*9v^ z+4D5*mst#h%7rcLBtYdcn?s3sssy9k%WpTR5$J3e~-3Z;c7xId=posTMr z1WzR&6lbbc^MDhSlT0-;clkPBKR?M4%nHAOVprXF_feI7+?ROx z+cAdmP|!^;ZUcPH5_|LFSjK(b_3Go7W)NAV!;@dsiG5cP(7(}7<(gEUWHSjw#-#oL z;5{HrfdyzllGB-ERN1+{ycY@HvLA4M&Ub@H0TbNT+`D;n>VxdZVBM16HebEZhp~!7 zkv=k4=-T3^9;Z>*=;@{V_@1cqFSA{rzyEFV;iT-4pl@&_`iWJ0%r zu0H$+z1c%>vM*nfc5FHn+mgGw9)^w20v8)Zq?OL=8ava0{T=ls!+M=z!Y%q zF*F@BcLAV?dn2+oLP;`hr(YceK7(F`Xe9A^*lHK2k#*#B_;w>J?4oaLKUFmncLLkd z?=lO({kD$Ps=24d4^+z8HI>U4_4BFxnWrf+Sa8hz+BMA5nOoV_uQTpXO(Zl-*48~L z&@4Bar|-56=D9~;(&9n3H9eZ&ITBV<&~JaW+~`G@>n|n^pquzHjPct1Kr-`A%ZwK> z0KCRWtb7JvX+jB{d++K<7sXW$YFhaWk2oK_yjZSrpagv_%LqRgz zL(5!%UrHw@vMe4IdH1ty5|yv?74cyuAhvw9g291rOa%n#(IX6-Q)-~if8vxA{*-vJ z{?HsiPp+696idI~T-tit?@Ef%d4-dsPyD_ES}2kGaR%uN3q^gMw(Moso#Y--%&;9C z>8{VOwvF&1_O6siWQ3y6kY6YYs$)Ff%UZ8!95~V6$ERMp4<$C<^I_JZViEa3*^YT_ z>ss;^k10dfZ`~=MKUr+yx)`eCM}0ELCk&Nt6zAHYwqqcuXRy!i$(wR=0w%=_bana0 z@Q>ZJDF${k;%UpzeVWd=k^!Jh>8zceRa3!PNc_tS3?Neb?k*PlCn_wBB!jsF!3Ao9 zMydJy1wY6~Ut6jaX*LNKiv&n%>PZ%taQh2~_DJ2=;(YKoY}W9lCe^@B`yK^?>Wtgb zm$&;?2yLDoKj>?kIhhl~cM58l+4il1moQn*+pb=>Wsmhc2Vc%sC8}m}kJ3Nt)|go;FlLG*q9A|9V7B!k-yt?toFL;D{4;Vr6d?t zMc``m`A~QtO7JxnzIs^h3wmkTtS8I-Fbh{*KWY(B44u4ZR5CFvr-7koG@}GiiCOpO z1_5W z6YJFzmeK1NeOHs}PzG;U_f|$hpZ8W`vrENA;tn5ZiazArE~>2LCSnd?l}H3o zVFt_nb^B>(3f2@j9soiQk?lSmU%rf1C@N&o&vl?QmOd<qguQ)GXk9qBpG875^9{NeiwfT~C{}lCmgXjzK>IWxWNYZ&Gk;r8={* zsFSMPjyvGOE!4aGvaL)!?SVr#j3lTC7$Vw*T*@ISzCiy9+gwFVf^d_X`K@8QGkfZ1 ziR;j$^6z5_d?aU6)9}5IKp2J-o7gmv6AXdP8CL)4J7p?beG_9kJZIrj0spUM_RrcW z+(yb1)^)_VFt{3s2Lpn;jXvJ<=7v1jeUy=I)xl!xNNED+$5bp}U2ab;XWilRn)nW1 z2RxMjcuL1iRD=oc@T@pH_@9xi zoi33$X0o+UK&uC3=qA^{QsO%)K&#M)gW{IL{8@i_a^;d#EG!=kDuT{g^aW@#9Z;x| z`=v$RW#RCzj_rl_bEos@)vEe`eN>?BMmP@Mo^g%s|HG}yod6sW;uw|RMe)BcMzjV8 z2z>hb?<_a;gMYce|E8GEV`|B&6HH>67qlDlxo3amozwNx=$=xKm;$d8M{deKtAEU(ceGe~2ao7N7 z+y9ye)lCM^(yv!=|L23i;*>5;$8^p$Vtk1I|}EMI-M^9qHiupz0t{` zW|R;8<2~^2gIVhbmz8}wRe_;z6{`e8-D~s}4h1|8(=(s%0Z{w=Ca|&HMR?>pb3W^(vFewZMXRj+;{U%aNv@xzpqUB{^{MmnCSQG zPN0prcsus=C-G$0e{~c4fBr@_eF?a?#ftLkt%pC~lk)d9Uq3!U&8|~Tix)%WzurU- zLB({*r~Q8Z$2i5`UwlR71|_q(@<6L`z2*9kq5d<4U%gi+7>Cd?PfEm*_0Dgf`|;bU zn-MWF_ol@mZ&!fJ3;#_Ps(;;g2mIIf1rQqU%~u-Kl& zkGKAADNu2yAR6xJrkRge6ZOBpj^@WOP!%lBsd~eFEx8|m`Cn`QNc<&1!rJ=dm_Pad z_a`fIG;F`1$1h3n2Mb#oDY%2+e_fsT7qn_3QlqE&1ZD7IrZF3Mts= z+K)kgzkQ;G7$N*$r2_nG-q|Y`|BuV`e(V!**`K5)jk49g;$v4zA?Vwepnf-7y1PTX zyqp;snTc!0@1;uXklu+7I!U-#=CmE|=jTVa(L#oE@zUJQ2^S&wwdHfj3pFEVBLz1m zRmJ~BQ&EE}M)NC&MW#s~3pppnK+c}GTN0WFY!=EGu22O*E>c~#?H#7mo1VU!6+uy9v3P@#g|%3fi&eWi>dUkZ+awt7#FaY?iOsgn3XjZd04h zNrOjYa86YQZRdmjp*y*ke!;p_GU(-9zTNtv+sA|&y+CH1i=9#l`etU##)##B)vjCJ z@9ozHS(%hGgM56f1)NoZYqblDiXLyzGFhX#VpYAz%I$o=vZx!t#w&*&PBeweRkAXL z^GH2@{9b5ymPuLP(a|wn2)CDhqS|z=0_GQ0s#jq{yF}LhlsHbFLohhJx>~r*YFp{! zHS%pMqjmPaWV>d^b({Cu5W2!d@{tmmp|8m0RR@B{mX_Kd!6DOke^wkqIyA;Jpe*W?D|&<$7##oFzz6-LWC;pA3wDVTX`^K z5RO1}S6N{U0DTiW@$mnTvF{FtYirw2v_#ZI5M9&|Ek=n>^k@k|bWsLTqjy0N(TN_t zMKGc>IuV`GyHTPWqYp;;?VR(T?|kp?&71FBSN_;n#_YXjueF|X-}m#Z7o?)%50>H845A-T*WK@QucgbVRsVnz#8-9)EJ+RJT7D z1-+wxcJ|W3_qe#w?oU;A2+FCizK#8{ZxFySv*(8!3qq?Yu*gM%t#Ks-gAz-AT|0O} zg~j01eKqh-PUpnH*3i%+t0r%c)z@*YIn(&?XSRt%N%u7%@LC~jDuTysoSaoI4Gw$5 z#Ygklx;1A|;XQQ0)hIhVJKsd@fSrK0SVJdlL%~3c!#;Xxsb$S^WwT01q$5?UBsO0& zLfx*MRarS)NlmTP+1a({6pwsu)&iW?aNv}tUBO^^!^g-dL+Ci@Yx?}MrB<|Dg^!L- z^2X?(Ao^%#aKmoE6ThV06NDAAIFlAKm!cT-1PeFUZ+bO-QT}S7!W$3F;$D+9614 zjAZ`HkF;=HM;w-Xeo)gZ2!YchX@&1-6g1KrBQa6E^Ec?eUMH9?ELyn~Lv;@*&8!q$ z?K_D`9E7+^ilMea<=}qGZvNK0^ZR||=?Qo&?-y#Cy@YZ#wo;9*QhuzhrM84UKTW+l zuc_EXfAhyI_ZX4$dVnyytZ%+*LVb1B9n`T1U^`U2y1KYJPu1CK#G{?8kf-&vEOm>S zt?{by*=TERl{%HUT)iUHL8gU{uS{7qJ#gtwn37(v^7B4_hjOUKyr42de<`-pWv;8M zyT9eU`mH-|KNWsyH@;ubndC9QLO@V%e!#-U!GXU6g`2&u1&&2M-=E-q=gk6bB~SMp z#>ugS1)|UABB!~y_yv2_e2+`*o_E#bw5?l|_vzQT!8?gW!68jkfb;m6EJ?w6HF0%N zlihl6KHDFEPy$uFan1hb)YXvu@r>$_QR6NZa@8soB6uB7q}mow=t@LBa$4YSIZrFd zTIFl(LpnMMuKJzE2{U_K{703oSkPI{-Ry5z7-9z<1-2Fyg4eofG~j;caT!zBQZ1Xg zK{ILANF(1%C4p=5nCg{{R%@=cj=Ou9*$C<08+5)8xSP*X(ju$Hz;nFN(3PRYwF%MTOjUQka8eK zhBWi&*HP5^mk0Q_DC(ZS;@%_JP0s88a?07aYsZG~%<7AnTRQR$RGONau2dyq zHLB1I+Jj{7KD!&9obc1*8XNj>y8?4Fvu|Kw*x-p232Zrl>xt@P{+wiq%h^%+kYU(G zXJ=>N+IZgsl+6q~{4&n|-3)(TZ)Fau%m@7hXo$6~Q|Ec%*;OtxXfszXlyp76rbaum z$xoTfFlAsrdx}9zkiXwUw|{ka!tW@N<)(#`!Z`UwcteNa(NvIC`F#Is+Htj8VxgSG z2m!7j{37@W)1#L^?^b$7{dDUxOTwAW z>907^7NfLzPkv+r+GEyu6N`0}%YfZ)KccjE17z`|N}}P>!=hIGiJGnXG*?Y+w;KI= zR~6xUsCjR`N^8L;|A`5BD3UwTHmAASXx?+p;B+^qMWY=BuhFA@Gd8Kfghu50QkK2+BpIWGyI+9KprN=r0-LfbLwnv-8+4*|YeyzLiB)J2J|? zi8)01I~HdOzez>+v!Ur}@Y?ceRx;P&$%OK=76Q&d^gZEir}J-$L)Bk>=X8gPNv4Mi&0R)b*Ldt+#9W|ldVCEsrP~``K@=-! z*P|cRtAK;&SZd`!pZYXueatclPKJ*9`x93txSK3NChKd(h@L?BM{ zu)>N*=LH1bz!u0t3jC1)am0;ry#2+8P>+GzMu<-KEo}NlfJnSRZGq z@H)LEx^K6!zE zg1?OVKZEs`pD~11+wQ$K%kqWllmD4fE8bZbq?t?ke)Vurf_rRC^Oz&W+!!r}nj{=U zMfnhKPd4k3RxTgV`R%qZfTwXU$^DLLX5Q%P^Uf_6Q*HBf8jam^SNHKYc>*-GuBn!u zKzR2#D_D14)HIzE%sguFbcwT>fy#=b&p&<=_xX+=|8<16#?`81rlV;q8k5U9K(EY% z{bfsk{lO;5KAj9~ujB}p2sU((4Ivlu+G$8lLy1u)dJvgujizl%gTN&ixoEJ)EEllyXp$k!~<%kPt*bjQc|1Z+(1 z4`E>qMJw|(A_1)&B>6P!Fm?;&gn^J#=p0d}8hpN3|+a z%55KtLN7(S?3`oI;P8<|Rj11L@dzTRg}1e4P%0YIw>J7L3RHuN!vL?Mfi$bEw;Z4w zywHeQnv=j1?n!gx!}vk<>C|9;0TDPqWa+>f)5JAOPH zV@KU5r#&9#31lO0^3;X~Ii_RAh8<_P z#S4ZR8}f^Fm}C9J1%0biQL}5;}{WG zX<`Io`|?3E6NkLEPj1L`F}v|5KZo<$N;fQCnRmqsyM65}y}{sfSay@$31OPV=|;gB zSjoyNw4Ziybq@XdV9wEBSvr5Nac?!%PV?z}qt4^;)CE%eA*5$xH&1rn43=D-jnKSp zo66t>ZM!XC`CSRJ)W6cx1+J= zZV`}(5>EaFLPZt=U?661^dD%4{%amX$zTKLe&~);8?8_uA~B}k1Z-YK%Yq_8l6?#K zY4!OC(G~hrftc%d`&TJ^%C6g8gT&Ax9Ld_@At5o*w|cAdBElkp6l8o|ZSf){Zri&z zbel``1ZG|lIa}VM*_R9x?=LX1(Q*hLBx`V5mHRuf2X zs?#8+8dC~dTt-|eD@zTy4}^__b%(;KAnVVVGE$2<3m?qi_Z|2oM&dEcZe|7Do0TD$DP;vJ!S}*)y$%qK0EPB8%&UPmdhY=mN}ZPgR0% zXamR*!`4JeMTOmZn1P5Jj)=+8hN(}_OH6KzRti0v1z8OZdXZr8()&YP54QnjkE;%D zKTh}~9R~EoQOXwv)cT)3hW4{WKMC1z$SZr3=iS-R(6Kf$bPH3qLLSJ* zgYdPNj}Cpw=ft~x?fF}2YwT+#7Z?Z(>-+}oK-B#Yk1LDB;O2twKGcYpRmvkT4^+Gi zhKF{R8vVZ4UwI3}6!%u6Yur9_&@8b{-zjyg_K@9bKmJd{C&_ZlOxZp)(nmy?*W~iK zhp>SMOTcs1p9l;ZyH7zQE{<1R;FY0iKBM!C{r!W$w81GqAAKn(mf_WMD)?mp)&9y_ z1|E))rRRkVwxMOcOGj#a3#Suu-i0LHM_kzV2`zsz*1n3D5pVp;lMK;`;gOk#1p}XA z9+6yw{*2yq8aJMe;}e{HYam0w5Zl}3BU8uHfpoq=y3xHHB?g>fYkvpDS8bTluccnE z4CFx)YSZSY`N8jp26vAUlE>G@rUaYLG=~O!IH8aUZ^D$a+{Q*}SDR*5rmeuauJfc_ zd|QyyV@jY)Jd$M9URAr2tI@~lhCMz3Ayg%9-*+58q5}FRzIM^B($?-}jet8BzsFkF za|&fgu~}TVok_bu)7b{R7mf!YxXXheG-{TWyWvFLA%0>ak>nobgIZo$5{tpK8h>(6 zVZmY;W`4TedXgUaGjJXPZ^gG^S_@iJh)1cC@U%e#YS0~((aSu%GUa+TFxX3En1V_t z)pt(}b#JWb-{PV!{)LSze?rfQ!7V2*Z)WqF+OAmoFG>R3HK{nr-r;^_ZmHnw6AN*^6nWM z@45M{mRxS$nUkv-b3EGQN2ixa9G)4SURWO=CE-fe0sSZ6WJLKIPH*@w{b0py7Nyyd z5?QaZq=7=~5r^98SL!_LPcU_V#!8My)0KN-Gu6`w@Fk2swxs|ZT(yfY0dlI}Xu!|1 z_Vh&Oy0Rz9g|wY?lp5WfqYrYLt~=Wq%Gq}zDbA&k0w;jh2D-<#CM>vAdm3;S`5^dM zt?S6p({fGAuZCGuhDsw}SkODZ>pZ*zg2Nmvc5sE=_VUefvbIas0Xjicvb5% zH#efAL`g~6W*a+f?gO^`gtEXF$|hasL#?1B&L5b&OrdrS0qCM~t=_h&{c0EP%d=MJ z2KOrU-k9Q^J|ZPu5qRH8&^|Ipael(*yu|XHU%m#%!A66hyq*zqmw~H& zxX-lsVmAEX0s&3|Fls&R8!uaF;OT}>?W!>A83F58PQ+c~lSK$fd8 zJdtBwU+2S)(u+~xTNnqpmOZ{c!oHAfY3JJPw9Zg=i~f--hz}P?=k6ifakBZ^F+$yX zx`J#@3$>RpsFqeHt-H=E`0hOpCiQ`{!yMW_-Opa>La>ezv zWp943IRNGF^J?BrXN`d;s!3dko)}Dob9t~7r6=?zw%_poHcfk!6f-K4jNpnr==8b@ zck^@kP?LLhNu%bYr#BN&KQT7eogYCJa3EY}yG3?7F4#iUyu7XZ#*dCrrVhM z-gQ#E(yjDI$~F(gJ*35e^XT0+9L?1}SL zJ^LF`CCRk2&sghS=d4B6_|d8w8gczeOBhmzPLowm1r4_4CYD3nxQQvLfvu!P{l`}` zSLYJRlBuD_Yfy1ph>qxozDDJceh2?+(5oXVLEGI9Oz=GlPE<-CE9Kia=O>CY2?qxU zW{tQ{ZaR;zb*o*ithv^kUBV=K;KI%?X}wNfnW}Tpr9Zxpph@c%HSa1DEH)zF7ZTcv zjsW+qyIHM8(ztFo#tF#~9Em$DuX3JlA83@%c<1XTq!yfAopCom%*YVtObN;^u2crP z4%_XJL^y5-60P_gI5GokX|UgcA<9jh1xTuQb_MQj1rikulIy=>F|m#E3|H7p>vJXV zK3)D9t29ptk8c1OoM}nHP60g2tJ)Tn?DlkfH`&N{M~UTRsll>Zp1{?XN7^um%CIX9 z^mEg%KvLa)v99#6e_CNO_ebL5;PfY{#LXUOeKV&qB}t<{IrlEt>y6~7BUDN;xoN)B z>poH2^lGEqQ>t|jHjONK4J|t>o6>zZ72}>8f5VYjrz3}~lhnsJFF+BD9)1^5_9>C0 zuc;ie+8L-}^wIKkW%ZteC2Ca7$7fWph&&38RJ-7`pb#m5z2>`?`Tdi8Z|z2{#H#=i z6SlL%s{~Fxna0D=vk+SEXFt*8v$L}miL2%-<^*ZEMzu)pPS@RyXreRE&F(+}lDC8+ zKR!@;?@w9S3FNyGID9TCsX{C+w6Z?I(3VpXNJGIT$nH2~OVOvK$w<`jf+~wvc?qsv z6JJ!6k$TY^NHj|)T1u-VvS+{&CQmoTZLOk*ZjJJ_sW~0P7bQ;^!0{|=?mFYJXYc-jMT<9 zo($ypbMB;+8a7gv+d+~6oWwGH8va@&z6#R=iAmJeTY5QfcRAc!dJn#7Oq`%AQSJvX zmiDMnUhk3dGpaQ3!sOP~wVP>4)ggU^w`3I+GcKZo;2QqZx?DNz3~?!~T!>6VJ!&S5 zOP5*IumVpGQDXoS=Pp<#Wp*|PLmSn`-bC8$-EzC&_>;c+JJkTL$`y^(SS{a^om>% zs4!4wl66Q-hMaj=g;b171rm?;V6%PB(ha^S4v-JGSt_Z4$Bd6_c@ZXZ$_!4-%`rB6 zooafZc4MM38?CA*w(sSzO!ku2Tp(49ze!weTeN09s~n(oSpMKUAaRlC7cY^tkOBmK zDO=XJX`U?Y#^eY*mY$K6l4_dMmsoa?MNQySH#Rm_4?rWv^#=10^#uj;JG1F_Hx?ty zo+WS^HO(4+s7{L#Pr2~9>6XS>W3T5{W4HPYbaj#8$Bs-krXqNdtaErUf{X`e>?OSO zHR|)1;E}%Rfz&J*{Ir+OWhR_{eD6(TKSkWo4uhEApYbnJoOJ(HA1DrnMv0E3 z@A14~;Y0}uAAx-7#Q69gfw*p|3>I!K_UcxQlrPPVIqig?EE=W42f{J|gie`hT8X9l zRcShfkL@eho|Q&W2`B(jAmV+KN%^gH7-k_C!I29~Z#!i-sNKrh#dYSs$Wr$v(2?(~ zR3_xv(Ec#euuO_WxHKaSheyA`c>^+0=%QQUS=FE9*p1cU4vMyv98909-`-P8y%wf$ z1vY55EAa-p!k436Qns1>&_ZieBy*})p;)& zG>v;VJ=BJL4Y3lq&O3d%_Hg68>DvfE_qk=y7*wuBp)M(a)UwJysM;=vfeHkdcnZOmJ zesJoV+IKgRdk}s-rOU~wIEa;)YnOopvhU@$w>E4U5LO9PL;@$-aF6J6HceQqto``U z(C`neNKzf!dELC(gZfb+>*B({{)o7)MFqqs+da)Mz+*D^<$IS~xT8|PYt~veeRKrI zp!cFx9CJ>$nQASIu=ls1u*7Y1Ks@mLSbX;_(3bL^uyhE8#{~XS>`og>crN4G+JBSs z#Ii-3VsH>dUPH*`HP{=?=`RoUBn^W0;YLmzRA_h1F47y7A*( zvM@MO$x`ABy{WgU`CAai|1dS{-nI!+?2{Fcv-o*)g1eYg#p?k2qJPe zk9OQ{kD6VoY(8jIMaxTK|4NJSb0hw^nREklL}2v&CFbeLWYQrDrD!fI>&O+I^M{~D zhWY37=T-YY6SjQ6N&odTmha)9M9lfQs8@euqd!`n{hBYjJ~6NWI5K%vr_8&qP`;_M zsz&KxrFVF0id26P>lZfTUwC2nU6|-T`19?-oDbciGvTIdtFQYyKZhq*9?#8*sXu=% zZi)x~L-qCZC73j_uKhSsUXY!Z%YM4T?8Jpd{9eP@-TmcZ{D5OCet!=4Z$xDq#4zQ) zChDP+u)~j{2WHs`I}k34dpC1y)YMcSKLkejwHEu3wiso?xijBTD_iUNpV7^~IXMPm z9_fga;W#A2Ga(goq~NlnTFRBKo}TS$*v&8fEPpI$+`BMka!;ahud?|#3NVv@?N9aB zf0>1!9~k@NC5a0FTCHK7{e27_?(cH)c|Zt$cIK6roBQHJ*oSXORM_B}lEu+u!Ji{u zzydn|n%USWOpbREw|e%!zyEJ<aSf`-eKFGb^5=1@qhmo`d1iO zhD_nHn7?33|I?DSn2-PpnPh>rhpsM6S4^M&qR@Z; zc8L{Y06~uN=a{>%oc@Xe!GYo{m)iNHaSqA zI3#%e+CREGJ()js1&Kd-Syvg+KYRDaST`FoDgOUe|6jb#qnpaAs(0LP2U6Al!*9gK z#YrzSb{K)kGKkxnLkcq3_(!6HcY@GauyHe6v9b}k9_m7wU1MI#BjQIR_@+eaO ziTMI1l0DkgQ2aKpA&mMTEum}vme?#VpJS-0{B9#4Hb2eyKbQ;-$LpZc_Zi;gVGXw( zPJi=_Xg+`H5ef|hO|}l^j-|nV(zF8l1*}81jk3Kr6zg=+kpo;qr!UBCqgKrG)OB?k zJUmX9Kv6at#10rFByqgL!VCZn2T%}+`^Rie`rSp%^7{`HKS*##0h&Z(_Zp1jOCe!) zMuQSw&n=CZ&^^8$EW0Dld6b=)7;NrpWVX4>*MwpJ=jk*!V>T1_xox|A=sP= zc*4F2T{xM~AF{r2I@@L@KAEeFh*Gr*eKID#{Xg zk;C%9is5uEUE>{TGE*k%YlCQ9aTe1c7EUs{AQTTKFygiu!DrqC=+}A0wx103^!DZs z%beAPlbzk@KB?`td?Wy_&laRDX0uHnl=kx~IpEb4wjw!#s`-l_l<5svI*UnD=|fXeUQ?|U z2n)aKf_ih6!BWH`z! z_`eBQ0bcgO_5P(2S&)I{0yeh)DHbrH$9VnTS+=Yj{^Ts}4P^b!Qm_v14E(JZk~0cf z6pk_=qxE&HZo-DH(&DP!l#PH)yy=BG;Cnw-&^D`hKS#b(E$=vvj&qtkiTpCf{`xhB z|7}kug4%mL%?^iAaWBJgUgJlFPbOXtvkN>AJcF>k@$~dkxhA*P&`MpFo_O)!r$f?s z57&|vN(Kar0g_A@(^}inj2gDAu|6hqEQ6xO#ekwFi=H_2Z8a%DgTtTyFBcL4%We;Q zM*Lnza^?e-BU3CQu1w`Sw&A)1gG=vg0_7%xcd(nyGFQ*DuT#&h=9z*?NlB;s$g#oU z*$FdlHqr9DnL;eTF>f5S*L}KP^d@=Ke*8HmNJ(uqNw;{J-SWZdHslP(i|wy~rLXtq z&(VDMxgjUdY9fY(=H$ITp`|~g*twEgzd0-InhSm6+iXO+xn%?EIQjPO@Q{E>biK_> zg%UP2GQ;i3MQ{jnvZnc~zT%gzSCRp1l}cFbcIVnXJb8K=Tl2QVnCHOn1}>Rt;Gqf} zlPm{V!*g1Eqr+H+SPWbv{W-~8n_CG}cb`%YSzt1#6JvjAPg<`#{FKt9xRvRW^$$$? z&p!c(l`mnEv{^t&b*r$-X;yofb;$v?-^}jc zZlC@-2E}dn=NcNM)P_uBQse_LY@^y&@|CLp&;=UX0m{)lugsXGYz8*&hZPiDXJZ5aEubf~Spe59+c_NjS!HV;g%|M_ zroXYXe}}RP$!}TecMG;{Ua7t?G-LsU8&nRD_(bo3!|KRSy~RCt{n6$l-&KG`JFl*z zbN_4_Y997U$V+bC>ncfES=n7M*-dv#Jvzf;E8k^%XD8XsOY4tB`~RBL7N8>SWA`Dz z@qd*um#x#Jc6#tT>}ua(R%_McKvq(*CK3+Gl@ zShgB0w>8p(Miro3kZ)N0KUP^m%4eTt2dC%|H}cIj*?1 zD(Er_-P~pV?Lwi~!*;c6mKU&}eE=x2)L*=~>qsGd*UMTjm0Dsi^i#dVeY3eI8K+Ac zH}cBLh77^>_NC|BYgbJ$ZlNuRy^BW9D3{KiVpO!p+p`}Q88_BIZ%o>uRqE!vht?F7 z0!J5o6i#Xl=~E z02dN+plUsL-cgtB!n|u@$o{)kA&G-0>a&XrlwhP;YVA45(j@?0;FZ$_q;5jzlu8>Y z?JBVAiZe5TVCmL&RYVe@fYrM5J*eb7eS9hbU1Gii6(5mb`-~IyTwshn9$bN z7VR>PtSNT2>f!|xcd__i9i1Y|edfxeGw~X^QHMlIR|^VVd3S%T_4peU$zbxEu%Y-g zd!2{|385#KN*NuFkB^h3jfHc~BMD;5YArWcAB&5>3gpff>f8@rxu0sCAGXLd6axGh z8u!Dpg<}wk0Fq?-1XKFEGFA%Qo*_s%4SfS=buzZ&H+%7|PhX(_^ z5t8FKMN!6ad>mPE!ON=*%X>bB=}xl^^UgC~cKb4pl5zDE|N zIhNOnEm_ZvzhoYiXy9Uc7z~EeuQQmmy%!j*ylvI({`}=Vg$cgR(9H19-@!SWm%>5`vC2xUuM65%2@bm%Qal1_$!|#kKP$SI#kliL9PrK}ST+lfzCTD*ji?KBm z&tPJH=zg=uhIyU$%9oiJdfv*&vRs4 zMh2@V*HhP9Kav-Pdql63HTw9p;&t~P^nO?&Xg#%L0m6A^342fXV9Vn%+vPcl>4UP1 zw%adt4IhY%tK4XhNMgEHN@JanINwZ-4}}#qIuyxYo*q@~FDC1;#7HfiIrT>~MQy)7 z_XQMOdEOpZo@`v7?6$BBm`XYGn3rYeMSdD=D$v-|LRM-^yUuoSpqdXt#^8`ZAA& zhe=RSTTYp#-pljH_lVT}FRA6*kfERKraVS+mfMXOZ6Uw^r(LM4+C zK9QY#Jou@eIBD;kqiZm+BvtSO_%SRy*(ya$>Up|Ka^DmGzA%Qx@Ts~D&hZo{>>k-o0h13u0j!7HTuWAZ>37;-lMhey7#`7L2BR-LRY zcie?4h{h&sdCgt{%AYQ8CJ-c&l3!lx?}_-&t8!V%3iaA-q7+~6^W0PdG$ff@{QRW$ zE;;$sQ4V8HDn^M9WtAxGObn^ch0ULVXH*l}ejl2RzCz9jxaZNoIUzphH^R z2ZMd>9vBunqQFV8M#Hp?{QMk51hX>gs!8>f>kGdWf4xp_*Jxco?WgW;-omN8qH`Qi*b@ryuqMLzqTnqZ!ypJS7x+pD~IXK^ikd zLn+wU?zkscfg8$y>RKJYX%wi1^vUxsZ?11rcZ=KnqU*rlu~4p_WQ9!auobQ9Sn*xA zhe*HOtcKI$`eT>%buZTgfX}CAS~qEPrbQhd_mt>y4v)>zh~~N)!|R>o9p*(Ah2w0i zKU52(ZnV|=)^*E#we2OVSJ=AfY6_7K;BtPCThhFP`~WU@-@O#eZY6_=UQXr!vaFPq zCnzdvYUk9+%!suN50=M+GT>VuQ*B7OZYo%RwpCTq^Q!lmpqrl3F%lHBk#^_^4}CHs zP}R7uu|bKI>rZK#-gi!ERyK`q4$=;F^K+Yp?9d8YZD|3bzQyRR^xnA&&er<2~YKYy;8A+|==U18+@6lmvXy=DPU zxoKy{GOZ%uuYI-a`E~hx!N1dNE1}<&1R-fuZByv&`cx)_Zx8(avCC@s`C^1%V2I~_ zD5ii?Fc+-&I@Ne-93a{5_6H;!$sp&`cim9Pl*rE#DU1HSTR?@FxK$vRbJM*3h%V@E zM~70suu`|AzwkkHRW%;xw2@nV)tIWa)`$G>i^gbI9nI|?58rS={9jDw*2lxI>#Pu^ zLN##%(_4SHL%-bf|2 zU;pV3ZPBmfaDo{Ip&&k97Rnb{d6f@%MpYljloTZn=;|A)(-$Qq^u&)h>1@4>zUNO3 zdLMnN#mahe|B+_+`P{WemX%^!2^@&Quh(Ecr;nki>B)K~kd z(o;mPV4$p?o)=JuV7Gf*ACe0PRlXygC)05ifBrb#?=1n?-yi ztJw)HX6G~ydY0K{)&LZ`Y^-nKnl-@8#TrLwx+slvTk+iRvfR8wI)IS}=8}gP_1K^d^6&t-~y^*BE3CvKgyU zRShTg+D}|LA9BXWKTJ?^ajCKdN30O^N{@%6Qt(_S(uNqMR=w_Wma!hc#W~HIGld7> zih(l*^>4%;BUk{1-->p#$B>!mO&dWIVOW*wJO_nvWfz2}J>61FU(SEOeB(aNY?;R( z{&kvw_8D+vtC()P7D&H;n3=z|hpVLUk#r9huL%~pCyVpTn1>VKn!M*E&ROk#KonZ7 zS6{t8HKqzEuTbu`G&I@mpMk=nFi#Sum2w;R5Q-t=EC5ACLX<7)T1o~sGN~Fr26+2@ z)BaIzuuj;XP9I)J2pi|8C(R3rT_ZBnwtA_~V&q|2)!^`sL&7P!qeef3axZx7n_K03 zRC>}qL4R^_Cs23H-rhH}+0VtFKao1WIi3{~0;*T^TWRdx-!1|DHrwvPxsFI0S0=Tq zy>0)j&HJd?exfnEoz9Yif;PrYIpG#3%4Uv%YNAxIl}uCJ7=w||REAHs0`J{J5FN~* ztf3XhxYkx=Z5=q^@P4ChZ$h-NE$FCGZx4-Wg+^A>Y-w;mGY#-;ke&I_cwqJbr~)sO z4(S^k_%52P+0Z#=)&FGigM^?qz1~3ch$6b85Iz zXRxue@g}Z;o7kTUfyv7!$`Zta8I%y))&%)-7+g zIZw*1Y-OInW!Sq&UR59jXSMFAp_9=#gfevPS{Xt_m{K7qR)pX8xs5#ToOSPN|NZ9o zGktV-_W<&r;%AI2J=jD28}x}jIvHbrV7ceRt)Ypz4>%k){61V~(?G&D=Y1vZh2TN< zsyus#<4~K4*G~qs4)$K(embjhI&VYnxt~72vByrSBq1Sj+&V@i*FS2$hIJN_9|-%>8)jy8>KvR=$7PSqH)r{3Dg3Os-p?JPvf?UF zzn*PxsqE`isiQkx5VEqD`t7FLj#cb1@Ir0-JQ%dz-AYP*2NeILi>R!x;-=z-PiUHz z8z$lKlD@l*Y)vdpiX0y@^#O{Z^pm;teP+20koQdOXpp~6dh&0r@8WC5$5(q!Cl^m~ z82TNQ2o=pdGqMlacA#>0aZ8m9douNkm!Q@dkEU%oy!)~5qDjSUgG;X+^lC9A_qD}> zs2%(z99bDmNdDYFlUW-wK$|TSa{G-xQwoJ-#ZC8KJtP3NC%ZMOTCWK(A7D8r-KGg( zoHhYf>EkvhO4#p2uAUcW**UA(k}UG7e5=3e)2&K!H8c$t99ncatooTWx|PE5LqR;m zDK4g~kCFye{Ug)w7ZyIKj-ObRrk!fi_w`s`Y9Oz0^0DnYo1w{)PH@+r=bPfZ_}K5l;#n9aR!X*X;Y@2CA!AMqI`6|W%Y<7K6J@UbuSvc+Xbu=P+&#WSJCBxB z2yp}A7MqtfT)Xs$QCi>xhIkE&f#7W;n*h)qO^8ABtMn-vyzx3K%^YfpQ^l@D7t3Iv z35G@R>#mw!`?=RKQIQ$MKJUlPAHLMmd&nwr!C@wmHf2%n1{k%nUN5xgN`xRhy@~*D z_~8P^>q@fag?2G^${e$o0fAynzo6(%pVHGv4j^6>E)4J`bgpo(6y4nPtFgnj@1 zJ#b_`^j{gmgQuY3k9ThfFC3-d-NHcVObqgWi-hEu)wIm8mMUrZ1X;!TOTO<{;V-}N zEbw-$W84m+X>beT<4Cs4p%Dw*pYE&(9BSLKAv5HW4)b={%-^0VlSGsFQjYZCIpgF|MEpnV;bhCZ6Mc8ZZ&RC$vq+rnMOko$x%rRx zM<7==sgaMS=zhmoD+Ij-=hiwG>Im4qv{x%=cc=C~e@g+$dd&1dO2p5_!)NaB83E>M zC|XTbw={i8;|!5w2*Q2N&Q z7poDj6-IOA=EsNDT7scCDf)a9$ex7$RI`Ha`Ni_IjCzOGk|i-|tk++PY!gVe)0!?X zJwnP0i?<1J3@w`t5yN?}Fu4!i30UIfjEwxccKM4^^DzaTQ+=J9e){!6Q9FPe;n~gp z`jx)cvk!}Q?o&)Fb8T#BF}Y>a+`|Iyn%-j{UUh%&2)*q=aEXyrB?<#e0m|CsI=z`^p;Lt@$60!JpO`(csUlgr*>Z4Ej8*) zCd~cD#oW3eu8q2Crj;ijX?!GR{)Shue12!SqKEmaP?i#Bt_$Ndn8HnKaL1E^o+^;>&C&rHTEq!I}{bNb;bv&zt>r7d!Vm3Nk9M z-|>wrl&pABtXKaXjQjcXgG3xBpp=Pd?LeadQM`i!I$e~bH;#USazHnr1d8%kErfOI zro0A#eMiiSpLTDD-`lBeD3H%|wW6mA74fRwCbdgzf;Y{vyRay95~wWo8!pbN7F4MK zEx!#ty}V>4u9mV**!XQHNfD15Z?iB4>(L=EcOLH;RM?Ml778Ca*OP{DseM(w2AvZEMleDyL2yVjI>hNc`zZl zX&RZ}7fV`0l0=*xK_2_F($+t&(1?PV?EwKd64*P!!a=v&pEXNQgNpshu~*wwWEMJ% ze2`xE6VYlC=dZfFIY7FCoVwb-H%PV^7cA=w+T0D6yt<-fvB^(FV)#W``m$+;g+BtA z2S(bPM3rkm2wTsgb7!4+;$pl+hon5!_2m}HD8y#^>wU<(E%MaVmCC|E@MjpI3qcdC zm=XlCUgx0tYd)rf%CvdcJQILgqqCEWu$p8S~MY^{IUq!Vcl2RV(|mfhzkmD6US|XUF@1)@E>( zX^-RSn3>^$krSCAHE=Penl`grFjMGKnrkn(NljH1i@b4f zd}e=K1UbEpwRk+XsBmeydAhMy5P)9)MhExo9QC`rAfypu?-W0OP@91|NkIh0()vTE z^8~uSr!|(=45|#EA=0kyN}A?DZsV#%L{XE*_yns-I((A~nEYG6~xH=kd=nEY*tVHZ$gl-UD)HrhUVD9P)}afCl#2*{Ov)O-|>8| zRrMc|7}R-kCJC89Zk4{)X9K7b@cUqfjYsYLmOR0PD_MD;KR*MC@VUqG;TA2TFwe^+ zW-Gzg+o2DX`iSp)?(FY7ZU|Cf@PS(6ttWOoEo+Q|b}Y&nX02@FS9`U0phm)ipCKGI z1pv#*doE~Gln5*wLs7zDFmO8u*12}p);jU8B_X@ z)Hc+m*>bS`c#0Y3h*s4njdb%vb~gga6-$a9XH8>KvY`w{+6ANI`iou$=u(htq0mxB zS|yVUd0}Ddw92hCs4$5RnBl&B=XYB(a}NHK0=i3{ggG!G=Q_DAIj63my=S2gRmFb- zd;jJ;09XG0nej5TY6)}^gME6cuC7i!tAv?-cD77)pWdB^1t`H?0h*v?y!noM1LuaV zFnfQ)b6s^NA)$@l)~vN^7ao_PyE?jpzqNpW@WIhH{rC5I3n5?daiK69@(BoB`P-rp z4#v&g|JT@=hb5VBaXj2w$TmYlGaU=eWi-*yY)lk1u`o^VZ8A|aLMmxdGP^ey#xb2- zQmNcBH*zVL(iC%dkkWDko6roG^ro0*<&uiX{A%W2pSe$$D}V634?lR`=RF*L=Wx#F zd|!grIG9`&j*q9IeXk;#r{`D&kIcVsfUD6nhhJJyNz3S~HtmBbQ#x8^W{|w=kPQ=y z@z-KvVhEEr@AHM7Dk4K)pzej(*`K)Otm!Yc>uLr>f%X7;Pe>eQLpIa|$?K}WK4LOZp+92w$oobFB|M*>XZG42Q8dI;&AcPeZe_EIKdsFVjQq$!G&(s79;|cJ^~LeMPJpSoHD7rWGn-Xi&=`HfG-*nmShTvlW5Cw5 zGhB&8)u?!U*$R;(t5o$R0n^$j$DVkEdb4Y*JVXI)kM$oVH#%)^!@G!HSO2zYYXCmX zXbtp}aP;T8cMqqxo(#ucGzG`&$|54lD922`~oHXr8$qzEF`}c zx|R~DEAWTT6}NJo-CcLWQ06ufS_D_#36b%1*ZgqoodfNJoGAQi|F0ts-%b@(*`exu z_lP~YUvP*s`bFdTl0yYOpfTJc)X!k2}p7 z@j4dOk*XSQtQz5MpYyxyumLiwC=@8Dr}Zof7!KFGPvfcQ3V?b%qJCL38E!JMwzfrHMxzrJCU7Oi5raCO0PIku?G@?nc_a8} zhc2UH8~bBY4}-2b*!Kz`DoE~IWH`k>?t(bt8YO>=ne%0}NMqxT6>8~5`%OD+A~x`X zJ}m?TCAjSl7t=uvRU{I{ScGnODa`v*KjxdeIXM;!^xj-S>$N{B>sPtX$CkJE+{{KH zbTVj-Cqo3~%rSnC;M`Y17FJ1#O=V>zGsqz}oh$ds?olC?2iDQAdD@pR#^l7z->ulu z_d|hI!kCNSf`MuWCW~epRh~xQObm#x__#NLuBhh3vxMz%_Ac=NDlhiV&hg5u4@xe0 zzjh%6&fbD%(cX8pD^Js_g9>CJAm5}N5#`5-UOp!Ly8m`nUv}%Q0uQ~EQZsD1QdT4`$`F9ZvAc89AjrqT6 z;{=YUIRufU^H^d^O3GMs;6R~y=Qkbu&p3-kL8g~0zP96xyMA&s)pT`w`RUZ_wJ(o2k-{!Z>xZOEt=F(09&H*_yqW@k<{EHr>M+&y4N zZ;B^OrYB_r@Jc~(YBf#LzShVlCn{r&%Bn4p_vxl#RjbHd5@vl=)fJC#2|FxBxHlU! zTr9U)&e*PCaKWYd8;8ty`;RkGc?p?zCBu_hvQoySa1v(?<|)KV3^-zn;RKWMYhMbb zP1_(E4$<{dV{_Gc%R}wSaoE_dXgCw$io7Q?oGoQ!*cWm@ z4cXE0Mb$(3Wqt%8(+WzN%U-vC99&}_x8xSgQ z-c3v-R#*Q};$bk4f-G4}))8Ji!x}y}HAnZAUX9?I?b~NaN=w7M3#Tlfm;V_KCXm!Td4eAOJ#Vd;l3#U%q%=OSm%{fN>b^N} z>E!aZ|o+|@Qu~m)ELtXW2)al33r08S?RbWE2sVP~0mmjels^V*`5tyky z8fz&e@%se&$h&vJe*ir$5_#C^$J6U`_9l%$tJ0LbyEY?cB9hB(_Zu0fCM5>3dq!T2 z&4~)fM)Nf(huHr8{hj*4hvrs`IU4-M7C1M!?6sL=o!%Ac?W;kTKCji!W*hDiZ<|6i!4lHsc5#r*S#F3f?;RU(Qvu(Bid~aqlY-yH~uO3}{0leW% z3IlVietp`i&4OFA63&EXu=ImCqR;A%n6=0&3YSU;Ca#O>3aX`bPy0Nda3#ve>+~C> zyfS}==7OZTFMS3x8hntgEC`qtGwZD8{__+jb!_PCV|dAr>(o%4z)F<9^ yQX}GFV>H@;gX8c8yyLmQ;Y>WKSHi_ULa8Q_2*u_o96h)KJP5Xq_#zvxpMD3GT#$YM literal 0 HcmV?d00001 diff --git a/dev/intellij-setup.md b/dev/intellij-setup.md index 1cedeb0d156d..977f92fceb6c 100644 --- a/dev/intellij-setup.md +++ b/dev/intellij-setup.md @@ -34,6 +34,16 @@ an alias name. You can do this in Using `File` -> `Project Structure...` -> `Pla ## Code Style The Code Style is available in XML format at [druid_intellij_formatting.xml](druid_intellij_formatting.xml) and can be [imported into IntelliJ](https://www.jetbrains.com/help/idea/2017.1/copying-code-style-settings.html). +## Set Code Coverage Runner +Druid CI checks are configured to enforce code coverage using JaCoCo. The checks will prevent PR from being merged +if test coverage of new added code is below the set threshold. You should run the tests locally to make sure that +your code pass the coverage threshold. In IntelliJ, edit the template of JUnit tests in "Run Configurations..." +to use Jacoco for code coverage (this provides branch coverage instead of the default which is only line coverage). +You can then right click src/test/java folder of the modules you are modifying and click run with coverage. This +will generate a report to show the current code coverage on the code (not just your change). +![Code Coverage Runner Setup 1](intellij-images/code_coverage_1.png) +![Code Coverage Runner Setup 2](intellij-images/code_coverage_2.png) + ## Git Checkstyle Verification Hook (Optional) Git Checkstyle pre-commit hook can be installed to automatically run checkstyle verification before committing, saving cycle from avoiding the checkstyle failing later in Travis/CI environment. From 3515839920cf3692d34f44950c549ffa40ac738d Mon Sep 17 00:00:00 2001 From: Stefan Birkner Date: Thu, 11 Jun 2020 23:15:02 +0200 Subject: [PATCH 076/107] Remove duplicate parameters from test (#10022) Commit 771870ae2d312d643e6d98f3d0af8a9618af9681 removed constructor arguments from the rules. Therefore multiple parameters of the test are now the same and can be removed. --- .../rules/BroadcastDistributionRuleSerdeTest.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleSerdeTest.java b/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleSerdeTest.java index 0dfe0eab0ac1..f2405060fea8 100644 --- a/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleSerdeTest.java +++ b/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleSerdeTest.java @@ -44,13 +44,7 @@ public static List constructorFeeder() { return Lists.newArrayList( new Object[]{new ForeverBroadcastDistributionRule()}, - new Object[]{new ForeverBroadcastDistributionRule()}, - new Object[]{new ForeverBroadcastDistributionRule()}, - new Object[]{new IntervalBroadcastDistributionRule(Intervals.of("0/1000"))}, - new Object[]{new IntervalBroadcastDistributionRule(Intervals.of("0/1000"))}, new Object[]{new IntervalBroadcastDistributionRule(Intervals.of("0/1000"))}, - new Object[]{new PeriodBroadcastDistributionRule(new Period(1000), null)}, - new Object[]{new PeriodBroadcastDistributionRule(new Period(1000), null)}, new Object[]{new PeriodBroadcastDistributionRule(new Period(1000), null)} ); } From 3602687b08fc01b50ef1891076eb3f7f6048736d Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn <52679095+maytasm@users.noreply.github.com> Date: Thu, 11 Jun 2020 14:08:03 -1000 Subject: [PATCH 077/107] Remove colocated datasources from web console for broadcast indexed tables (#10018) --- .../__snapshots__/rule-editor.spec.tsx.snap | 180 ++++++++++++++++++ .../rule-editor/rule-editor.spec.tsx | 18 ++ .../components/rule-editor/rule-editor.tsx | 10 - web-console/src/utils/load-rule.ts | 11 -- 4 files changed, 198 insertions(+), 21 deletions(-) diff --git a/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap b/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap index 43d59de16916..869d8d020238 100644 --- a/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap +++ b/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap @@ -214,6 +214,186 @@ exports[`rule editor matches snapshot no tier in rule 1`] = ` `; +exports[`rule editor matches snapshot with broadcast rule 1`] = ` +
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + double-caret-vertical + + + + +
+
+ +
+
+
+
+
+
+
+
+`; + exports[`rule editor matches snapshot with existing tier and non existing tier in rule 1`] = `
{ const { container } = render(ruleEditor); expect(container.firstChild).toMatchSnapshot(); }); + + it('matches snapshot with broadcast rule', () => { + const ruleEditor = ( + {}} + onDelete={() => {}} + moveUp={null} + moveDown={null} + /> + ); + const { container } = render(ruleEditor); + expect(container.firstChild).toMatchSnapshot(); + }); }); diff --git a/web-console/src/components/rule-editor/rule-editor.tsx b/web-console/src/components/rule-editor/rule-editor.tsx index cf861c099598..09fca2fe8c5b 100644 --- a/web-console/src/components/rule-editor/rule-editor.tsx +++ b/web-console/src/components/rule-editor/rule-editor.tsx @@ -26,7 +26,6 @@ import { InputGroup, NumericInput, Switch, - TagInput, } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import React, { useState } from 'react'; @@ -209,15 +208,6 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps) {renderTierAdder()} )} - {RuleUtil.hasColocatedDataSources(rule) && ( - - onChange(RuleUtil.changeColocatedDataSources(rule, v))} - fill - /> - - )}
diff --git a/web-console/src/utils/load-rule.ts b/web-console/src/utils/load-rule.ts index ecd90306397e..6cae0a306cb4 100644 --- a/web-console/src/utils/load-rule.ts +++ b/web-console/src/utils/load-rule.ts @@ -36,7 +36,6 @@ export interface Rule { period?: string; includeFuture?: boolean; tieredReplicants?: Record; - colocatedDataSources?: string[]; } export class RuleUtil { @@ -83,8 +82,6 @@ export class RuleUtil { delete newRule.tieredReplicants; } - if (!RuleUtil.hasColocatedDataSources(newRule)) delete newRule.colocatedDataSources; - return newRule; } @@ -124,12 +121,4 @@ export class RuleUtil { const newTieredReplicants = deepSet(rule.tieredReplicants || {}, tier, replication); return deepSet(rule, 'tieredReplicants', newTieredReplicants); } - - static hasColocatedDataSources(rule: Rule): boolean { - return rule.type.startsWith('broadcast'); - } - - static changeColocatedDataSources(rule: Rule, colocatedDataSources: string[]): Rule { - return deepSet(rule, 'colocatedDataSources', colocatedDataSources); - } } From 1537b62227802cc24c56fc585d28ab7eb50cbe66 Mon Sep 17 00:00:00 2001 From: Chi Cao Minh Date: Thu, 11 Jun 2020 17:30:13 -0700 Subject: [PATCH 078/107] Fix CVE-2020-13602 (#10024) Upgrade postgres jdbc driver to latest version to address CVE, which was fixed in 42.2.13. --- licenses.yaml | 6 +++--- pom.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/licenses.yaml b/licenses.yaml index 0dd9acd048da..24d93ee0d1c3 100644 --- a/licenses.yaml +++ b/licenses.yaml @@ -3625,7 +3625,7 @@ name: PostgreSQL JDBC Driver license_category: binary module: extensions/druid-lookups-cached-single license_name: BSD-2-Clause License -version: 42.2.8 +version: 42.2.14 copyright: PostgreSQL Global Development Group license_file_path: licenses/bin/postgresql.BSD2 libraries: @@ -3637,7 +3637,7 @@ name: PostgreSQL JDBC Driver license_category: binary module: extensions/druid-lookups-cached-global license_name: BSD-2-Clause License -version: 42.2.8 +version: 42.2.14 copyright: PostgreSQL Global Development Group license_file_path: licenses/bin/postgresql.BSD2 libraries: @@ -3649,7 +3649,7 @@ name: PostgreSQL JDBC Driver license_category: binary module: extensions/postgresql-metadata-storage license_name: BSD-2-Clause License -version: 42.2.8 +version: 42.2.14 copyright: PostgreSQL Global Development Group license_file_path: licenses/bin/postgresql.BSD2 libraries: diff --git a/pom.xml b/pom.xml index 1b04a8ac461f..c6e191a06a70 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ 4.1.48.Final v10.14.2 6.5.0 - 42.2.8 + 42.2.14 3.11.0 1.7.12 From 519cbb0dbd1985c8ec562a8a641baea200aa9590 Mon Sep 17 00:00:00 2001 From: Jonathan Wei Date: Fri, 12 Jun 2020 02:33:28 -0700 Subject: [PATCH 079/107] Fix broadcast rule drop and docs (#10019) * Fix broadcast rule drop and docs * Remove racy test check * Don't drop non-broadcast segments on tasks, add overshadowing handling * Don't use realtimes for overshadowing * Fix dropping for ingestion services --- docs/operations/rule-configuration.md | 18 +- .../MarkAsUnusedOvershadowedSegments.java | 35 +- .../server/coordinator/duty/RunRules.java | 21 +- .../duty/UnloadUnusedSegments.java | 110 +++++- .../MarkAsUnusedOvershadowedSegmentsTest.java | 16 +- .../duty/UnloadUnusedSegmentsTest.java | 364 ++++++++++++++++++ 6 files changed, 517 insertions(+), 47 deletions(-) create mode 100644 server/src/test/java/org/apache/druid/server/coordinator/duty/UnloadUnusedSegmentsTest.java diff --git a/docs/operations/rule-configuration.md b/docs/operations/rule-configuration.md index 4228994a30d3..1ea074f72394 100644 --- a/docs/operations/rule-configuration.md +++ b/docs/operations/rule-configuration.md @@ -170,8 +170,9 @@ The interval of a segment will be compared against the specified period. The per ## Broadcast Rules -Broadcast rules indicate how segments of different datasources should be co-located in Historical processes. -Once a broadcast rule is configured for a datasource, all segments of the datasource are broadcasted to the servers holding _any segments_ of the co-located datasources. +Broadcast rules indicate that segments of a data source should be loaded by all servers of a cluster of the following types: historicals, brokers, tasks, and indexers. + +Note that the broadcast segments are only directly queryable through the historicals, but they are currently loaded on other server types to support join queries. ### Forever Broadcast Rule @@ -179,13 +180,13 @@ Forever broadcast rules are of the form: ```json { - "type" : "broadcastForever", - "colocatedDataSources" : [ "target_source1", "target_source2" ] + "type" : "broadcastForever" } ``` * `type` - this should always be "broadcastForever" -* `colocatedDataSources` - A JSON List containing datasource names to be co-located. `null` and empty list means broadcasting to every process in the cluster. + +This rule applies to all segments of a datasource, covering all intervals. ### Interval Broadcast Rule @@ -194,13 +195,11 @@ Interval broadcast rules are of the form: ```json { "type" : "broadcastByInterval", - "colocatedDataSources" : [ "target_source1", "target_source2" ], "interval" : "2012-01-01/2013-01-01" } ``` * `type` - this should always be "broadcastByInterval" -* `colocatedDataSources` - A JSON List containing datasource names to be co-located. `null` and empty list means broadcasting to every process in the cluster. * `interval` - A JSON Object representing ISO-8601 Periods. Only the segments of the interval will be broadcasted. ### Period Broadcast Rule @@ -210,22 +209,17 @@ Period broadcast rules are of the form: ```json { "type" : "broadcastByPeriod", - "colocatedDataSources" : [ "target_source1", "target_source2" ], "period" : "P1M", "includeFuture" : true } ``` * `type` - this should always be "broadcastByPeriod" -* `colocatedDataSources` - A JSON List containing datasource names to be co-located. `null` and empty list means broadcasting to every process in the cluster. * `period` - A JSON Object representing ISO-8601 Periods * `includeFuture` - A JSON Boolean indicating whether the load period should include the future. This property is optional, Default is true. The interval of a segment will be compared against the specified period. The period is from some time in the past to the future or to the current time, which depends on `includeFuture` is true or false. The rule matches if the period *overlaps* the interval. -> broadcast rules don't guarantee that segments of the datasources are always co-located because segments for the colocated datasources are not loaded together atomically. -> If you want to always co-locate the segments of some datasources together, it is recommended to leave colocatedDataSources empty. - ## Permanently deleting data Druid can fully drop data from the cluster, wipe the metadata store entry, and remove the data from deep storage for any diff --git a/server/src/main/java/org/apache/druid/server/coordinator/duty/MarkAsUnusedOvershadowedSegments.java b/server/src/main/java/org/apache/druid/server/coordinator/duty/MarkAsUnusedOvershadowedSegments.java index febf9a4deafd..e278a7582302 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/duty/MarkAsUnusedOvershadowedSegments.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/duty/MarkAsUnusedOvershadowedSegments.java @@ -58,19 +58,17 @@ public DruidCoordinatorRuntimeParams run(DruidCoordinatorRuntimeParams params) for (SortedSet serverHolders : cluster.getSortedHistoricalsByTier()) { for (ServerHolder serverHolder : serverHolders) { - ImmutableDruidServer server = serverHolder.getServer(); - - for (ImmutableDruidDataSource dataSource : server.getDataSources()) { - VersionedIntervalTimeline timeline = timelines - .computeIfAbsent( - dataSource.getName(), - dsName -> new VersionedIntervalTimeline<>(Comparator.naturalOrder()) - ); - VersionedIntervalTimeline.addSegments(timeline, dataSource.getSegments().iterator()); - } + addSegmentsFromServer(serverHolder, timelines); } } + for (ServerHolder serverHolder : cluster.getBrokers()) { + addSegmentsFromServer(serverHolder, timelines); + } + + // Note that we do not include segments from ingestion services such as tasks or indexers, + // to prevent unpublished segments from prematurely overshadowing segments. + // Mark all segments as unused in db that are overshadowed by served segments for (DataSegment dataSegment : params.getUsedSegments()) { VersionedIntervalTimeline timeline = timelines.get(dataSegment.getDataSource()); @@ -83,4 +81,21 @@ public DruidCoordinatorRuntimeParams run(DruidCoordinatorRuntimeParams params) return params.buildFromExisting().withCoordinatorStats(stats).build(); } + + private void addSegmentsFromServer( + ServerHolder serverHolder, + Map> timelines + ) + { + ImmutableDruidServer server = serverHolder.getServer(); + + for (ImmutableDruidDataSource dataSource : server.getDataSources()) { + VersionedIntervalTimeline timeline = timelines + .computeIfAbsent( + dataSource.getName(), + dsName -> new VersionedIntervalTimeline<>(Comparator.naturalOrder()) + ); + VersionedIntervalTimeline.addSegments(timeline, dataSource.getSegments().iterator()); + } + } } diff --git a/server/src/main/java/org/apache/druid/server/coordinator/duty/RunRules.java b/server/src/main/java/org/apache/druid/server/coordinator/duty/RunRules.java index 3dc7b4d2f918..d8c207148ffe 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/duty/RunRules.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/duty/RunRules.java @@ -20,6 +20,7 @@ package org.apache.druid.server.coordinator.duty; import com.google.common.collect.Lists; +import org.apache.druid.client.ImmutableDruidDataSource; import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.emitter.EmittingLogger; import org.apache.druid.metadata.MetadataRuleManager; @@ -103,7 +104,21 @@ public DruidCoordinatorRuntimeParams run(DruidCoordinatorRuntimeParams params) final List segmentsWithMissingRules = Lists.newArrayListWithCapacity(MAX_MISSING_RULES); int missingRules = 0; + final Set broadcastDatasources = new HashSet<>(); + for (ImmutableDruidDataSource dataSource : params.getDataSourcesSnapshot().getDataSourcesMap().values()) { + List rules = databaseRuleManager.getRulesWithDefault(dataSource.getName()); + for (Rule rule : rules) { + // A datasource is considered a broadcast datasource if it has any broadcast rules. + // The set of broadcast datasources is used by BalanceSegments, so it's important that RunRules + // executes before BalanceSegments. + if (rule instanceof BroadcastDistributionRule) { + broadcastDatasources.add(dataSource.getName()); + break; + } + } + } + for (DataSegment segment : params.getUsedSegments()) { if (overshadowed.contains(segment.getId())) { // Skipping overshadowed segments @@ -115,12 +130,6 @@ public DruidCoordinatorRuntimeParams run(DruidCoordinatorRuntimeParams params) if (rule.appliesTo(segment, now)) { stats.accumulate(rule.run(coordinator, paramsWithReplicationManager, segment)); foundMatchingRule = true; - - // The set of broadcast datasources is used by BalanceSegments, so it's important that RunRules - // executes before BalanceSegments - if (rule instanceof BroadcastDistributionRule) { - broadcastDatasources.add(segment.getDataSource()); - } break; } } diff --git a/server/src/main/java/org/apache/druid/server/coordinator/duty/UnloadUnusedSegments.java b/server/src/main/java/org/apache/druid/server/coordinator/duty/UnloadUnusedSegments.java index bea7a9d1cc79..bd8b2c30d550 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/duty/UnloadUnusedSegments.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/duty/UnloadUnusedSegments.java @@ -27,13 +27,18 @@ import org.apache.druid.server.coordinator.DruidCoordinatorRuntimeParams; import org.apache.druid.server.coordinator.LoadQueuePeon; import org.apache.druid.server.coordinator.ServerHolder; +import org.apache.druid.server.coordinator.rules.BroadcastDistributionRule; +import org.apache.druid.server.coordinator.rules.Rule; import org.apache.druid.timeline.DataSegment; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Set; import java.util.SortedSet; /** - * Unloads segments that are no longer marked as used from Historical servers. + * Unloads segments that are no longer marked as used from servers. */ public class UnloadUnusedSegments implements CoordinatorDuty { @@ -46,31 +51,102 @@ public DruidCoordinatorRuntimeParams run(DruidCoordinatorRuntimeParams params) Set usedSegments = params.getUsedSegments(); DruidCluster cluster = params.getDruidCluster(); + Map broadcastStatusByDatasource = new HashMap<>(); + for (String broadcastDatasource : params.getBroadcastDatasources()) { + broadcastStatusByDatasource.put(broadcastDatasource, true); + } + for (SortedSet serverHolders : cluster.getSortedHistoricalsByTier()) { for (ServerHolder serverHolder : serverHolders) { - ImmutableDruidServer server = serverHolder.getServer(); + handleUnusedSegmentsForServer( + serverHolder, + usedSegments, + params, + stats, + false, + broadcastStatusByDatasource + ); + } + } - for (ImmutableDruidDataSource dataSource : server.getDataSources()) { - for (DataSegment segment : dataSource.getSegments()) { - if (!usedSegments.contains(segment)) { - LoadQueuePeon queuePeon = params.getLoadManagementPeons().get(server.getName()); + for (ServerHolder serverHolder : cluster.getBrokers()) { + handleUnusedSegmentsForServer( + serverHolder, + usedSegments, + params, + stats, + false, + broadcastStatusByDatasource + ); + } + + for (ServerHolder serverHolder : cluster.getRealtimes()) { + handleUnusedSegmentsForServer( + serverHolder, + usedSegments, + params, + stats, + true, + broadcastStatusByDatasource + ); + } + + return params.buildFromExisting().withCoordinatorStats(stats).build(); + } - if (!queuePeon.getSegmentsToDrop().contains(segment)) { - queuePeon.dropSegment(segment, () -> {}); - stats.addToTieredStat("unneededCount", server.getTier(), 1); - log.info( - "Dropping uneeded segment [%s] from server [%s] in tier [%s]", - segment.getId(), - server.getName(), - server.getTier() - ); + private void handleUnusedSegmentsForServer( + ServerHolder serverHolder, + Set usedSegments, + DruidCoordinatorRuntimeParams params, + CoordinatorStats stats, + boolean dropBroadcastOnly, + Map broadcastStatusByDatasource + ) + { + ImmutableDruidServer server = serverHolder.getServer(); + for (ImmutableDruidDataSource dataSource : server.getDataSources()) { + boolean isBroadcastDatasource = broadcastStatusByDatasource.computeIfAbsent( + dataSource.getName(), + (dataSourceName) -> { + List rules = params.getDatabaseRuleManager().getRulesWithDefault(dataSource.getName()); + for (Rule rule : rules) { + // A datasource is considered a broadcast datasource if it has any broadcast rules. + if (rule instanceof BroadcastDistributionRule) { + return true; } } + return false; + } + ); + + // The coordinator tracks used segments by examining the metadata store. + // For tasks, the segments they create are unpublished, so those segments will get dropped + // unless we exclude them here. We currently drop only broadcast segments in that case. + // This check relies on the assumption that queryable stream tasks will never + // ingest data to a broadcast datasource. If a broadcast datasource is switched to become a non-broadcast + // datasource, this will result in the those segments not being dropped from tasks. + // A more robust solution which requires a larger rework could be to expose + // the set of segments that were created by a task/indexer here, and exclude them. + if (dropBroadcastOnly && !isBroadcastDatasource) { + continue; + } + + for (DataSegment segment : dataSource.getSegments()) { + if (!usedSegments.contains(segment)) { + LoadQueuePeon queuePeon = params.getLoadManagementPeons().get(server.getName()); + + if (!queuePeon.getSegmentsToDrop().contains(segment)) { + queuePeon.dropSegment(segment, () -> {}); + stats.addToTieredStat("unneededCount", server.getTier(), 1); + log.info( + "Dropping uneeded segment [%s] from server [%s] in tier [%s]", + segment.getId(), + server.getName(), + server.getTier() + ); } } } } - - return params.buildFromExisting().withCoordinatorStats(stats).build(); } } diff --git a/server/src/test/java/org/apache/druid/server/coordinator/duty/MarkAsUnusedOvershadowedSegmentsTest.java b/server/src/test/java/org/apache/druid/server/coordinator/duty/MarkAsUnusedOvershadowedSegmentsTest.java index 73b95ee960ce..301b3bd8f9f9 100644 --- a/server/src/test/java/org/apache/druid/server/coordinator/duty/MarkAsUnusedOvershadowedSegmentsTest.java +++ b/server/src/test/java/org/apache/druid/server/coordinator/duty/MarkAsUnusedOvershadowedSegmentsTest.java @@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; import org.apache.druid.client.ImmutableDruidDataSource; import org.apache.druid.client.ImmutableDruidServer; import org.apache.druid.java.util.common.DateTimes; @@ -39,9 +41,11 @@ import org.joda.time.DateTime; import org.joda.time.Interval; import org.junit.Test; +import org.junit.runner.RunWith; import java.util.List; +@RunWith(JUnitParamsRunner.class) public class MarkAsUnusedOvershadowedSegmentsTest { MarkAsUnusedOvershadowedSegments markAsUnusedOvershadowedSegments; @@ -69,8 +73,16 @@ public class MarkAsUnusedOvershadowedSegmentsTest .build(); @Test - public void testRun() + @Parameters( + { + "historical", + "broker" + } + ) + public void testRun(String serverTypeString) { + ServerType serverType = ServerType.fromString(serverTypeString); + markAsUnusedOvershadowedSegments = new MarkAsUnusedOvershadowedSegments(coordinator); usedSegments = ImmutableList.of(segmentV1, segmentV0, segmentV2); @@ -95,7 +107,7 @@ public void testRun() .andReturn("") .anyTimes(); EasyMock.expect(druidServer.getType()) - .andReturn(ServerType.HISTORICAL) + .andReturn(serverType) .anyTimes(); EasyMock.expect(druidServer.getDataSources()) diff --git a/server/src/test/java/org/apache/druid/server/coordinator/duty/UnloadUnusedSegmentsTest.java b/server/src/test/java/org/apache/druid/server/coordinator/duty/UnloadUnusedSegmentsTest.java new file mode 100644 index 000000000000..f74762c7ad9d --- /dev/null +++ b/server/src/test/java/org/apache/druid/server/coordinator/duty/UnloadUnusedSegmentsTest.java @@ -0,0 +1,364 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.server.coordinator.duty; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.apache.druid.client.DruidServer; +import org.apache.druid.client.ImmutableDruidDataSource; +import org.apache.druid.client.ImmutableDruidServer; +import org.apache.druid.client.ImmutableDruidServerTests; +import org.apache.druid.java.util.common.DateTimes; +import org.apache.druid.metadata.MetadataRuleManager; +import org.apache.druid.server.coordination.ServerType; +import org.apache.druid.server.coordinator.CoordinatorRuntimeParamsTestHelpers; +import org.apache.druid.server.coordinator.CoordinatorStats; +import org.apache.druid.server.coordinator.DruidClusterBuilder; +import org.apache.druid.server.coordinator.DruidCoordinator; +import org.apache.druid.server.coordinator.DruidCoordinatorRuntimeParams; +import org.apache.druid.server.coordinator.LoadQueuePeonTester; +import org.apache.druid.server.coordinator.ServerHolder; +import org.apache.druid.server.coordinator.rules.ForeverBroadcastDistributionRule; +import org.apache.druid.server.coordinator.rules.ForeverLoadRule; +import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.partition.NoneShardSpec; +import org.easymock.EasyMock; +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +public class UnloadUnusedSegmentsTest +{ + private DruidCoordinator coordinator; + private ImmutableDruidServer historicalServer; + private ImmutableDruidServer historicalServerTier2; + private ImmutableDruidServer brokerServer; + private ImmutableDruidServer indexerServer; + private LoadQueuePeonTester historicalPeon; + private LoadQueuePeonTester historicalTier2Peon; + private LoadQueuePeonTester brokerPeon; + private LoadQueuePeonTester indexerPeon; + private DataSegment segment1; + private DataSegment segment2; + private DataSegment broadcastSegment; + private DataSegment realtimeOnlySegment; + private List segments; + private List segmentsForRealtime; + private ImmutableDruidDataSource dataSource1; + private ImmutableDruidDataSource dataSource2; + private ImmutableDruidDataSource dataSource2ForRealtime; + private ImmutableDruidDataSource broadcastDatasource; + private List dataSources; + private List dataSourcesForRealtime; + private Set broadcastDatasourceNames; + private MetadataRuleManager databaseRuleManager; + + @Before + public void setUp() + { + coordinator = EasyMock.createMock(DruidCoordinator.class); + historicalServer = EasyMock.createMock(ImmutableDruidServer.class); + historicalServerTier2 = EasyMock.createMock(ImmutableDruidServer.class); + brokerServer = EasyMock.createMock(ImmutableDruidServer.class); + indexerServer = EasyMock.createMock(ImmutableDruidServer.class); + segment1 = EasyMock.createMock(DataSegment.class); + segment2 = EasyMock.createMock(DataSegment.class); + databaseRuleManager = EasyMock.createMock(MetadataRuleManager.class); + + DateTime start1 = DateTimes.of("2012-01-01"); + DateTime start2 = DateTimes.of("2012-02-01"); + DateTime version = DateTimes.of("2012-05-01"); + segment1 = new DataSegment( + "datasource1", + new Interval(start1, start1.plusHours(1)), + version.toString(), + new HashMap<>(), + new ArrayList<>(), + new ArrayList<>(), + NoneShardSpec.instance(), + 0, + 11L + ); + segment2 = new DataSegment( + "datasource2", + new Interval(start1, start1.plusHours(1)), + version.toString(), + new HashMap<>(), + new ArrayList<>(), + new ArrayList<>(), + NoneShardSpec.instance(), + 0, + 7L + ); + realtimeOnlySegment = new DataSegment( + "datasource2", + new Interval(start2, start2.plusHours(1)), + version.toString(), + new HashMap<>(), + new ArrayList<>(), + new ArrayList<>(), + NoneShardSpec.instance(), + 0, + 7L + ); + broadcastSegment = new DataSegment( + "broadcastDatasource", + new Interval(start1, start1.plusHours(1)), + version.toString(), + new HashMap<>(), + new ArrayList<>(), + new ArrayList<>(), + NoneShardSpec.instance(), + 0, + 7L + ); + + segments = new ArrayList<>(); + segments.add(segment1); + segments.add(segment2); + segments.add(broadcastSegment); + + segmentsForRealtime = new ArrayList<>(); + segmentsForRealtime.add(realtimeOnlySegment); + segmentsForRealtime.add(broadcastSegment); + + historicalPeon = new LoadQueuePeonTester(); + historicalTier2Peon = new LoadQueuePeonTester(); + brokerPeon = new LoadQueuePeonTester(); + indexerPeon = new LoadQueuePeonTester(); + + dataSource1 = new ImmutableDruidDataSource( + "datasource1", + Collections.emptyMap(), + Collections.singleton(segment1) + ); + dataSource2 = new ImmutableDruidDataSource( + "datasource2", + Collections.emptyMap(), + Collections.singleton(segment2) + ); + + broadcastDatasourceNames = Collections.singleton("broadcastDatasource"); + broadcastDatasource = new ImmutableDruidDataSource( + "broadcastDatasource", + Collections.emptyMap(), + Collections.singleton(broadcastSegment) + ); + + dataSources = ImmutableList.of(dataSource1, dataSource2, broadcastDatasource); + + // This simulates a task that is ingesting to an existing non-broadcast datasource, with unpublished segments, + // while also having a broadcast segment loaded. + dataSource2ForRealtime = new ImmutableDruidDataSource( + "datasource2", + Collections.emptyMap(), + Collections.singleton(realtimeOnlySegment) + ); + dataSourcesForRealtime = ImmutableList.of(dataSource2ForRealtime, broadcastDatasource); + } + + @After + public void tearDown() + { + EasyMock.verify(coordinator); + EasyMock.verify(historicalServer); + EasyMock.verify(historicalServerTier2); + EasyMock.verify(brokerServer); + EasyMock.verify(indexerServer); + EasyMock.verify(databaseRuleManager); + } + + @Test + public void test_unloadUnusedSegmentsFromAllServers() + { + mockDruidServer( + historicalServer, + ServerType.HISTORICAL, + "historical", + DruidServer.DEFAULT_TIER, + 30L, + 100L, + segments, + dataSources + ); + mockDruidServer( + historicalServerTier2, + ServerType.HISTORICAL, + "historicalTier2", + "tier2", + 30L, + 100L, + segments, + dataSources + ); + mockDruidServer( + brokerServer, + ServerType.BROKER, + "broker", + DruidServer.DEFAULT_TIER, + 30L, + 100L, + segments, + dataSources + ); + mockDruidServer( + indexerServer, + ServerType.INDEXER_EXECUTOR, + "indexer", + DruidServer.DEFAULT_TIER, + 30L, + 100L, + segmentsForRealtime, + dataSourcesForRealtime + ); + + // Mock stuff that the coordinator needs + mockCoordinator(coordinator); + + mockRuleManager(databaseRuleManager); + + // We keep datasource2 segments only, drop datasource1 and broadcastDatasource from all servers + // realtimeSegment is intentionally missing from the set, to match how a realtime tasks's unpublished segments + // will not appear in the coordinator's view of used segments. + Set usedSegments = ImmutableSet.of(segment2); + + DruidCoordinatorRuntimeParams params = CoordinatorRuntimeParamsTestHelpers + .newBuilder() + .withDruidCluster( + DruidClusterBuilder + .newBuilder() + .addTier( + DruidServer.DEFAULT_TIER, + new ServerHolder(historicalServer, historicalPeon, false) + ) + .addTier( + "tier2", + new ServerHolder(historicalServerTier2, historicalTier2Peon, false) + ) + .withBrokers( + new ServerHolder(brokerServer, brokerPeon, false) + ) + .withRealtimes( + new ServerHolder(indexerServer, indexerPeon, false) + ) + .build() + ) + .withLoadManagementPeons( + ImmutableMap.of( + "historical", historicalPeon, + "historicalTier2", historicalTier2Peon, + "broker", brokerPeon, + "indexer", indexerPeon + ) + ) + .withUsedSegmentsInTest(usedSegments) + .withBroadcastDatasources(broadcastDatasourceNames) + .withDatabaseRuleManager(databaseRuleManager) + .build(); + + params = new UnloadUnusedSegments().run(params); + CoordinatorStats stats = params.getCoordinatorStats(); + + // We drop segment1 and broadcast1 from all servers, realtimeSegment is not dropped by the indexer + Assert.assertEquals(5, stats.getTieredStat("unneededCount", DruidServer.DEFAULT_TIER)); + Assert.assertEquals(2, stats.getTieredStat("unneededCount", "tier2")); + } + + private static void mockDruidServer( + ImmutableDruidServer druidServer, + ServerType serverType, + String name, + String tier, + long currentSize, + long maxSize, + List segments, + List dataSources + ) + { + EasyMock.expect(druidServer.getName()).andReturn(name).anyTimes(); + EasyMock.expect(druidServer.getTier()).andReturn(tier).anyTimes(); + EasyMock.expect(druidServer.getCurrSize()).andReturn(currentSize).anyTimes(); + EasyMock.expect(druidServer.getMaxSize()).andReturn(maxSize).anyTimes(); + ImmutableDruidServerTests.expectSegments(druidServer, segments); + EasyMock.expect(druidServer.getHost()).andReturn(name).anyTimes(); + EasyMock.expect(druidServer.getType()).andReturn(serverType).anyTimes(); + EasyMock.expect(druidServer.getDataSources()).andReturn(dataSources).anyTimes(); + if (!segments.isEmpty()) { + segments.forEach( + s -> EasyMock.expect(druidServer.getSegment(s.getId())).andReturn(s).anyTimes() + ); + } + EasyMock.expect(druidServer.getSegment(EasyMock.anyObject())).andReturn(null).anyTimes(); + EasyMock.replay(druidServer); + } + + private static void mockCoordinator(DruidCoordinator coordinator) + { + coordinator.moveSegment( + EasyMock.anyObject(), + EasyMock.anyObject(), + EasyMock.anyObject(), + EasyMock.anyObject(), + EasyMock.anyObject() + ); + EasyMock.expectLastCall().anyTimes(); + EasyMock.replay(coordinator); + } + + private static void mockRuleManager(MetadataRuleManager metadataRuleManager) + { + EasyMock.expect(metadataRuleManager.getRulesWithDefault("datasource1")).andReturn( + Collections.singletonList( + new ForeverLoadRule( + ImmutableMap.of( + DruidServer.DEFAULT_TIER, 1, + "tier2", 1 + ) + ) + )).anyTimes(); + + EasyMock.expect(metadataRuleManager.getRulesWithDefault("datasource2")).andReturn( + Collections.singletonList( + new ForeverLoadRule( + ImmutableMap.of( + DruidServer.DEFAULT_TIER, 1, + "tier2", 1 + ) + ) + )).anyTimes(); + + EasyMock.expect(metadataRuleManager.getRulesWithDefault("broadcastDatasource")).andReturn( + Collections.singletonList( + new ForeverBroadcastDistributionRule() + )).anyTimes(); + + EasyMock.replay(metadataRuleManager); + } +} From 38327facce716aa45bfb4b9e51903be08f0d8087 Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Fri, 12 Jun 2020 13:09:22 -0700 Subject: [PATCH 080/107] fix balancer + broadcast segments npe (#10021) --- .../coordinator/DruidCoordinatorRuntimeParams.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java index 3337b8e7647a..d73febeca0e3 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java @@ -226,7 +226,8 @@ public Builder buildFromExisting() coordinatorCompactionConfig, stats, balancerReferenceTimestamp, - balancerStrategy + balancerStrategy, + broadcastDatasources ); } @@ -246,7 +247,8 @@ public Builder buildFromExistingWithoutSegmentsMetadata() coordinatorCompactionConfig, stats, balancerReferenceTimestamp, - balancerStrategy + balancerStrategy, + broadcastDatasources ); } @@ -300,7 +302,8 @@ private Builder() CoordinatorCompactionConfig coordinatorCompactionConfig, CoordinatorStats stats, DateTime balancerReferenceTimestamp, - BalancerStrategy balancerStrategy + BalancerStrategy balancerStrategy, + Set broadcastDatasources ) { this.startTimeNanos = startTimeNanos; @@ -317,6 +320,7 @@ private Builder() this.stats = stats; this.balancerReferenceTimestamp = balancerReferenceTimestamp; this.balancerStrategy = balancerStrategy; + this.broadcastDatasources = broadcastDatasources; } public DruidCoordinatorRuntimeParams build() From f3b2b1af36f58d7bf9a346e44fa18cc231a61924 Mon Sep 17 00:00:00 2001 From: Jihoon Son Date: Fri, 12 Jun 2020 21:39:37 -0700 Subject: [PATCH 081/107] Set the core partition set size properly for batch ingestion with dynamic partitioning (#10012) * Fill in the core partition set size properly for batch ingestion with dynamic partitioning * incomplete javadoc * Address comments * fix tests * fix json serde, add tests * checkstyle --- .../partition/BuildingNumberedShardSpec.java | 153 ++++++++++++++++++ .../partition/NumberedOverwriteShardSpec.java | 21 ++- .../partition/NumberedPartialShardSpec.java | 7 + .../timeline/partition/NumberedShardSpec.java | 5 + .../timeline/partition/PartialShardSpec.java | 8 +- .../druid/timeline/partition/ShardSpec.java | 35 +++- .../timeline/partition/ShardSpecLookup.java | 4 + .../BuildingNumberedShardSpecTest.java | 93 +++++++++++ .../NumberedOverwriteShardSpecTest.java | 53 ++++++ .../common/task/LocalSegmentAllocator.java | 4 +- .../parallel/ParallelIndexSupervisorTask.java | 3 +- .../common/task/CompactionTaskRunTest.java | 24 +-- .../SinglePhaseParallelIndexingTest.java | 92 ++++++++++- .../appenderator/SegmentPublisherHelper.java | 107 ++++++++++++ .../TransactionalSegmentPublisher.java | 60 +------ 15 files changed, 588 insertions(+), 81 deletions(-) create mode 100644 core/src/main/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpec.java create mode 100644 core/src/test/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpecTest.java create mode 100644 core/src/test/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpecTest.java create mode 100644 server/src/main/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelper.java diff --git a/core/src/main/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpec.java new file mode 100644 index 000000000000..4604f23a982c --- /dev/null +++ b/core/src/main/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpec.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Preconditions; +import com.google.common.collect.RangeSet; +import org.apache.druid.data.input.InputRow; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * This is a special shardSpec which is temporarily used during batch ingestion. In Druid, there is a concept + * of core partition set which is a set of segments atomically becoming queryable together in Brokers. The core + * partition set is represented as a range of partitionIds. For {@link NumberedShardSpec}, the core partition set + * is [0, {@link NumberedShardSpec#partitions}). + * + * The NumberedShardSpec is used for dynamic partitioning which is based on the number of rows in each segment. + * In streaming ingestion, the core partition set size cannot be determined since it's impossible to know how many + * segments will be created per time chunk. However, in batch ingestion with time chunk locking, the core partition + * set is the set of segments created by an initial task or an overwriting task. Since the core partition set is + * determined when the task publishes segments at the end, the task postpones creating proper NumberedShardSpec + * until the end. + * + * This shardSpec is used for such use case. A non-appending batch task can use this shardSpec until it publishes + * segments at last. When it publishes segments, it should convert the shardSpec of those segments to NumberedShardSpec. + * See {@code SegmentPublisherHelper#annotateShardSpec} for converting to NumberedShardSpec. Note that, when + * the segment lock is used, the Overlord coordinates the segment allocation and this class is never used. Instead, + * the task sends {@link PartialShardSpec} to the Overlord to allocate a new segment. The result segment could have + * either a {@link ShardSpec} (for root generation segments) or an {@link OverwriteShardSpec} (for non-root + * generation segments). + * + * This class should be Jackson-serializable as the subtasks can send it to the parallel task in parallel ingestion. + * + * Finally, this shardSpec has only partitionId which is same as {@link LinearShardSpec}. The difference between + * them is this shardSpec should never be published and so never be used in other places such as Broker timeline. + * + * @see NumberedShardSpec + */ +public class BuildingNumberedShardSpec implements ShardSpec +{ + public static final String TYPE = "building_numbered"; + + private final int partitionId; + + @JsonCreator + public BuildingNumberedShardSpec(@JsonProperty("partitionId") int partitionId) + { + Preconditions.checkArgument(partitionId >= 0, "partitionId >= 0"); + this.partitionId = partitionId; + } + + public NumberedShardSpec toNumberedShardSpec(int numTotalPartitions) + { + return new NumberedShardSpec(partitionId, numTotalPartitions); + } + + @Override + public PartitionChunk createChunk(T obj) + { + // This method can be called in AppenderatorImpl to create a sinkTimeline. + // The sinkTimeline doesn't seem in use in batch ingestion, let's set 'chunks' to 0 for now. + return new NumberedPartitionChunk<>(partitionId, 0, obj); + } + + @JsonProperty("partitionId") + @Override + public int getPartitionNum() + { + return partitionId; + } + + @Override + public ShardSpecLookup getLookup(List shardSpecs) + { + return NumberedShardSpec.createLookup(shardSpecs); + } + + // The below methods are used on the query side, and so must not be called for this shardSpec. + + @Override + public boolean isInChunk(long timestamp, InputRow inputRow) + { + throw new UnsupportedOperationException(); + } + + @JsonIgnore + @Override + public List getDomainDimensions() + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean possibleInDomain(Map> domain) + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isCompatible(Class other) + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BuildingNumberedShardSpec shardSpec = (BuildingNumberedShardSpec) o; + return partitionId == shardSpec.partitionId; + } + + @Override + public int hashCode() + { + return Objects.hash(partitionId); + } + + @Override + public String toString() + { + return "BuildingNumberedShardSpec{" + + "partitionId=" + partitionId + + '}'; + } +} diff --git a/core/src/main/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpec.java index d5b36576a654..dbbb8f66e936 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpec.java @@ -24,6 +24,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.RangeSet; import org.apache.druid.data.input.InputRow; +import org.apache.druid.timeline.DataSegment; import java.util.Collections; import java.util.List; @@ -31,10 +32,28 @@ import java.util.Objects; /** - * ShardSpec for segments which overshadow others with their minorVersion. + * This shardSpec is used only for the segments created by overwriting tasks with segment lock enabled. + * When the segment lock is used, there is a concept of atomic update group which is a set of segments atomically + * becoming queryable together in Brokers. It is a similar concept to the core partition set (explained + * {@link NumberedShardSpec}), but different in a sense that there is only one core partition set per time chunk + * while there could be multiple atomic update groups in one time chunk. + * + * The atomic update group has the root partition range and the minor version to determine the visibility between + * atomic update groups; the group of the highest minor version in the same root partition range becomes queryable + * when they have the same major version ({@link DataSegment#getVersion()}). + * + * Note that this shardSpec is used only when you overwrite existing segments with segment lock enabled. + * If the task doesn't overwrite segments, it will use NumberedShardSpec instead even when segment lock is used. + * Similar to NumberedShardSpec, the size of the atomic update group is determined when the task publishes segments + * at the end of ingestion. As a result, {@link #atomicUpdateGroupSize} is set to + * {@link PartitionIds#UNKNOWN_ATOMIC_UPDATE_GROUP_SIZE} first, and updated when publishing segments + * in {@code SegmentPublisherHelper#annotateShardSpec}. + * + * @see AtomicUpdateGroup */ public class NumberedOverwriteShardSpec implements OverwriteShardSpec { + public static final String TYPE = "numbered_overwrite"; private final int partitionId; private final short startRootPartitionId; diff --git a/core/src/main/java/org/apache/druid/timeline/partition/NumberedPartialShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/NumberedPartialShardSpec.java index 0e258ee786b2..7c7b9753aaa8 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/NumberedPartialShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/NumberedPartialShardSpec.java @@ -40,6 +40,13 @@ private NumberedPartialShardSpec() public ShardSpec complete(ObjectMapper objectMapper, @Nullable ShardSpec specOfPreviousMaxPartitionId) { if (specOfPreviousMaxPartitionId == null) { + // The shardSpec is created by the Overlord. + // - For streaming ingestion tasks, the core partition set is always 0. + // - For batch tasks, this code is executed only with segment locking (forceTimeChunkLock = false). + // In this mode, you can have 2 or more tasks concurrently ingesting into the same time chunk of + // the same datasource. Since there is no restriction for those tasks in segment allocation, the + // allocated IDs for each task can interleave. As a result, the core partition set cannot be + // represented as a range. We always set 0 for the core partition set size. return new NumberedShardSpec(0, 0); } else { final NumberedShardSpec prevSpec = (NumberedShardSpec) specOfPreviousMaxPartitionId; diff --git a/core/src/main/java/org/apache/druid/timeline/partition/NumberedShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/NumberedShardSpec.java index d6f98d71751d..6f8898e298f1 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/NumberedShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/NumberedShardSpec.java @@ -67,6 +67,11 @@ public int getPartitionNum() @Override public ShardSpecLookup getLookup(final List shardSpecs) + { + return createLookup(shardSpecs); + } + + static ShardSpecLookup createLookup(List shardSpecs) { return (long timestamp, InputRow row) -> shardSpecs.get(0); } diff --git a/core/src/main/java/org/apache/druid/timeline/partition/PartialShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/PartialShardSpec.java index 9cbee2c7571c..6afaa939471e 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/PartialShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/PartialShardSpec.java @@ -28,8 +28,12 @@ import javax.annotation.Nullable; /** - * Class to contain all information of a {@link ShardSpec} except for the partition ID. - * This class is mainly used by the indexing tasks to allocate new segments using the Overlord. + * This interface is used in the segment allocation protocol when it is coordinated by the Overlord; when appending + * segments to an existing datasource (either streaming ingestion or batch append) or any case when segment + * lock is used. The implementations of this interface contain all information of the corresponding {@link ShardSpec} + * except the partition ID. + * The ingestion tasks send all information required for allocating a new segment using this interface and the Overlord + * determines the partition ID to create a new segment. */ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ diff --git a/core/src/main/java/org/apache/druid/timeline/partition/ShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/ShardSpec.java index 43aaf701db36..06ff8dd6720d 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/ShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/ShardSpec.java @@ -19,6 +19,7 @@ package org.apache.druid.timeline.partition; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.google.common.collect.RangeSet; @@ -38,36 +39,63 @@ @JsonSubTypes.Type(name = "linear", value = LinearShardSpec.class), @JsonSubTypes.Type(name = "numbered", value = NumberedShardSpec.class), @JsonSubTypes.Type(name = "hashed", value = HashBasedNumberedShardSpec.class), - @JsonSubTypes.Type(name = "numbered_overwrite", value = NumberedOverwriteShardSpec.class) + @JsonSubTypes.Type(name = NumberedOverwriteShardSpec.TYPE, value = NumberedOverwriteShardSpec.class), + @JsonSubTypes.Type(name = BuildingNumberedShardSpec.TYPE, value = BuildingNumberedShardSpec.class) }) public interface ShardSpec { + @JsonIgnore PartitionChunk createChunk(T obj); + @JsonIgnore boolean isInChunk(long timestamp, InputRow inputRow); + /** + * Returns the partition ID of this segment. + */ int getPartitionNum(); + /** + * Returns the start root partition ID of the atomic update group which this segment belongs to. + * + * @see AtomicUpdateGroup + */ default int getStartRootPartitionId() { return getPartitionNum(); } + /** + * Returns the end root partition ID of the atomic update group which this segment belongs to. + * + * @see AtomicUpdateGroup + */ default int getEndRootPartitionId() { return getPartitionNum() + 1; } + /** + * Returns the minor version associated to the atomic update group which this segment belongs to. + * + * @see AtomicUpdateGroup + */ default short getMinorVersion() { return 0; } + /** + * Returns the atomic update group size which this segment belongs to. + * + * @see AtomicUpdateGroup + */ default short getAtomicUpdateGroupSize() { return 1; } + @JsonIgnore ShardSpecLookup getLookup(List shardSpecs); /** @@ -75,16 +103,19 @@ default short getAtomicUpdateGroupSize() * * @return list of dimensions who has its possible range. Dimensions with unknown possible range are not listed */ + @JsonIgnore List getDomainDimensions(); /** * if given domain ranges are not possible in this shard, return false; otherwise return true; * @return possibility of in domain */ + @JsonIgnore boolean possibleInDomain(Map> domain); /** - * Returns true if two segments of this and other shardSpecs can exist in the same timeChunk. + * Returns true if two segments of this and other shardSpecs can exist in the same time chunk. */ + @JsonIgnore boolean isCompatible(Class other); } diff --git a/core/src/main/java/org/apache/druid/timeline/partition/ShardSpecLookup.java b/core/src/main/java/org/apache/druid/timeline/partition/ShardSpecLookup.java index 25c785b542e8..610e92a0ea6b 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/ShardSpecLookup.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/ShardSpecLookup.java @@ -23,5 +23,9 @@ public interface ShardSpecLookup { + /** + * Returns a {@link ShardSpec} for the given timestamp and the inputRow. + * The timestamp must be bucketed using {@code GranularitySpec#getQueryGranularity}. + */ ShardSpec getShardSpec(long timestamp, InputRow row); } diff --git a/core/src/test/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpecTest.java new file mode 100644 index 000000000000..21c5a03644e5 --- /dev/null +++ b/core/src/test/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpecTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.apache.druid.data.input.MapBasedInputRow; +import org.apache.druid.java.util.common.DateTimes; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class BuildingNumberedShardSpecTest +{ + @Test + public void testToNumberedShardSpec() + { + Assert.assertEquals(new NumberedShardSpec(5, 10), new BuildingNumberedShardSpec(5).toNumberedShardSpec(10)); + } + + @Test + public void testCreateChunk() + { + Assert.assertEquals( + new NumberedPartitionChunk<>(5, 0, "test"), + new BuildingNumberedShardSpec(5).createChunk("test") + ); + } + + @Test + public void testShardSpecLookup() + { + final List shardSpecs = ImmutableList.of( + new BuildingNumberedShardSpec(1), + new BuildingNumberedShardSpec(2), + new BuildingNumberedShardSpec(3) + ); + final ShardSpecLookup lookup = shardSpecs.get(0).getLookup(shardSpecs); + // Timestamp doesn't matter. It always returns the first shardSpec. + final long currentTime = DateTimes.nowUtc().getMillis(); + Assert.assertEquals( + shardSpecs.get(0), + lookup.getShardSpec( + currentTime, + new MapBasedInputRow( + currentTime, + ImmutableList.of("dim"), ImmutableMap.of("dim", "val", "time", currentTime) + ) + ) + ); + } + + @Test + public void testSerde() throws JsonProcessingException + { + final ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.registerSubtypes(new NamedType(BuildingNumberedShardSpec.class, BuildingNumberedShardSpec.TYPE)); + final BuildingNumberedShardSpec original = new BuildingNumberedShardSpec(5); + final String json = mapper.writeValueAsString(original); + final BuildingNumberedShardSpec fromJson = (BuildingNumberedShardSpec) mapper.readValue(json, ShardSpec.class); + Assert.assertEquals(original, fromJson); + } + + @Test + public void testEquals() + { + EqualsVerifier.forClass(BuildingNumberedShardSpec.class).usingGetClass().verify(); + } +} diff --git a/core/src/test/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpecTest.java new file mode 100644 index 000000000000..d605105c48d5 --- /dev/null +++ b/core/src/test/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpecTest.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Assert; +import org.junit.Test; + +public class NumberedOverwriteShardSpecTest +{ + @Test + public void testEquals() + { + EqualsVerifier.forClass(NumberedOverwriteShardSpec.class).usingGetClass().verify(); + } + + @Test + public void testSerde() throws JsonProcessingException + { + final ObjectMapper mapper = new ObjectMapper(); + mapper.registerSubtypes(new NamedType(NumberedOverwriteShardSpec.class, NumberedOverwriteShardSpec.TYPE)); + final NumberedOverwriteShardSpec original = new NumberedOverwriteShardSpec( + PartitionIds.NON_ROOT_GEN_START_PARTITION_ID + 2, + 0, + 10, + (short) 1, + (short) 3 + ); + final String json = mapper.writeValueAsString(original); + final NumberedOverwriteShardSpec fromJson = (NumberedOverwriteShardSpec) mapper.readValue(json, ShardSpec.class); + Assert.assertEquals(original, fromJson); + } +} diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/LocalSegmentAllocator.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/LocalSegmentAllocator.java index ead0f635599f..2cb4db551c4a 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/LocalSegmentAllocator.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/LocalSegmentAllocator.java @@ -30,7 +30,7 @@ import org.apache.druid.segment.indexing.granularity.GranularitySpec; import org.apache.druid.segment.realtime.appenderator.SegmentAllocator; import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; -import org.apache.druid.timeline.partition.NumberedShardSpec; +import org.apache.druid.timeline.partition.BuildingNumberedShardSpec; import org.joda.time.DateTime; import org.joda.time.Interval; @@ -77,7 +77,7 @@ class LocalSegmentAllocator implements SegmentAllocator dataSource, interval, version, - new NumberedShardSpec(partitionId, 0) + new BuildingNumberedShardSpec(partitionId) ); }; } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTask.java index 2d2ec3a0f2b6..cb1bc3986510 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTask.java @@ -78,6 +78,7 @@ import org.apache.druid.server.security.Action; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.partition.BuildingNumberedShardSpec; import org.apache.druid.timeline.partition.NumberedShardSpec; import org.apache.druid.timeline.partition.PartitionBoundaries; import org.apache.druid.utils.CollectionUtils; @@ -984,7 +985,7 @@ SegmentIdWithShardSpec allocateNewSegment(DateTime timestamp) throws IOException dataSource, interval, version, - new NumberedShardSpec(partitionNum, 0) + new BuildingNumberedShardSpec(partitionNum) ); } diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskRunTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskRunTest.java index 2a5b02e1db17..e08361450fb9 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskRunTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/CompactionTaskRunTest.java @@ -252,7 +252,7 @@ public void testRun() throws Exception segments.get(i).getShardSpec() ); } else { - Assert.assertEquals(new NumberedShardSpec(0, 0), segments.get(i).getShardSpec()); + Assert.assertEquals(new NumberedShardSpec(0, 1), segments.get(i).getShardSpec()); } } @@ -301,7 +301,7 @@ public void testRunCompactionTwice() throws Exception segments.get(i).getShardSpec() ); } else { - Assert.assertEquals(new NumberedShardSpec(0, 0), segments.get(i).getShardSpec()); + Assert.assertEquals(new NumberedShardSpec(0, 1), segments.get(i).getShardSpec()); } } @@ -334,7 +334,7 @@ public void testRunCompactionTwice() throws Exception segments.get(i).getShardSpec() ); } else { - Assert.assertEquals(new NumberedShardSpec(0, 0), segments.get(i).getShardSpec()); + Assert.assertEquals(new NumberedShardSpec(0, 1), segments.get(i).getShardSpec()); } } } @@ -414,7 +414,11 @@ public void testRunIndexAndCompactAtTheSameTimeForDifferentInterval() throws Exc for (int i = 0; i < 6; i++) { Assert.assertEquals(Intervals.of("2014-01-01T0%d:00:00/2014-01-01T0%d:00:00", 3 + i / 2, 3 + i / 2 + 1), segments.get(i).getInterval()); - Assert.assertEquals(new NumberedShardSpec(i % 2, 0), segments.get(i).getShardSpec()); + if (lockGranularity == LockGranularity.SEGMENT) { + Assert.assertEquals(new NumberedShardSpec(i % 2, 0), segments.get(i).getShardSpec()); + } else { + Assert.assertEquals(new NumberedShardSpec(i % 2, 2), segments.get(i).getShardSpec()); + } } Assert.assertTrue(compactionFuture.get().lhs.isSuccess()); @@ -434,7 +438,7 @@ public void testRunIndexAndCompactAtTheSameTimeForDifferentInterval() throws Exc segments.get(i).getShardSpec() ); } else { - Assert.assertEquals(new NumberedShardSpec(0, 0), segments.get(i).getShardSpec()); + Assert.assertEquals(new NumberedShardSpec(0, 1), segments.get(i).getShardSpec()); } } } @@ -472,7 +476,7 @@ public void testWithSegmentGranularity() throws Exception Assert.assertEquals(1, segments.size()); Assert.assertEquals(Intervals.of("2014-01-01/2014-01-02"), segments.get(0).getInterval()); - Assert.assertEquals(new NumberedShardSpec(0, 0), segments.get(0).getShardSpec()); + Assert.assertEquals(new NumberedShardSpec(0, 1), segments.get(0).getShardSpec()); Assert.assertEquals(DEFAULT_COMPACTION_STATE, segments.get(0).getLastCompactionState()); // hour segmentGranularity @@ -490,7 +494,7 @@ public void testWithSegmentGranularity() throws Exception for (int i = 0; i < 3; i++) { Assert.assertEquals(Intervals.of("2014-01-01T0%d:00:00/2014-01-01T0%d:00:00", i, i + 1), segments.get(i).getInterval()); - Assert.assertEquals(new NumberedShardSpec(0, 0), segments.get(i).getShardSpec()); + Assert.assertEquals(new NumberedShardSpec(0, 1), segments.get(i).getShardSpec()); Assert.assertEquals(DEFAULT_COMPACTION_STATE, segments.get(i).getLastCompactionState()); } } @@ -594,7 +598,7 @@ public void testRunIndexAndCompactForSameSegmentAtTheSameTime() throws Exception segments.get(i).getShardSpec() ); } else { - Assert.assertEquals(new NumberedShardSpec(i % 2, 0), segments.get(i).getShardSpec()); + Assert.assertEquals(new NumberedShardSpec(i % 2, 2), segments.get(i).getShardSpec()); } } @@ -667,7 +671,7 @@ public void testRunIndexAndCompactForSameSegmentAtTheSameTime2() throws Exceptio segments.get(i).getShardSpec() ); } else { - Assert.assertEquals(new NumberedShardSpec(i % 2, 0), segments.get(i).getShardSpec()); + Assert.assertEquals(new NumberedShardSpec(i % 2, 2), segments.get(i).getShardSpec()); } } @@ -757,7 +761,7 @@ public void testRunRegularIndexTaskWithIngestSegmentFirehose() throws Exception segments.get(i).getShardSpec() ); } else { - Assert.assertEquals(new NumberedShardSpec(0, 0), segments.get(i).getShardSpec()); + Assert.assertEquals(new NumberedShardSpec(0, 1), segments.get(i).getShardSpec()); } } } diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseParallelIndexingTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseParallelIndexingTest.java index 7b1baa9bc1f0..e1e21a56719d 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseParallelIndexingTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseParallelIndexingTest.java @@ -43,6 +43,8 @@ import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.Partitions; import org.apache.druid.timeline.VersionedIntervalTimeline; +import org.apache.druid.timeline.partition.NumberedOverwriteShardSpec; +import org.apache.druid.timeline.partition.NumberedShardSpec; import org.joda.time.Interval; import org.junit.After; import org.junit.Assert; @@ -57,10 +59,13 @@ import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; @@ -155,6 +160,19 @@ private void runTestTask(@Nullable Interval interval, Granularity segmentGranula final ParallelIndexSupervisorTask task = newTask(interval, segmentGranularity, appendToExisting, true); task.addToContext(Tasks.FORCE_TIME_CHUNK_LOCK_KEY, lockGranularity == LockGranularity.TIME_CHUNK); Assert.assertEquals(TaskState.SUCCESS, getIndexingServiceClient().runAndWait(task).getStatusCode()); + assertShardSpec(interval, appendToExisting); + } + + private void runOverwriteTask( + @Nullable Interval interval, + Granularity segmentGranularity, + LockGranularity actualLockGranularity + ) + { + final ParallelIndexSupervisorTask task = newTask(interval, segmentGranularity, false, true); + task.addToContext(Tasks.FORCE_TIME_CHUNK_LOCK_KEY, lockGranularity == LockGranularity.TIME_CHUNK); + Assert.assertEquals(TaskState.SUCCESS, getIndexingServiceClient().runAndWait(task).getStatusCode()); + assertShardSpecAfterOverwrite(interval, actualLockGranularity); } private void runTestTask(@Nullable Interval interval, Granularity segmentGranularity) @@ -173,7 +191,11 @@ private void testRunAndOverwrite(@Nullable Interval inputInterval, Granularity s ); // Reingest the same data. Each segment should get replaced by a segment with a newer version. - runTestTask(inputInterval, secondSegmentGranularity); + runOverwriteTask( + inputInterval, + secondSegmentGranularity, + secondSegmentGranularity.equals(Granularities.DAY) ? lockGranularity : LockGranularity.TIME_CHUNK + ); // Verify that the segment has been replaced. final Collection newSegments = @@ -184,6 +206,62 @@ private void testRunAndOverwrite(@Nullable Interval inputInterval, Granularity s Assert.assertEquals(new HashSet<>(newSegments), visibles); } + private void assertShardSpec(@Nullable Interval interval, boolean appendToExisting) + { + final Interval nonNullInterval = interval == null ? Intervals.ETERNITY : interval; + final Collection segments = + getStorageCoordinator().retrieveUsedSegmentsForInterval("dataSource", nonNullInterval, Segments.ONLY_VISIBLE); + if (!appendToExisting && lockGranularity != LockGranularity.SEGMENT) { + // Check the core partition set in the shardSpec + final Map> intervalToSegments = new HashMap<>(); + segments.forEach( + segment -> intervalToSegments.computeIfAbsent(segment.getInterval(), k -> new ArrayList<>()).add(segment) + ); + for (List segmentsPerInterval : intervalToSegments.values()) { + for (DataSegment segment : segmentsPerInterval) { + Assert.assertSame(NumberedShardSpec.class, segment.getShardSpec().getClass()); + final NumberedShardSpec shardSpec = (NumberedShardSpec) segment.getShardSpec(); + Assert.assertEquals(segmentsPerInterval.size(), shardSpec.getPartitions()); + } + } + } else { + for (DataSegment segment : segments) { + Assert.assertSame(NumberedShardSpec.class, segment.getShardSpec().getClass()); + final NumberedShardSpec shardSpec = (NumberedShardSpec) segment.getShardSpec(); + Assert.assertEquals(0, shardSpec.getPartitions()); + } + } + } + + private void assertShardSpecAfterOverwrite(@Nullable Interval interval, LockGranularity actualLockGranularity) + { + final Interval nonNullInterval = interval == null ? Intervals.ETERNITY : interval; + final Collection segments = + getStorageCoordinator().retrieveUsedSegmentsForInterval("dataSource", nonNullInterval, Segments.ONLY_VISIBLE); + final Map> intervalToSegments = new HashMap<>(); + segments.forEach( + segment -> intervalToSegments.computeIfAbsent(segment.getInterval(), k -> new ArrayList<>()).add(segment) + ); + if (actualLockGranularity != LockGranularity.SEGMENT) { + // Check the core partition set in the shardSpec + for (List segmentsPerInterval : intervalToSegments.values()) { + for (DataSegment segment : segmentsPerInterval) { + Assert.assertSame(NumberedShardSpec.class, segment.getShardSpec().getClass()); + final NumberedShardSpec shardSpec = (NumberedShardSpec) segment.getShardSpec(); + Assert.assertEquals(segmentsPerInterval.size(), shardSpec.getPartitions()); + } + } + } else { + for (List segmentsPerInterval : intervalToSegments.values()) { + for (DataSegment segment : segmentsPerInterval) { + Assert.assertSame(NumberedOverwriteShardSpec.class, segment.getShardSpec().getClass()); + final NumberedOverwriteShardSpec shardSpec = (NumberedOverwriteShardSpec) segment.getShardSpec(); + Assert.assertEquals(segmentsPerInterval.size(), shardSpec.getAtomicUpdateGroupSize()); + } + } + } + } + @Test public void testWithoutInterval() { @@ -213,9 +291,12 @@ public void testRunInParallelWithDifferentSegmentGranularity() @Test public void testRunInSequential() { - final ParallelIndexSupervisorTask task = newTask(Intervals.of("2017-12/P1M"), false, false); + final Interval interval = Intervals.of("2017-12/P1M"); + final boolean appendToExisting = false; + final ParallelIndexSupervisorTask task = newTask(interval, appendToExisting, false); task.addToContext(Tasks.FORCE_TIME_CHUNK_LOCK_KEY, lockGranularity == LockGranularity.TIME_CHUNK); Assert.assertEquals(TaskState.SUCCESS, getIndexingServiceClient().runAndWait(task).getStatusCode()); + assertShardSpec(interval, appendToExisting); } @Test @@ -229,10 +310,12 @@ public void testPublishEmptySegments() @Test public void testWith1MaxNumConcurrentSubTasks() { + final Interval interval = Intervals.of("2017-12/P1M"); + final boolean appendToExisting = false; final ParallelIndexSupervisorTask task = newTask( - Intervals.of("2017-12/P1M"), + interval, Granularities.DAY, - false, + appendToExisting, true, new ParallelIndexTuningConfig( null, @@ -266,6 +349,7 @@ public void testWith1MaxNumConcurrentSubTasks() task.addToContext(Tasks.FORCE_TIME_CHUNK_LOCK_KEY, lockGranularity == LockGranularity.TIME_CHUNK); Assert.assertEquals(TaskState.SUCCESS, getIndexingServiceClient().runAndWait(task).getStatusCode()); Assert.assertNull("Runner must be null if the task was in the sequential mode", task.getCurrentRunner()); + assertShardSpec(interval, appendToExisting); } @Test diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelper.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelper.java new file mode 100644 index 000000000000..76093beb8c8a --- /dev/null +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelper.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.realtime.appenderator; + +import org.apache.druid.java.util.common.ISE; +import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.partition.BuildingNumberedShardSpec; +import org.apache.druid.timeline.partition.OverwriteShardSpec; +import org.apache.druid.timeline.partition.ShardSpec; +import org.joda.time.Interval; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +public final class SegmentPublisherHelper +{ + /** + * This method fills missing information in the shard spec if necessary when publishing segments. + * + * - When time chunk lock is used, the non-appending task should set the proper size of the core partitions for + * dynamically-partitioned segments. See {@link #annotateNumberedShardSpecFn}. + * - When segment lock is used, the overwriting task should set the proper size of the atomic update group. + * See {@link #annotateAtomicUpdateGroupFn}. + */ + static Set annotateShardSpec(Set segments) + { + final Map> intervalToSegments = new HashMap<>(); + segments.forEach( + segment -> intervalToSegments.computeIfAbsent(segment.getInterval(), k -> new ArrayList<>()).add(segment) + ); + + for (Entry> entry : intervalToSegments.entrySet()) { + final Interval interval = entry.getKey(); + final List segmentsPerInterval = entry.getValue(); + final ShardSpec firstShardSpec = segmentsPerInterval.get(0).getShardSpec(); + final boolean anyMismatch = segmentsPerInterval.stream().anyMatch( + segment -> segment.getShardSpec().getClass() != firstShardSpec.getClass() + ); + if (anyMismatch) { + throw new ISE( + "Mismatched shardSpecs in interval[%s] for segments[%s]", + interval, + segmentsPerInterval + ); + } + final Function annotateFn; + if (firstShardSpec instanceof OverwriteShardSpec) { + annotateFn = annotateAtomicUpdateGroupFn(segmentsPerInterval.size()); + } else if (firstShardSpec instanceof BuildingNumberedShardSpec) { + annotateFn = annotateNumberedShardSpecFn(segmentsPerInterval.size()); + } else { + annotateFn = null; + } + + if (annotateFn != null) { + intervalToSegments.put(interval, segmentsPerInterval.stream().map(annotateFn).collect(Collectors.toList())); + } + } + + return intervalToSegments.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); + } + + private static Function annotateAtomicUpdateGroupFn(int atomicUpdateGroupSize) + { + // The segments which are published together consist an atomicUpdateGroup. + return segment -> { + final OverwriteShardSpec shardSpec = (OverwriteShardSpec) segment.getShardSpec(); + return segment.withShardSpec(shardSpec.withAtomicUpdateGroupSize((short) atomicUpdateGroupSize)); + }; + } + + private static Function annotateNumberedShardSpecFn(int corePartitionSetSize) + { + return segment -> { + final BuildingNumberedShardSpec shardSpec = (BuildingNumberedShardSpec) segment.getShardSpec(); + return segment.withShardSpec(shardSpec.toNumberedShardSpec(corePartitionSetSize)); + }; + } + + private SegmentPublisherHelper() + { + } +} diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/TransactionalSegmentPublisher.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/TransactionalSegmentPublisher.java index cb9b9ffb4448..26d8fac9dff5 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/TransactionalSegmentPublisher.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/TransactionalSegmentPublisher.java @@ -20,21 +20,11 @@ package org.apache.druid.segment.realtime.appenderator; import org.apache.druid.indexing.overlord.SegmentPublishResult; -import org.apache.druid.java.util.common.ISE; import org.apache.druid.timeline.DataSegment; -import org.apache.druid.timeline.partition.OverwriteShardSpec; -import org.joda.time.Interval; import javax.annotation.Nullable; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import java.util.Set; -import java.util.stream.Collectors; public interface TransactionalSegmentPublisher { @@ -63,7 +53,7 @@ default SegmentPublishResult publishSegments( { return publishAnnotatedSegments( segmentsToBeOverwritten, - annotateAtomicUpdateGroupSize(segmentsToPublish), + SegmentPublisherHelper.annotateShardSpec(segmentsToPublish), commitMetadata ); } @@ -76,52 +66,4 @@ default boolean supportsEmptyPublish() { return false; } - - static Set annotateAtomicUpdateGroupSize(Set segments) - { - final Map> intervalToSegments = new HashMap<>(); - segments.forEach( - segment -> intervalToSegments.computeIfAbsent(segment.getInterval(), k -> new ArrayList<>()).add(segment) - ); - - for (Entry> entry : intervalToSegments.entrySet()) { - final Interval interval = entry.getKey(); - final List segmentsPerInterval = entry.getValue(); - final boolean isNonFirstGeneration = segmentsPerInterval.get(0).getShardSpec() instanceof OverwriteShardSpec; - - final boolean anyMismatch = segmentsPerInterval.stream().anyMatch( - segment -> (segment.getShardSpec() instanceof OverwriteShardSpec) != isNonFirstGeneration - ); - if (anyMismatch) { - throw new ISE( - "WTH? some segments have empty overshadwedSegments but others are not? " - + "segments with non-overwritingShardSpec: [%s]," - + "segments with overwritingShardSpec: [%s]", - segmentsPerInterval.stream() - .filter(segment -> !(segment.getShardSpec() instanceof OverwriteShardSpec)) - .collect(Collectors.toList()), - segmentsPerInterval.stream() - .filter(segment -> segment.getShardSpec() instanceof OverwriteShardSpec) - .collect(Collectors.toList()) - ); - } - - if (isNonFirstGeneration) { - // The segments which are published together consist an atomicUpdateGroup. - - intervalToSegments.put( - interval, - segmentsPerInterval - .stream() - .map(segment -> { - final OverwriteShardSpec shardSpec = (OverwriteShardSpec) segment.getShardSpec(); - return segment.withShardSpec(shardSpec.withAtomicUpdateGroupSize((short) segmentsPerInterval.size())); - }) - .collect(Collectors.toList()) - ); - } - } - - return intervalToSegments.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); - } } From 42b50b1796a66a911939a74ef8bb8dbaa254690f Mon Sep 17 00:00:00 2001 From: Suneet Saldanha <44787917+suneet-s@users.noreply.github.com> Date: Mon, 15 Jun 2020 10:47:57 -0700 Subject: [PATCH 082/107] lpad and rpad functions match postrges behavior in SQL compatible mode (#10006) * lpad and rpad functions deal with empty pad Return null if the pad string used by the `lpad` and `rpad` functions is an empty string * Fix rpad * Match PostgreSQL behavior in SQL compliant null handling mode * Match PostgreSQL behavior for pad -ve len * address review comments --- .../druid/java/util/common/StringUtils.java | 62 ++++++++++++------- .../java/util/common/StringUtilsTest.java | 59 ++++++++++-------- .../apache/druid/math/expr/FunctionTest.java | 39 ++++++++++-- docs/misc/math-expr.md | 4 +- docs/querying/sql.md | 4 +- 5 files changed, 109 insertions(+), 59 deletions(-) diff --git a/core/src/main/java/org/apache/druid/java/util/common/StringUtils.java b/core/src/main/java/org/apache/druid/java/util/common/StringUtils.java index 33a4e3c74d58..bf9540015d33 100644 --- a/core/src/main/java/org/apache/druid/java/util/common/StringUtils.java +++ b/core/src/main/java/org/apache/druid/java/util/common/StringUtils.java @@ -21,6 +21,7 @@ import com.google.common.base.Strings; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; @@ -472,28 +473,34 @@ public static String repeat(String s, int count) /** * Returns the string left-padded with the string pad to a length of len characters. * If str is longer than len, the return value is shortened to len characters. - * Lpad and rpad functions are migrated from flink's scala function with minor refactor + * This function is migrated from flink's scala function with minor refactor * https://github.com/apache/flink/blob/master/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/runtime/functions/ScalarFunctions.scala + * - Modified to handle empty pad string. + * - Padding of negative length return an empty string. * * @param base The base string to be padded * @param len The length of padded string * @param pad The pad string * - * @return the string left-padded with pad to a length of len + * @return the string left-padded with pad to a length of len or null if the pad is empty or the len is less than 0. */ - public static String lpad(String base, Integer len, String pad) + @Nonnull + public static String lpad(@Nonnull String base, int len, @Nonnull String pad) { - if (len < 0) { - return null; - } else if (len == 0) { + if (len <= 0) { return ""; } - char[] data = new char[len]; - // The length of the padding needed int pos = Math.max(len - base.length(), 0); + // short-circuit if there is no pad and we need to add a padding + if (pos > 0 && pad.isEmpty()) { + return base; + } + + char[] data = new char[len]; + // Copy the padding for (int i = 0; i < pos; i += pad.length()) { for (int j = 0; j < pad.length() && j < pos - i; j++) { @@ -512,37 +519,48 @@ public static String lpad(String base, Integer len, String pad) /** * Returns the string right-padded with the string pad to a length of len characters. * If str is longer than len, the return value is shortened to len characters. + * This function is migrated from flink's scala function with minor refactor + * https://github.com/apache/flink/blob/master/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/runtime/functions/ScalarFunctions.scala + * - Modified to handle empty pad string. + * - Modified to only copy the pad string if needed (this implementation mimics lpad). + * - Padding of negative length return an empty string. * * @param base The base string to be padded * @param len The length of padded string * @param pad The pad string * - * @return the string right-padded with pad to a length of len + * @return the string right-padded with pad to a length of len or null if the pad is empty or the len is less than 0. */ - public static String rpad(String base, Integer len, String pad) + @Nonnull + public static String rpad(@Nonnull String base, int len, @Nonnull String pad) { - if (len < 0) { - return null; - } else if (len == 0) { + if (len <= 0) { return ""; } - char[] data = new char[len]; - - int pos = 0; + // The length of the padding needed + int paddingLen = Math.max(len - base.length(), 0); - // Copy the base - for (; pos < base.length() && pos < len; pos++) { - data[pos] = base.charAt(pos); + // short-circuit if there is no pad and we need to add a padding + if (paddingLen > 0 && pad.isEmpty()) { + return base; } + char[] data = new char[len]; + + // Copy the padding - for (; pos < len; pos += pad.length()) { - for (int i = 0; i < pad.length() && i < len - pos; i++) { - data[pos + i] = pad.charAt(i); + for (int i = len - paddingLen; i < len; i += pad.length()) { + for (int j = 0; j < pad.length() && i + j < data.length; j++) { + data[i + j] = pad.charAt(j); } } + // Copy the base + for (int i = 0; i < len && i < base.length(); i++) { + data[i] = base.charAt(i); + } + return new String(data); } diff --git a/core/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java b/core/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java index 1d4cb6dbade9..bd5c0b2ba6c1 100644 --- a/core/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java +++ b/core/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java @@ -133,13 +133,6 @@ public void fromUtf8ByteBufferDirect() Assert.assertEquals("abcd", StringUtils.fromUtf8(bytes)); } - @Test - public void testCharsetShowsUpAsDeprecated() - { - // Not actually a runnable test, just checking the IDE - Assert.assertNotNull(StringUtils.UTF8_CHARSET); - } - @SuppressWarnings("MalformedFormatString") @Test public void testNonStrictFormat() @@ -211,39 +204,51 @@ public void testRepeat() @Test public void testLpad() { - String s1 = StringUtils.lpad("abc", 7, "de"); - Assert.assertEquals(s1, "dedeabc"); + String lpad = StringUtils.lpad("abc", 7, "de"); + Assert.assertEquals("dedeabc", lpad); - String s2 = StringUtils.lpad("abc", 6, "de"); - Assert.assertEquals(s2, "dedabc"); + lpad = StringUtils.lpad("abc", 6, "de"); + Assert.assertEquals("dedabc", lpad); - String s3 = StringUtils.lpad("abc", 2, "de"); - Assert.assertEquals(s3, "ab"); + lpad = StringUtils.lpad("abc", 2, "de"); + Assert.assertEquals("ab", lpad); - String s4 = StringUtils.lpad("abc", 0, "de"); - Assert.assertEquals(s4, ""); + lpad = StringUtils.lpad("abc", 0, "de"); + Assert.assertEquals("", lpad); - String s5 = StringUtils.lpad("abc", -1, "de"); - Assert.assertEquals(s5, null); + lpad = StringUtils.lpad("abc", -1, "de"); + Assert.assertEquals("", lpad); + + lpad = StringUtils.lpad("abc", 10, ""); + Assert.assertEquals("abc", lpad); + + lpad = StringUtils.lpad("abc", 1, ""); + Assert.assertEquals("a", lpad); } @Test public void testRpad() { - String s1 = StringUtils.rpad("abc", 7, "de"); - Assert.assertEquals(s1, "abcdede"); + String rpad = StringUtils.rpad("abc", 7, "de"); + Assert.assertEquals("abcdede", rpad); + + rpad = StringUtils.rpad("abc", 6, "de"); + Assert.assertEquals("abcded", rpad); + + rpad = StringUtils.rpad("abc", 2, "de"); + Assert.assertEquals("ab", rpad); - String s2 = StringUtils.rpad("abc", 6, "de"); - Assert.assertEquals(s2, "abcded"); + rpad = StringUtils.rpad("abc", 0, "de"); + Assert.assertEquals("", rpad); - String s3 = StringUtils.rpad("abc", 2, "de"); - Assert.assertEquals(s3, "ab"); + rpad = StringUtils.rpad("abc", -1, "de"); + Assert.assertEquals("", rpad); - String s4 = StringUtils.rpad("abc", 0, "de"); - Assert.assertEquals(s4, ""); + rpad = StringUtils.rpad("abc", 10, ""); + Assert.assertEquals("abc", rpad); - String s5 = StringUtils.rpad("abc", -1, "de"); - Assert.assertEquals(s5, null); + rpad = StringUtils.rpad("abc", 1, ""); + Assert.assertEquals("a", rpad); } @Test diff --git a/core/src/test/java/org/apache/druid/math/expr/FunctionTest.java b/core/src/test/java/org/apache/druid/math/expr/FunctionTest.java index 2fe52490c400..fc83b4f7dcdf 100644 --- a/core/src/test/java/org/apache/druid/math/expr/FunctionTest.java +++ b/core/src/test/java/org/apache/druid/math/expr/FunctionTest.java @@ -146,9 +146,23 @@ public void testLpad() assertExpr("lpad(x, 5, 'ab')", "abfoo"); assertExpr("lpad(x, 4, 'ab')", "afoo"); assertExpr("lpad(x, 2, 'ab')", "fo"); - assertArrayExpr("lpad(x, 0, 'ab')", null); - assertArrayExpr("lpad(x, 5, null)", null); - assertArrayExpr("lpad(null, 5, x)", null); + assertExpr("lpad(x, -1, 'ab')", NullHandling.replaceWithDefault() ? null : ""); + assertExpr("lpad(null, 5, 'ab')", null); + assertExpr("lpad(x, 2, '')", NullHandling.replaceWithDefault() ? null : "fo"); + assertExpr("lpad(x, 6, '')", NullHandling.replaceWithDefault() ? null : "foo"); + assertExpr("lpad('', 3, '*')", NullHandling.replaceWithDefault() ? null : "***"); + assertExpr("lpad(x, 2, null)", null); + assertExpr("lpad(a, 4, '*')", "[foo"); + assertExpr("lpad(a, 2, '*')", "[f"); + assertExpr("lpad(a, 2, '')", NullHandling.replaceWithDefault() ? null : "[f"); + assertExpr("lpad(b, 4, '*')", "[1, "); + assertExpr("lpad(b, 2, '')", NullHandling.replaceWithDefault() ? null : "[1"); + assertExpr("lpad(b, 2, null)", null); + assertExpr("lpad(x, 5, x)", "fofoo"); + assertExpr("lpad(x, 5, y)", "22foo"); + assertExpr("lpad(x, 5, z)", "3.foo"); + assertExpr("lpad(y, 5, x)", "foof2"); + assertExpr("lpad(z, 5, y)", "223.1"); } @Test @@ -157,9 +171,22 @@ public void testRpad() assertExpr("rpad(x, 5, 'ab')", "fooab"); assertExpr("rpad(x, 4, 'ab')", "fooa"); assertExpr("rpad(x, 2, 'ab')", "fo"); - assertArrayExpr("rpad(x, 0, 'ab')", null); - assertArrayExpr("rpad(x, 5, null)", null); - assertArrayExpr("rpad(null, 5, x)", null); + assertExpr("rpad(x, -1, 'ab')", NullHandling.replaceWithDefault() ? null : ""); + assertExpr("rpad(null, 5, 'ab')", null); + assertExpr("rpad(x, 2, '')", NullHandling.replaceWithDefault() ? null : "fo"); + assertExpr("rpad(x, 6, '')", NullHandling.replaceWithDefault() ? null : "foo"); + assertExpr("rpad('', 3, '*')", NullHandling.replaceWithDefault() ? null : "***"); + assertExpr("rpad(x, 2, null)", null); + assertExpr("rpad(a, 2, '*')", "[f"); + assertExpr("rpad(a, 2, '')", NullHandling.replaceWithDefault() ? null : "[f"); + assertExpr("rpad(b, 4, '*')", "[1, "); + assertExpr("rpad(b, 2, '')", NullHandling.replaceWithDefault() ? null : "[1"); + assertExpr("rpad(b, 2, null)", null); + assertExpr("rpad(x, 5, x)", "foofo"); + assertExpr("rpad(x, 5, y)", "foo22"); + assertExpr("rpad(x, 5, z)", "foo3."); + assertExpr("rpad(y, 5, x)", "2foof"); + assertExpr("rpad(z, 5, y)", "3.122"); } @Test diff --git a/docs/misc/math-expr.md b/docs/misc/math-expr.md index 249eb1ef209c..709715ac9c1e 100644 --- a/docs/misc/math-expr.md +++ b/docs/misc/math-expr.md @@ -91,8 +91,8 @@ The following built-in functions are available. |upper|upper(expr) converts a string to uppercase| |reverse|reverse(expr) reverses a string| |repeat|repeat(expr, N) repeats a string N times| -|lpad|lpad(expr, length, chars) returns a string of `length` from `expr` left-padded with `chars`. If `length` is shorter than the length of `expr`, the result is `expr` which is truncated to `length`. If either `expr` or `chars` are null, the result will be null.| -|rpad|rpad(expr, length, chars) returns a string of `length` from `expr` right-padded with `chars`. If `length` is shorter than the length of `expr`, the result is `expr` which is truncated to `length`. If either `expr` or `chars` are null, the result will be null.| +|lpad|lpad(expr, length, chars) returns a string of `length` from `expr` left-padded with `chars`. If `length` is shorter than the length of `expr`, the result is `expr` which is truncated to `length`. The result will be null if either `expr` or `chars` is null. If `chars` is an empty string, no padding is added, however `expr` may be trimmed if necessary.| +|rpad|rpad(expr, length, chars) returns a string of `length` from `expr` right-padded with `chars`. If `length` is shorter than the length of `expr`, the result is `expr` which is truncated to `length`. The result will be null if either `expr` or `chars` is null. If `chars` is an empty string, no padding is added, however `expr` may be trimmed if necessary.| ## Time functions diff --git a/docs/querying/sql.md b/docs/querying/sql.md index dff5c7176e1f..1f6e7e3ab716 100644 --- a/docs/querying/sql.md +++ b/docs/querying/sql.md @@ -337,8 +337,8 @@ String functions accept strings, and return a type appropriate to the function. |`UPPER(expr)`|Returns expr in all uppercase.| |`REVERSE(expr)`|Reverses expr.| |`REPEAT(expr, [N])`|Repeats expr N times| -|`LPAD(expr, length[, chars])`|Returns a string of "length" from "expr" left-padded with "chars". If "length" is shorter than the length of "expr", the result is "expr" which is truncated to "length". If either "expr" or "chars" are null, the result will be null.| -|`RPAD(expr, length[, chars])`|Returns a string of "length" from "expr" right-padded with "chars". If "length" is shorter than the length of "expr", the result is "expr" which is truncated to "length". If either "expr" or "chars" are null, the result will be null.| +|`LPAD(expr, length[, chars])`|Returns a string of `length` from `expr` left-padded with `chars`. If `length` is shorter than the length of `expr`, the result is `expr` which is truncated to `length`. The result will be null if either `expr` or `chars` is null. If `chars` is an empty string, no padding is added, however `expr` may be trimmed if necessary.| +|`RPAD(expr, length[, chars])`|Returns a string of `length` from `expr` right-padded with `chars`. If `length` is shorter than the length of `expr`, the result is `expr` which is truncated to `length`. The result will be null if either `expr` or `chars` is null. If `chars` is an empty string, no padding is added, however `expr` may be trimmed if necessary.| ### Time functions From 346276b86028c56002039cb175634b8fd87f24fb Mon Sep 17 00:00:00 2001 From: agricenko Date: Tue, 16 Jun 2020 03:48:34 +0300 Subject: [PATCH 083/107] Integration test docker compose readme (#10016) * Integration Tests. Docker-compose readme part * Readme updates. PR fixes Co-authored-by: agritsenko --- integration-tests/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/integration-tests/README.md b/integration-tests/README.md index afb123a67490..d5476cda8a39 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -68,6 +68,27 @@ can either be 8 or 11. Druid's configuration (using Docker) can be overrided by providing -Doverride.config.path=. The file must contain one property per line, the key must start with `druid_` and the format should be snake case. +## Docker compose + +Docker compose yamls located in "docker" folder + +docker-compose.base.yml - Base file that defines all containers for integration test + +docker-compose.yml - Defines minimal Druid cluster that can be used for non cluster tests + + docker-compose -f docker-compose.yml up + // DRUID_INTEGRATION_TEST_GROUP - this variable is used in Druid docker container for "security" and "query" test group. Use next docker-compose if you want to run security/query tests. + DRUID_INTEGRATION_TEST_GROUP=security docker-compose -f docker-compose.yml up + +docker-compose.override-env.yml - the same configuration as docker-compose.yml + override-env variable that needed to run cloud tests + + // OVERRIDE_ENV - variable that must contains path to Druid configuration file + OVERRIDE_ENV=./environment-configs/override-examples/s3 docker-compose -f docker-compose.override-env.yml up + +docker-compose.druid-hadoop.yml - for starting Apache Hadoop 2.8.5 cluster with the same setup as the Druid tutorial + + docker-compose -f docker-compose.druid-hadoop.yml up + ## Manual bringing up docker containers and running tests 1. Build druid-cluster, druid-hadoop docker images. From root module run maven command: From d18398763f92a62ffe6785d20b1a2c8f4c73ba4a Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Mon, 15 Jun 2020 19:56:49 -0700 Subject: [PATCH 084/107] make phaser of ReferenceCountingCloseableObject protected instead of private so subclasses can do stuff with it (#10035) --- .../apache/druid/segment/ReferenceCountingCloseableObject.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processing/src/main/java/org/apache/druid/segment/ReferenceCountingCloseableObject.java b/processing/src/main/java/org/apache/druid/segment/ReferenceCountingCloseableObject.java index db37fc595684..af0e1c7a3770 100644 --- a/processing/src/main/java/org/apache/druid/segment/ReferenceCountingCloseableObject.java +++ b/processing/src/main/java/org/apache/druid/segment/ReferenceCountingCloseableObject.java @@ -41,7 +41,7 @@ public abstract class ReferenceCountingCloseableObject Date: Tue, 16 Jun 2020 14:40:35 -0700 Subject: [PATCH 085/107] Remove LegacyDataSource. (#10037) * Remove LegacyDataSource. Its purpose was to enable deserialization of strings into TableDataSources. But we can do this more straightforwardly with Jackson annotations. * Slight test improvement. --- .../org/apache/druid/query/DataSource.java | 2 +- .../apache/druid/query/LegacyDataSource.java | 33 --------------- .../apache/druid/query/TableDataSource.java | 6 +++ .../druid/query/TableDataSourceTest.java | 41 +++++++++++++++---- .../query/metadata/SegmentAnalyzerTest.java | 4 +- .../server/log/FilteredRequestLoggerTest.java | 4 +- .../server/log/LoggingRequestLoggerTest.java | 6 +-- 7 files changed, 46 insertions(+), 50 deletions(-) delete mode 100644 processing/src/main/java/org/apache/druid/query/LegacyDataSource.java diff --git a/processing/src/main/java/org/apache/druid/query/DataSource.java b/processing/src/main/java/org/apache/druid/query/DataSource.java index 549b06b0b3eb..b78a45fc36c6 100644 --- a/processing/src/main/java/org/apache/druid/query/DataSource.java +++ b/processing/src/main/java/org/apache/druid/query/DataSource.java @@ -28,7 +28,7 @@ /** * Represents a source... of data... for a query. Analogous to the "FROM" clause in SQL. */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = LegacyDataSource.class) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = TableDataSource.class) @JsonSubTypes({ @JsonSubTypes.Type(value = TableDataSource.class, name = "table"), @JsonSubTypes.Type(value = QueryDataSource.class, name = "query"), diff --git a/processing/src/main/java/org/apache/druid/query/LegacyDataSource.java b/processing/src/main/java/org/apache/druid/query/LegacyDataSource.java deleted file mode 100644 index d3812489be20..000000000000 --- a/processing/src/main/java/org/apache/druid/query/LegacyDataSource.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonTypeName; - -@JsonTypeName("table") -public class LegacyDataSource extends TableDataSource -{ - @JsonCreator - public LegacyDataSource(String name) - { - super(name); - } -} diff --git a/processing/src/main/java/org/apache/druid/query/TableDataSource.java b/processing/src/main/java/org/apache/druid/query/TableDataSource.java index 4c371cf84510..f9a07a181ee4 100644 --- a/processing/src/main/java/org/apache/druid/query/TableDataSource.java +++ b/processing/src/main/java/org/apache/druid/query/TableDataSource.java @@ -40,6 +40,12 @@ public TableDataSource(@JsonProperty("name") String name) this.name = Preconditions.checkNotNull(name, "'name' must be nonnull"); } + @JsonCreator + public static TableDataSource create(final String name) + { + return new TableDataSource(name); + } + @JsonProperty public String getName() { diff --git a/processing/src/test/java/org/apache/druid/query/TableDataSourceTest.java b/processing/src/test/java/org/apache/druid/query/TableDataSourceTest.java index ef50f3e45d9d..ec8d27d29029 100644 --- a/processing/src/test/java/org/apache/druid/query/TableDataSourceTest.java +++ b/processing/src/test/java/org/apache/druid/query/TableDataSourceTest.java @@ -36,6 +36,7 @@ public class TableDataSourceTest public ExpectedException expectedException = ExpectedException.none(); private final TableDataSource fooDataSource = new TableDataSource("foo"); + private final TableDataSource barDataSource = new TableDataSource("bar"); @Test public void test_getTableNames() @@ -89,27 +90,49 @@ public void test_equals() } @Test - public void test_equals_legacy() + public void test_serde_roundTrip() throws Exception { - final LegacyDataSource legacyFoo = new LegacyDataSource("foo"); - final LegacyDataSource legacyBar = new LegacyDataSource("bar"); + final ObjectMapper jsonMapper = TestHelper.makeJsonMapper(); + final TableDataSource deserialized = (TableDataSource) jsonMapper.readValue( + jsonMapper.writeValueAsString(fooDataSource), + DataSource.class + ); - Assert.assertEquals(legacyFoo, fooDataSource); - Assert.assertEquals(fooDataSource, legacyFoo); + Assert.assertEquals(fooDataSource, deserialized); + Assert.assertNotEquals(barDataSource, deserialized); + } - Assert.assertNotEquals(legacyBar, fooDataSource); - Assert.assertNotEquals(fooDataSource, legacyBar); + @Test + public void test_deserialize_fromObject() throws Exception + { + final ObjectMapper jsonMapper = TestHelper.makeJsonMapper(); + final TableDataSource deserialized = (TableDataSource) jsonMapper.readValue( + "{\"type\":\"table\",\"name\":\"foo\"}", + DataSource.class + ); + + Assert.assertEquals(fooDataSource, deserialized); + Assert.assertNotEquals(barDataSource, deserialized); } @Test - public void test_serde() throws Exception + public void test_deserialize_fromString() throws Exception { final ObjectMapper jsonMapper = TestHelper.makeJsonMapper(); final TableDataSource deserialized = (TableDataSource) jsonMapper.readValue( - jsonMapper.writeValueAsString(fooDataSource), + "\"foo\"", DataSource.class ); Assert.assertEquals(fooDataSource, deserialized); + Assert.assertNotEquals(barDataSource, deserialized); + } + + @Test + public void test_serialize() throws Exception + { + final ObjectMapper jsonMapper = TestHelper.makeJsonMapper(); + final String s = jsonMapper.writeValueAsString(fooDataSource); + Assert.assertEquals("{\"type\":\"table\",\"name\":\"foo\"}", s); } } diff --git a/processing/src/test/java/org/apache/druid/query/metadata/SegmentAnalyzerTest.java b/processing/src/test/java/org/apache/druid/query/metadata/SegmentAnalyzerTest.java index 4f3a81dbb208..66df87904daa 100644 --- a/processing/src/test/java/org/apache/druid/query/metadata/SegmentAnalyzerTest.java +++ b/processing/src/test/java/org/apache/druid/query/metadata/SegmentAnalyzerTest.java @@ -20,11 +20,11 @@ package org.apache.druid.query.metadata; import org.apache.druid.data.input.impl.DimensionSchema; -import org.apache.druid.query.LegacyDataSource; import org.apache.druid.query.QueryPlus; import org.apache.druid.query.QueryRunner; import org.apache.druid.query.QueryRunnerFactory; import org.apache.druid.query.QueryRunnerTestHelper; +import org.apache.druid.query.TableDataSource; import org.apache.druid.query.metadata.metadata.ColumnAnalysis; import org.apache.druid.query.metadata.metadata.SegmentAnalysis; import org.apache.druid.query.metadata.metadata.SegmentMetadataQuery; @@ -190,7 +190,7 @@ private List getSegmentAnalysises(Segment index, EnumSet queryContext = ImmutableMap.of("foo", "bar"); final Query query = new FakeQuery( - new LegacyDataSource("datasource"), + new TableDataSource("datasource"), new QuerySegmentSpec() { @Override @@ -127,7 +127,7 @@ public QueryRunner lookup(Query query, QuerySegmentWalker walker) ); final Query unionQuery = new FakeQuery( - new UnionDataSource(ImmutableList.of(new LegacyDataSource("A"), new LegacyDataSource("B"))), + new UnionDataSource(ImmutableList.of(new TableDataSource("A"), new TableDataSource("B"))), new QuerySegmentSpec() { @Override From 61fe473bfc552aa8ff1f1f4316d2c09aea89a848 Mon Sep 17 00:00:00 2001 From: Suneet Saldanha <44787917+suneet-s@users.noreply.github.com> Date: Tue, 16 Jun 2020 16:09:46 -0700 Subject: [PATCH 086/107] ROUND and having comparators correctly handle special double values (#10014) * ROUND and having comparators correctly handle doubles Double.NaN, Double.POSITIVE_INFINITY and Double.NEGATIVE_INFINITY are not real numbers. Because of this, they can not be converted to BigDecimal and instead throw a NumberFormatException. This change adds support for calculations that produce these numbers either for use in the `ROUND` function or the HavingSpecMetricComparator by not attempting to convert the number to a BigDecimal. The bug in ROUND was first introduced in #7224 where we added the ability to round to any decimal place. This PR changes the behavior back to using `Math.round` if we recognize a number that can not be converted to a BigDecimal. * Add tests and fix spellcheck * update error message in ExpressionsTest * Address comments * fix up round for infinity * round non numeric doubles returns a double * fix spotbugs * Update docs/misc/math-expr.md * Update docs/querying/sql.md --- .../org/apache/druid/math/expr/Function.java | 35 +++- .../apache/druid/math/expr/FunctionTest.java | 157 +++++++++++++++++- docs/misc/math-expr.md | 2 +- docs/querying/sql.md | 2 +- .../having/HavingSpecMetricComparator.java | 9 +- .../HavingSpecMetricComparatorTest.java | 60 +++++++ .../sql/calcite/planner/DruidRexExecutor.java | 5 +- .../druid/sql/calcite/CalciteQueryTest.java | 24 +++ .../calcite/expression/ExpressionsTest.java | 69 +++++++- .../expression/GreatestExpressionTest.java | 17 ++ .../expression/LeastExpressionTest.java | 18 ++ 11 files changed, 382 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/apache/druid/math/expr/Function.java b/core/src/main/java/org/apache/druid/math/expr/Function.java index af52555793b7..a20863929875 100644 --- a/core/src/main/java/org/apache/druid/math/expr/Function.java +++ b/core/src/main/java/org/apache/druid/math/expr/Function.java @@ -694,6 +694,11 @@ protected ExprEval eval(double param) class Round implements Function { + //CHECKSTYLE.OFF: Regexp + private static final BigDecimal MAX_FINITE_VALUE = BigDecimal.valueOf(Double.MAX_VALUE); + private static final BigDecimal MIN_FINITE_VALUE = BigDecimal.valueOf(-1 * Double.MAX_VALUE); + //CHECKSTYLE.ON: Regexp + @Override public String name() { @@ -705,7 +710,11 @@ public ExprEval apply(List args, Expr.ObjectBinding bindings) { ExprEval value1 = args.get(0).eval(bindings); if (value1.type() != ExprType.LONG && value1.type() != ExprType.DOUBLE) { - throw new IAE("The first argument to the function[%s] should be integer or double type but get the %s type", name(), value1.type()); + throw new IAE( + "The first argument to the function[%s] should be integer or double type but got the type: %s", + name(), + value1.type() + ); } if (args.size() == 1) { @@ -713,7 +722,11 @@ public ExprEval apply(List args, Expr.ObjectBinding bindings) } else { ExprEval value2 = args.get(1).eval(bindings); if (value2.type() != ExprType.LONG) { - throw new IAE("The second argument to the function[%s] should be integer type but get the %s type", name(), value2.type()); + throw new IAE( + "The second argument to the function[%s] should be integer type but got the type: %s", + name(), + value2.type() + ); } return eval(value1, value2.asInt()); } @@ -737,11 +750,27 @@ private ExprEval eval(ExprEval param, int scale) if (param.type() == ExprType.LONG) { return ExprEval.of(BigDecimal.valueOf(param.asLong()).setScale(scale, RoundingMode.HALF_UP).longValue()); } else if (param.type() == ExprType.DOUBLE) { - return ExprEval.of(BigDecimal.valueOf(param.asDouble()).setScale(scale, RoundingMode.HALF_UP).doubleValue()); + BigDecimal decimal = safeGetFromDouble(param.asDouble()); + return ExprEval.of(decimal.setScale(scale, RoundingMode.HALF_UP).doubleValue()); } else { return ExprEval.of(null); } } + + /** + * Converts non-finite doubles to BigDecimal values instead of throwing a NumberFormatException. + */ + private static BigDecimal safeGetFromDouble(double val) + { + if (Double.isNaN(val)) { + return BigDecimal.ZERO; + } else if (val == Double.POSITIVE_INFINITY) { + return MAX_FINITE_VALUE; + } else if (val == Double.NEGATIVE_INFINITY) { + return MIN_FINITE_VALUE; + } + return BigDecimal.valueOf(val); + } } class Signum extends UnivariateMathFunction diff --git a/core/src/test/java/org/apache/druid/math/expr/FunctionTest.java b/core/src/test/java/org/apache/druid/math/expr/FunctionTest.java index fc83b4f7dcdf..bd755ba7e0ec 100644 --- a/core/src/test/java/org/apache/druid/math/expr/FunctionTest.java +++ b/core/src/test/java/org/apache/druid/math/expr/FunctionTest.java @@ -20,13 +20,19 @@ package org.apache.druid.math.expr; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import org.apache.druid.common.config.NullHandling; +import org.apache.druid.java.util.common.Pair; import org.apache.druid.testing.InitializedNullHandlingTest; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import javax.annotation.Nullable; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Locale; +import java.util.Set; public class FunctionTest extends InitializedNullHandlingTest { @@ -35,13 +41,23 @@ public class FunctionTest extends InitializedNullHandlingTest @Before public void setup() { - ImmutableMap.Builder builder = ImmutableMap.builder(); - builder.put("x", "foo"); - builder.put("y", 2); - builder.put("z", 3.1); - builder.put("a", new String[] {"foo", "bar", "baz", "foobar"}); - builder.put("b", new Long[] {1L, 2L, 3L, 4L, 5L}); - builder.put("c", new Double[] {3.1, 4.2, 5.3}); + ImmutableMap.Builder builder = ImmutableMap.builder() + .put("x", "foo") + .put("y", 2) + .put("z", 3.1) + .put("d", 34.56D) + .put("maxLong", Long.MAX_VALUE) + .put("minLong", Long.MIN_VALUE) + .put("f", 12.34F) + .put("nan", Double.NaN) + .put("inf", Double.POSITIVE_INFINITY) + .put("-inf", Double.NEGATIVE_INFINITY) + .put("o", 0) + .put("od", 0D) + .put("of", 0F) + .put("a", new String[] {"foo", "bar", "baz", "foobar"}) + .put("b", new Long[] {1L, 2L, 3L, 4L, 5L}) + .put("c", new Double[] {3.1, 4.2, 5.3}); bindings = Parser.withMap(builder.build()); } @@ -320,6 +336,133 @@ public void testArrayPrepend() assertArrayExpr("array_prepend(1, [])", new Double[]{1.0}); } + @Test + public void testRoundWithNonNumericValuesShouldReturn0() + { + assertExpr("round(nan)", 0D); + assertExpr("round(nan, 5)", 0D); + //CHECKSTYLE.OFF: Regexp + assertExpr("round(inf)", Double.MAX_VALUE); + assertExpr("round(inf, 4)", Double.MAX_VALUE); + assertExpr("round(-inf)", -1 * Double.MAX_VALUE); + assertExpr("round(-inf, 3)", -1 * Double.MAX_VALUE); + assertExpr("round(-inf, -5)", -1 * Double.MAX_VALUE); + //CHECKSTYLE.ON: Regexp + + // Calculations that result in non numeric numbers + assertExpr("round(0/od)", 0D); + assertExpr("round(od/od)", 0D); + //CHECKSTYLE.OFF: Regexp + assertExpr("round(1/od)", Double.MAX_VALUE); + assertExpr("round(-1/od)", -1 * Double.MAX_VALUE); + //CHECKSTYLE.ON: Regexp + + assertExpr("round(0/of)", 0D); + assertExpr("round(of/of)", 0D); + //CHECKSTYLE.OFF: Regexp + assertExpr("round(1/of)", Double.MAX_VALUE); + assertExpr("round(-1/of)", -1 * Double.MAX_VALUE); + //CHECKSTYLE.ON: Regexp + } + + @Test + public void testRoundWithLong() + { + assertExpr("round(y)", 2L); + assertExpr("round(y, 2)", 2L); + assertExpr("round(y, -1)", 0L); + } + + @Test + public void testRoundWithDouble() + { + assertExpr("round(d)", 35D); + assertExpr("round(d, 2)", 34.56D); + assertExpr("round(d, y)", 34.56D); + assertExpr("round(d, 1)", 34.6D); + assertExpr("round(d, -1)", 30D); + } + + @Test + public void testRoundWithFloat() + { + assertExpr("round(f)", 12D); + assertExpr("round(f, 2)", 12.34D); + assertExpr("round(f, y)", 12.34D); + assertExpr("round(f, 1)", 12.3D); + assertExpr("round(f, -1)", 10D); + } + + @Test + public void testRoundWithExtremeNumbers() + { + assertExpr("round(maxLong)", BigDecimal.valueOf(Long.MAX_VALUE).setScale(0, RoundingMode.HALF_UP).longValue()); + assertExpr("round(minLong)", BigDecimal.valueOf(Long.MIN_VALUE).setScale(0, RoundingMode.HALF_UP).longValue()); + // overflow + assertExpr("round(maxLong + 1, 1)", BigDecimal.valueOf(Long.MIN_VALUE).setScale(1, RoundingMode.HALF_UP).longValue()); + // underflow + assertExpr("round(minLong - 1, -2)", BigDecimal.valueOf(Long.MAX_VALUE).setScale(-2, RoundingMode.HALF_UP).longValue()); + + assertExpr("round(CAST(maxLong, 'DOUBLE') + 1, 1)", BigDecimal.valueOf(((double) Long.MAX_VALUE) + 1).setScale(1, RoundingMode.HALF_UP).doubleValue()); + assertExpr("round(CAST(minLong, 'DOUBLE') - 1, -2)", BigDecimal.valueOf(((double) Long.MIN_VALUE) - 1).setScale(-2, RoundingMode.HALF_UP).doubleValue()); + } + + @Test + public void testRoundWithInvalidFirstArgument() + { + Set> invalidArguments = ImmutableSet.of( + Pair.of("b", "LONG_ARRAY"), + Pair.of("x", "STRING"), + Pair.of("c", "DOUBLE_ARRAY"), + Pair.of("a", "STRING_ARRAY") + + ); + for (Pair argAndType : invalidArguments) { + try { + assertExpr(String.format(Locale.ENGLISH, "round(%s)", argAndType.lhs), null); + Assert.fail("Did not throw IllegalArgumentException"); + } + catch (IllegalArgumentException e) { + Assert.assertEquals( + String.format( + Locale.ENGLISH, + "The first argument to the function[round] should be integer or double type but got the type: %s", + argAndType.rhs + ), + e.getMessage() + ); + } + } + } + + @Test + public void testRoundWithInvalidSecondArgument() + { + Set> invalidArguments = ImmutableSet.of( + Pair.of("1.2", "DOUBLE"), + Pair.of("x", "STRING"), + Pair.of("a", "STRING_ARRAY"), + Pair.of("c", "DOUBLE_ARRAY") + + ); + for (Pair argAndType : invalidArguments) { + try { + assertExpr(String.format(Locale.ENGLISH, "round(d, %s)", argAndType.lhs), null); + Assert.fail("Did not throw IllegalArgumentException"); + } + catch (IllegalArgumentException e) { + Assert.assertEquals( + String.format( + Locale.ENGLISH, + "The second argument to the function[round] should be integer type but got the type: %s", + argAndType.rhs + ), + e.getMessage() + ); + } + } + } + @Test public void testGreatest() { diff --git a/docs/misc/math-expr.md b/docs/misc/math-expr.md index 709715ac9c1e..dc356479ad58 100644 --- a/docs/misc/math-expr.md +++ b/docs/misc/math-expr.md @@ -141,7 +141,7 @@ See javadoc of java.lang.Math for detailed explanation for each function. |pow|pow(x, y) would return the value of the x raised to the power of y| |remainder|remainder(x, y) would return the remainder operation on two arguments as prescribed by the IEEE 754 standard| |rint|rint(x) would return value that is closest in value to x and is equal to a mathematical integer| -|round|round(x, y) would return the value of the x rounded to the y decimal places. While x can be an integer or floating-point number, y must be an integer. The type of the return value is specified by that of x. y defaults to 0 if omitted. When y is negative, x is rounded on the left side of the y decimal points.| +|round|round(x, y) would return the value of the x rounded to the y decimal places. While x can be an integer or floating-point number, y must be an integer. The type of the return value is specified by that of x. y defaults to 0 if omitted. When y is negative, x is rounded on the left side of the y decimal points. If x is `NaN`, x will return 0. If x is infinity, x will be converted to the nearest finite double. | |scalb|scalb(d, sf) would return d * 2^sf rounded as if performed by a single correctly rounded floating-point multiply to a member of the double value set| |signum|signum(x) would return the signum function of the argument x| |sin|sin(x) would return the trigonometric sine of an angle x| diff --git a/docs/querying/sql.md b/docs/querying/sql.md index 1f6e7e3ab716..acbc3ae0e726 100644 --- a/docs/querying/sql.md +++ b/docs/querying/sql.md @@ -287,7 +287,7 @@ to FLOAT. At runtime, Druid will widen 32-bit floats to 64-bit for most expressi |`SQRT(expr)`|Square root.| |`TRUNCATE(expr[, digits])`|Truncate expr to a specific number of decimal digits. If digits is negative, then this truncates that many places to the left of the decimal point. Digits defaults to zero if not specified.| |`TRUNC(expr[, digits])`|Synonym for `TRUNCATE`.| -|`ROUND(expr[, digits])`|`ROUND(x, y)` would return the value of the x rounded to the y decimal places. While x can be an integer or floating-point number, y must be an integer. The type of the return value is specified by that of x. y defaults to 0 if omitted. When y is negative, x is rounded on the left side of the y decimal points.| +|`ROUND(expr[, digits])`|`ROUND(x, y)` would return the value of the x rounded to the y decimal places. While x can be an integer or floating-point number, y must be an integer. The type of the return value is specified by that of x. y defaults to 0 if omitted. When y is negative, x is rounded on the left side of the y decimal points. If `expr` evaluates to either `NaN`, `expr` will be converted to 0. If `expr` is infinity, `expr` will be converted to the nearest finite double. | |`x + y`|Addition.| |`x - y`|Subtraction.| |`x * y`|Multiplication.| diff --git a/processing/src/main/java/org/apache/druid/query/groupby/having/HavingSpecMetricComparator.java b/processing/src/main/java/org/apache/druid/query/groupby/having/HavingSpecMetricComparator.java index 88f50efb8488..e3f786e1f8aa 100644 --- a/processing/src/main/java/org/apache/druid/query/groupby/having/HavingSpecMetricComparator.java +++ b/processing/src/main/java/org/apache/druid/query/groupby/having/HavingSpecMetricComparator.java @@ -19,6 +19,7 @@ package org.apache.druid.query.groupby.having; +import com.google.common.annotations.VisibleForTesting; import com.google.common.primitives.Doubles; import com.google.common.primitives.Longs; import org.apache.druid.java.util.common.ISE; @@ -89,10 +90,16 @@ static int compare(String aggregationName, Number value, Map Date: Tue, 16 Jun 2020 17:58:05 -0700 Subject: [PATCH 087/107] global table datasource for broadcast segments (#10020) * global table datasource for broadcast segments * tests * fix * fix test * comments and javadocs * review stuffs * use generated equals and hashcode --- .../org/apache/druid/query/DataSource.java | 3 +- .../druid/query/GlobalTableDataSource.java | 58 +++ .../apache/druid/query/TableDataSource.java | 17 +- .../apache/druid/query/DataSourceTest.java | 1 - .../query/GlobalTableDataSourceTest.java | 67 ++++ .../druid/query/TableDataSourceTest.java | 2 +- .../apache/druid/client/BrokerServerView.java | 57 +-- .../druid/server/LocalQuerySegmentWalker.java | 1 + .../apache/druid/server/SegmentManager.java | 6 + .../druid/server/SegmentManagerTest.java | 26 +- .../druid/sql/calcite/schema/DruidSchema.java | 336 +++++++++--------- .../schema/DruidCalciteSchemaModuleTest.java | 4 + .../schema/DruidSchemaNoDataInitTest.java | 4 + .../sql/calcite/schema/DruidSchemaTest.java | 125 +++++-- .../sql/calcite/schema/SystemSchemaTest.java | 3 + .../druid/sql/calcite/util/CalciteTests.java | 4 + .../calcite/util/TestServerInventoryView.java | 78 +++- 17 files changed, 547 insertions(+), 245 deletions(-) create mode 100644 processing/src/main/java/org/apache/druid/query/GlobalTableDataSource.java create mode 100644 processing/src/test/java/org/apache/druid/query/GlobalTableDataSourceTest.java diff --git a/processing/src/main/java/org/apache/druid/query/DataSource.java b/processing/src/main/java/org/apache/druid/query/DataSource.java index b78a45fc36c6..c3edd1c79647 100644 --- a/processing/src/main/java/org/apache/druid/query/DataSource.java +++ b/processing/src/main/java/org/apache/druid/query/DataSource.java @@ -35,7 +35,8 @@ @JsonSubTypes.Type(value = UnionDataSource.class, name = "union"), @JsonSubTypes.Type(value = JoinDataSource.class, name = "join"), @JsonSubTypes.Type(value = LookupDataSource.class, name = "lookup"), - @JsonSubTypes.Type(value = InlineDataSource.class, name = "inline") + @JsonSubTypes.Type(value = InlineDataSource.class, name = "inline"), + @JsonSubTypes.Type(value = GlobalTableDataSource.class, name = "globalTable") }) public interface DataSource { diff --git a/processing/src/main/java/org/apache/druid/query/GlobalTableDataSource.java b/processing/src/main/java/org/apache/druid/query/GlobalTableDataSource.java new file mode 100644 index 000000000000..da5f1390ca3d --- /dev/null +++ b/processing/src/main/java/org/apache/druid/query/GlobalTableDataSource.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; + +/** + * {@link TableDataSource} variant for globally available 'broadcast' segments. If bound to a + * {@link org.apache.druid.segment.join.JoinableFactory} that can create an + * {@link org.apache.druid.segment.join.table.IndexedTable} using DruidBinders.joinableFactoryBinder, this allows + * optimal usage of segments using this DataSource type in join operations (because they are global), and so can be + * pushed down to historicals as a {@link JoinDataSource}, instead of requiring a subquery join using + * {@link InlineDataSource} to construct an {@link org.apache.druid.segment.join.table.IndexedTable} on the fly on the + * broker. Because it is also a {@link TableDataSource}, when queried directly, or on the left hand side of a join, + * they will be treated as any normal table datasource. + */ +@JsonTypeName("globalTable") +public class GlobalTableDataSource extends TableDataSource +{ + @JsonCreator + public GlobalTableDataSource(@JsonProperty("name") String name) + { + super(name); + } + + @Override + public boolean isGlobal() + { + return true; + } + + @Override + public String toString() + { + return "GlobalTableDataSource{" + + "name='" + getName() + '\'' + + '}'; + } +} diff --git a/processing/src/main/java/org/apache/druid/query/TableDataSource.java b/processing/src/main/java/org/apache/druid/query/TableDataSource.java index f9a07a181ee4..469d5be21719 100644 --- a/processing/src/main/java/org/apache/druid/query/TableDataSource.java +++ b/processing/src/main/java/org/apache/druid/query/TableDataSource.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; @JsonTypeName("table") @@ -99,27 +100,21 @@ public String toString() } @Override - public final boolean equals(Object o) + public boolean equals(Object o) { if (this == o) { return true; } - if (!(o instanceof TableDataSource)) { + if (o == null || getClass() != o.getClass()) { return false; } - TableDataSource that = (TableDataSource) o; - - if (!name.equals(that.name)) { - return false; - } - - return true; + return name.equals(that.name); } @Override - public final int hashCode() + public int hashCode() { - return name.hashCode(); + return Objects.hash(name); } } diff --git a/processing/src/test/java/org/apache/druid/query/DataSourceTest.java b/processing/src/test/java/org/apache/druid/query/DataSourceTest.java index 090570db7acb..7c7f50f281bb 100644 --- a/processing/src/test/java/org/apache/druid/query/DataSourceTest.java +++ b/processing/src/test/java/org/apache/druid/query/DataSourceTest.java @@ -99,5 +99,4 @@ public void testUnionDataSource() throws Exception final DataSource serde = JSON_MAPPER.readValue(JSON_MAPPER.writeValueAsString(dataSource), DataSource.class); Assert.assertEquals(dataSource, serde); } - } diff --git a/processing/src/test/java/org/apache/druid/query/GlobalTableDataSourceTest.java b/processing/src/test/java/org/apache/druid/query/GlobalTableDataSourceTest.java new file mode 100644 index 000000000000..fea379015ad3 --- /dev/null +++ b/processing/src/test/java/org/apache/druid/query/GlobalTableDataSourceTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.query; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.apache.druid.segment.TestHelper; +import org.junit.Assert; +import org.junit.Test; + +public class GlobalTableDataSourceTest +{ + private static final GlobalTableDataSource GLOBAL_TABLE_DATA_SOURCE = new GlobalTableDataSource("foo"); + + @Test + public void testEquals() + { + EqualsVerifier.forClass(GlobalTableDataSource.class) + .usingGetClass() + .withNonnullFields("name") + .verify(); + } + + @Test + public void testGlobalTableIsNotEqualsTable() + { + TableDataSource tbl = new TableDataSource(GLOBAL_TABLE_DATA_SOURCE.getName()); + Assert.assertNotEquals(GLOBAL_TABLE_DATA_SOURCE, tbl); + Assert.assertNotEquals(tbl, GLOBAL_TABLE_DATA_SOURCE); + } + + @Test + public void testIsGlobal() + { + Assert.assertTrue(GLOBAL_TABLE_DATA_SOURCE.isGlobal()); + } + + @Test + public void testSerde() throws JsonProcessingException + { + final ObjectMapper jsonMapper = TestHelper.makeJsonMapper(); + final GlobalTableDataSource deserialized = (GlobalTableDataSource) jsonMapper.readValue( + jsonMapper.writeValueAsString(GLOBAL_TABLE_DATA_SOURCE), + DataSource.class + ); + + Assert.assertEquals(GLOBAL_TABLE_DATA_SOURCE, deserialized); + } +} diff --git a/processing/src/test/java/org/apache/druid/query/TableDataSourceTest.java b/processing/src/test/java/org/apache/druid/query/TableDataSourceTest.java index ec8d27d29029..b5aeeb11bab4 100644 --- a/processing/src/test/java/org/apache/druid/query/TableDataSourceTest.java +++ b/processing/src/test/java/org/apache/druid/query/TableDataSourceTest.java @@ -86,7 +86,7 @@ public void test_withChildren_nonEmpty() @Test public void test_equals() { - EqualsVerifier.forClass(TableDataSource.class).withNonnullFields("name").verify(); + EqualsVerifier.forClass(TableDataSource.class).usingGetClass().withNonnullFields("name").verify(); } @Test diff --git a/server/src/main/java/org/apache/druid/client/BrokerServerView.java b/server/src/main/java/org/apache/druid/client/BrokerServerView.java index 3b5d40872cd6..debd72131c23 100644 --- a/server/src/main/java/org/apache/druid/client/BrokerServerView.java +++ b/server/src/main/java/org/apache/druid/client/BrokerServerView.java @@ -218,51 +218,54 @@ private QueryableDruidServer removeServer(DruidServer server) private void serverAddedSegment(final DruidServerMetadata server, final DataSegment segment) { - if (server.getType().equals(ServerType.BROKER)) { - // in theory we could just filter this to ensure we don't put ourselves in here, to make dope broker tree - // query topologies, but for now just skip all brokers, so we don't create some sort of wild infinite query - // loop... - return; - } SegmentId segmentId = segment.getId(); synchronized (lock) { - log.debug("Adding segment[%s] for server[%s]", segment, server); - - ServerSelector selector = selectors.get(segmentId); - if (selector == null) { - selector = new ServerSelector(segment, tierSelectorStrategy); + // in theory we could probably just filter this to ensure we don't put ourselves in here, to make broker tree + // query topologies, but for now just skip all brokers, so we don't create some sort of wild infinite query + // loop... + if (!server.getType().equals(ServerType.BROKER)) { + log.debug("Adding segment[%s] for server[%s]", segment, server); + ServerSelector selector = selectors.get(segmentId); + if (selector == null) { + selector = new ServerSelector(segment, tierSelectorStrategy); + + VersionedIntervalTimeline timeline = timelines.get(segment.getDataSource()); + if (timeline == null) { + timeline = new VersionedIntervalTimeline<>(Ordering.natural()); + timelines.put(segment.getDataSource(), timeline); + } - VersionedIntervalTimeline timeline = timelines.get(segment.getDataSource()); - if (timeline == null) { - timeline = new VersionedIntervalTimeline<>(Ordering.natural()); - timelines.put(segment.getDataSource(), timeline); + timeline.add(segment.getInterval(), segment.getVersion(), segment.getShardSpec().createChunk(selector)); + selectors.put(segmentId, selector); } - timeline.add(segment.getInterval(), segment.getVersion(), segment.getShardSpec().createChunk(selector)); - selectors.put(segmentId, selector); - } - - QueryableDruidServer queryableDruidServer = clients.get(server.getName()); - if (queryableDruidServer == null) { - queryableDruidServer = addServer(baseView.getInventoryValue(server.getName())); + QueryableDruidServer queryableDruidServer = clients.get(server.getName()); + if (queryableDruidServer == null) { + queryableDruidServer = addServer(baseView.getInventoryValue(server.getName())); + } + selector.addServerAndUpdateSegment(queryableDruidServer, segment); } - selector.addServerAndUpdateSegment(queryableDruidServer, segment); + // run the callbacks, even if the segment came from a broker, lets downstream watchers decide what to do with it runTimelineCallbacks(callback -> callback.segmentAdded(server, segment)); } } private void serverRemovedSegment(DruidServerMetadata server, DataSegment segment) { - if (server.getType().equals(ServerType.BROKER)) { - // might as well save the trouble of grabbing a lock for something that isn't there.. - return; - } + SegmentId segmentId = segment.getId(); final ServerSelector selector; synchronized (lock) { log.debug("Removing segment[%s] from server[%s].", segmentId, server); + // we don't store broker segments here, but still run the callbacks for the segment being removed from the server + // since the broker segments are not stored on the timeline, do not fire segmentRemoved event + if (server.getType().equals(ServerType.BROKER)) { + runTimelineCallbacks(callback -> callback.serverSegmentRemoved(server, segment)); + return; + } + selector = selectors.get(segmentId); if (selector == null) { log.warn("Told to remove non-existant segment[%s]", segmentId); diff --git a/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java b/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java index 5e3928c799e0..8283524040f4 100644 --- a/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java +++ b/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java @@ -129,6 +129,7 @@ public QueryRunner getQueryRunnerForIntervals(final Query query, final .applyPostMergeDecoration() .emitCPUTimeMetric(emitter, cpuAccumulator); } + @Override public QueryRunner getQueryRunnerForSegments(final Query query, final Iterable specs) { diff --git a/server/src/main/java/org/apache/druid/server/SegmentManager.java b/server/src/main/java/org/apache/druid/server/SegmentManager.java index 45b6538bb610..a5484f21c9a7 100644 --- a/server/src/main/java/org/apache/druid/server/SegmentManager.java +++ b/server/src/main/java/org/apache/druid/server/SegmentManager.java @@ -40,6 +40,7 @@ import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** @@ -121,6 +122,11 @@ public Map getDataSourceSizes() return CollectionUtils.mapValues(dataSources, SegmentManager.DataSourceState::getTotalSegmentSize); } + public Set getDataSourceNames() + { + return dataSources.keySet(); + } + /** * Returns a map of dataSource to the number of segments managed by this segmentManager. This method should be * carefully because the returned map might be different from the actual data source states. diff --git a/server/src/test/java/org/apache/druid/server/SegmentManagerTest.java b/server/src/test/java/org/apache/druid/server/SegmentManagerTest.java index 562fc1dfffbf..a91411b8db73 100644 --- a/server/src/test/java/org/apache/druid/server/SegmentManagerTest.java +++ b/server/src/test/java/org/apache/druid/server/SegmentManagerTest.java @@ -51,6 +51,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -421,17 +422,19 @@ public void testLoadAndDropNonRootGenerationSegment() throws SegmentLoadingExcep @SuppressWarnings("RedundantThrows") // TODO remove when the bug in intelliJ is fixed. private void assertResult(List expectedExistingSegments) throws SegmentLoadingException { - final Map expectedDataSourceSizes = expectedExistingSegments - .stream() - .collect(Collectors.toMap(DataSegment::getDataSource, DataSegment::getSize, Long::sum)); - final Map expectedDataSourceCounts = expectedExistingSegments - .stream() - .collect(Collectors.toMap(DataSegment::getDataSource, segment -> 1L, Long::sum)); - final Map> expectedDataSources - = new HashMap<>(); + final Map expectedDataSourceSizes = + expectedExistingSegments.stream() + .collect(Collectors.toMap(DataSegment::getDataSource, DataSegment::getSize, Long::sum)); + final Map expectedDataSourceCounts = + expectedExistingSegments.stream() + .collect(Collectors.toMap(DataSegment::getDataSource, segment -> 1L, Long::sum)); + final Set expectedDataSourceNames = expectedExistingSegments.stream() + .map(DataSegment::getDataSource) + .collect(Collectors.toSet()); + final Map> expectedTimelines = new HashMap<>(); for (DataSegment segment : expectedExistingSegments) { final VersionedIntervalTimeline expectedTimeline = - expectedDataSources.computeIfAbsent( + expectedTimelines.computeIfAbsent( segment.getDataSource(), k -> new VersionedIntervalTimeline<>(Ordering.natural()) ); @@ -444,11 +447,12 @@ private void assertResult(List expectedExistingSegments) throws Seg ); } + Assert.assertEquals(expectedDataSourceNames, segmentManager.getDataSourceNames()); Assert.assertEquals(expectedDataSourceCounts, segmentManager.getDataSourceCounts()); Assert.assertEquals(expectedDataSourceSizes, segmentManager.getDataSourceSizes()); final Map dataSources = segmentManager.getDataSources(); - Assert.assertEquals(expectedDataSources.size(), dataSources.size()); + Assert.assertEquals(expectedTimelines.size(), dataSources.size()); dataSources.forEach( (sourceName, dataSourceState) -> { @@ -458,7 +462,7 @@ private void assertResult(List expectedExistingSegments) throws Seg dataSourceState.getTotalSegmentSize() ); Assert.assertEquals( - expectedDataSources.get(sourceName).getAllTimelineEntries(), + expectedTimelines.get(sourceName).getAllTimelineEntries(), dataSourceState.getTimeline().getAllTimelineEntries() ); } diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java b/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java index 35037e2ff2cb..6762aaf13c94 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java @@ -47,6 +47,7 @@ import org.apache.druid.java.util.common.lifecycle.LifecycleStart; import org.apache.druid.java.util.common.lifecycle.LifecycleStop; import org.apache.druid.java.util.emitter.EmittingLogger; +import org.apache.druid.query.GlobalTableDataSource; import org.apache.druid.query.TableDataSource; import org.apache.druid.query.metadata.metadata.AllColumnIncluderator; import org.apache.druid.query.metadata.metadata.ColumnAnalysis; @@ -56,6 +57,7 @@ import org.apache.druid.segment.column.RowSignature; import org.apache.druid.segment.column.ValueType; import org.apache.druid.server.QueryLifecycleFactory; +import org.apache.druid.server.SegmentManager; import org.apache.druid.server.coordination.DruidServerMetadata; import org.apache.druid.server.coordination.ServerType; import org.apache.druid.server.security.AuthenticationResult; @@ -100,6 +102,7 @@ public class DruidSchema extends AbstractSchema private final QueryLifecycleFactory queryLifecycleFactory; private final PlannerConfig config; + private final SegmentManager segmentManager; private final ViewManager viewManager; private final ExecutorService cacheExec; private final ConcurrentMap tables; @@ -117,26 +120,34 @@ public class DruidSchema extends AbstractSchema private int totalSegments = 0; // All mutable segments. + @GuardedBy("lock") private final Set mutableSegments = new TreeSet<>(SEGMENT_ORDER); // All dataSources that need tables regenerated. + @GuardedBy("lock") private final Set dataSourcesNeedingRebuild = new HashSet<>(); // All segments that need to be refreshed. + @GuardedBy("lock") private final TreeSet segmentsNeedingRefresh = new TreeSet<>(SEGMENT_ORDER); // Escalator, so we can attach an authentication result to queries we generate. private final Escalator escalator; + @GuardedBy("lock") private boolean refreshImmediately = false; + @GuardedBy("lock") private long lastRefresh = 0L; + @GuardedBy("lock") private long lastFailure = 0L; + @GuardedBy("lock") private boolean isServerViewInitialized = false; @Inject public DruidSchema( final QueryLifecycleFactory queryLifecycleFactory, final TimelineServerView serverView, + final SegmentManager segmentManager, final PlannerConfig config, final ViewManager viewManager, final Escalator escalator @@ -144,6 +155,7 @@ public DruidSchema( { this.queryLifecycleFactory = Preconditions.checkNotNull(queryLifecycleFactory, "queryLifecycleFactory"); Preconditions.checkNotNull(serverView, "serverView"); + this.segmentManager = segmentManager; this.config = Preconditions.checkNotNull(config, "config"); this.viewManager = Preconditions.checkNotNull(viewManager, "viewManager"); this.cacheExec = Execs.singleThreaded("DruidSchema-Cache-%d"); @@ -196,118 +208,113 @@ public ServerView.CallbackAction serverSegmentRemoved( public void start() throws InterruptedException { cacheExec.submit( - new Runnable() - { - @Override - public void run() - { - try { - while (!Thread.currentThread().isInterrupted()) { - final Set segmentsToRefresh = new TreeSet<>(); - final Set dataSourcesToRebuild = new TreeSet<>(); - - try { - synchronized (lock) { - final long nextRefreshNoFuzz = DateTimes - .utc(lastRefresh) - .plus(config.getMetadataRefreshPeriod()) - .getMillis(); - - // Fuzz a bit to spread load out when we have multiple brokers. - final long nextRefresh = nextRefreshNoFuzz + (long) ((nextRefreshNoFuzz - lastRefresh) * 0.10); - - while (true) { - // Do not refresh if it's too soon after a failure (to avoid rapid cycles of failure). - final boolean wasRecentFailure = DateTimes.utc(lastFailure) - .plus(config.getMetadataRefreshPeriod()) - .isAfterNow(); - - if (isServerViewInitialized && - !wasRecentFailure && - (!segmentsNeedingRefresh.isEmpty() || !dataSourcesNeedingRebuild.isEmpty()) && - (refreshImmediately || nextRefresh < System.currentTimeMillis())) { - // We need to do a refresh. Break out of the waiting loop. - break; - } - - if (isServerViewInitialized) { - // Server view is initialized, but we don't need to do a refresh. Could happen if there are - // no segments in the system yet. Just mark us as initialized, then. - initialized.countDown(); - } - - // Wait some more, we'll wake up when it might be time to do another refresh. - lock.wait(Math.max(1, nextRefresh - System.currentTimeMillis())); + () -> { + try { + while (!Thread.currentThread().isInterrupted()) { + final Set segmentsToRefresh = new TreeSet<>(); + final Set dataSourcesToRebuild = new TreeSet<>(); + + try { + synchronized (lock) { + final long nextRefreshNoFuzz = DateTimes + .utc(lastRefresh) + .plus(config.getMetadataRefreshPeriod()) + .getMillis(); + + // Fuzz a bit to spread load out when we have multiple brokers. + final long nextRefresh = nextRefreshNoFuzz + (long) ((nextRefreshNoFuzz - lastRefresh) * 0.10); + + while (true) { + // Do not refresh if it's too soon after a failure (to avoid rapid cycles of failure). + final boolean wasRecentFailure = DateTimes.utc(lastFailure) + .plus(config.getMetadataRefreshPeriod()) + .isAfterNow(); + + if (isServerViewInitialized && + !wasRecentFailure && + (!segmentsNeedingRefresh.isEmpty() || !dataSourcesNeedingRebuild.isEmpty()) && + (refreshImmediately || nextRefresh < System.currentTimeMillis())) { + // We need to do a refresh. Break out of the waiting loop. + break; } - segmentsToRefresh.addAll(segmentsNeedingRefresh); - segmentsNeedingRefresh.clear(); - - // Mutable segments need a refresh every period, since new columns could be added dynamically. - segmentsNeedingRefresh.addAll(mutableSegments); + if (isServerViewInitialized) { + // Server view is initialized, but we don't need to do a refresh. Could happen if there are + // no segments in the system yet. Just mark us as initialized, then. + initialized.countDown(); + } - lastFailure = 0L; - lastRefresh = System.currentTimeMillis(); - refreshImmediately = false; + // Wait some more, we'll wake up when it might be time to do another refresh. + lock.wait(Math.max(1, nextRefresh - System.currentTimeMillis())); } - // Refresh the segments. - final Set refreshed = refreshSegments(segmentsToRefresh); + segmentsToRefresh.addAll(segmentsNeedingRefresh); + segmentsNeedingRefresh.clear(); - synchronized (lock) { - // Add missing segments back to the refresh list. - segmentsNeedingRefresh.addAll(Sets.difference(segmentsToRefresh, refreshed)); + // Mutable segments need a refresh every period, since new columns could be added dynamically. + segmentsNeedingRefresh.addAll(mutableSegments); - // Compute the list of dataSources to rebuild tables for. - dataSourcesToRebuild.addAll(dataSourcesNeedingRebuild); - refreshed.forEach(segment -> dataSourcesToRebuild.add(segment.getDataSource())); - dataSourcesNeedingRebuild.clear(); + lastFailure = 0L; + lastRefresh = System.currentTimeMillis(); + refreshImmediately = false; + } - lock.notifyAll(); - } + // Refresh the segments. + final Set refreshed = refreshSegments(segmentsToRefresh); - // Rebuild the dataSources. - for (String dataSource : dataSourcesToRebuild) { - final DruidTable druidTable = buildDruidTable(dataSource); - final DruidTable oldTable = tables.put(dataSource, druidTable); - if (oldTable == null || !oldTable.getRowSignature().equals(druidTable.getRowSignature())) { - log.info("dataSource [%s] has new signature: %s.", dataSource, druidTable.getRowSignature()); - } else { - log.debug("dataSource [%s] signature is unchanged.", dataSource); - } - } + synchronized (lock) { + // Add missing segments back to the refresh list. + segmentsNeedingRefresh.addAll(Sets.difference(segmentsToRefresh, refreshed)); - initialized.countDown(); - } - catch (InterruptedException e) { - // Fall through. - throw e; + // Compute the list of dataSources to rebuild tables for. + dataSourcesToRebuild.addAll(dataSourcesNeedingRebuild); + refreshed.forEach(segment -> dataSourcesToRebuild.add(segment.getDataSource())); + dataSourcesNeedingRebuild.clear(); + + lock.notifyAll(); } - catch (Exception e) { - log.warn(e, "Metadata refresh failed, trying again soon."); - - synchronized (lock) { - // Add our segments and dataSources back to their refresh and rebuild lists. - segmentsNeedingRefresh.addAll(segmentsToRefresh); - dataSourcesNeedingRebuild.addAll(dataSourcesToRebuild); - lastFailure = System.currentTimeMillis(); - lock.notifyAll(); + + // Rebuild the dataSources. + for (String dataSource : dataSourcesToRebuild) { + final DruidTable druidTable = buildDruidTable(dataSource); + final DruidTable oldTable = tables.put(dataSource, druidTable); + if (oldTable == null || !oldTable.getRowSignature().equals(druidTable.getRowSignature())) { + log.info("dataSource [%s] has new signature: %s.", dataSource, druidTable.getRowSignature()); + } else { + log.debug("dataSource [%s] signature is unchanged.", dataSource); } } + + initialized.countDown(); + } + catch (InterruptedException e) { + // Fall through. + throw e; + } + catch (Exception e) { + log.warn(e, "Metadata refresh failed, trying again soon."); + + synchronized (lock) { + // Add our segments and dataSources back to their refresh and rebuild lists. + segmentsNeedingRefresh.addAll(segmentsToRefresh); + dataSourcesNeedingRebuild.addAll(dataSourcesToRebuild); + lastFailure = System.currentTimeMillis(); + lock.notifyAll(); + } } } - catch (InterruptedException e) { - // Just exit. - } - catch (Throwable e) { - // Throwables that fall out to here (not caught by an inner try/catch) are potentially gnarly, like - // OOMEs. Anyway, let's just emit an alert and stop refreshing metadata. - log.makeAlert(e, "Metadata refresh failed permanently").emit(); - throw e; - } - finally { - log.info("Metadata refresh stopped."); - } + } + catch (InterruptedException e) { + // Just exit. + } + catch (Throwable e) { + // Throwables that fall out to here (not caught by an inner try/catch) are potentially gnarly, like + // OOMEs. Anyway, let's just emit an alert and stop refreshing metadata. + log.makeAlert(e, "Metadata refresh failed permanently").emit(); + throw e; + } + finally { + log.info("Metadata refresh stopped."); } } ); @@ -350,51 +357,51 @@ protected Multimap getFunctionMultim @VisibleForTesting void addSegment(final DruidServerMetadata server, final DataSegment segment) { - if (server.getType().equals(ServerType.BROKER)) { - // in theory we could just filter this to ensure we don't put ourselves in here, to make dope broker tree - // query topologies, but for now just skip all brokers, so we don't create some sort of wild infinite metadata - // loop... - return; - } synchronized (lock) { - final Map knownSegments = segmentMetadataInfo.get(segment.getDataSource()); - AvailableSegmentMetadata segmentMetadata = knownSegments != null ? knownSegments.get(segment.getId()) : null; - if (segmentMetadata == null) { - // segmentReplicatable is used to determine if segments are served by historical or realtime servers - long isRealtime = server.isSegmentReplicationTarget() ? 0 : 1; - segmentMetadata = AvailableSegmentMetadata.builder( - segment, - isRealtime, - ImmutableSet.of(server), - null, - DEFAULT_NUM_ROWS - ).build(); - // Unknown segment. - setAvailableSegmentMetadata(segment.getId(), segmentMetadata); - segmentsNeedingRefresh.add(segment.getId()); - if (!server.isSegmentReplicationTarget()) { - log.debug("Added new mutable segment[%s].", segment.getId()); - mutableSegments.add(segment.getId()); - } else { - log.debug("Added new immutable segment[%s].", segment.getId()); - } + if (server.getType().equals(ServerType.BROKER)) { + // a segment on a broker means a broadcast datasource, skip metadata because we'll also see this segment on the + // historical, however mark the datasource for refresh because it needs to be globalized + dataSourcesNeedingRebuild.add(segment.getDataSource()); } else { - final Set segmentServers = segmentMetadata.getReplicas(); - final ImmutableSet servers = new ImmutableSet.Builder() - .addAll(segmentServers) - .add(server) - .build(); - final AvailableSegmentMetadata metadataWithNumReplicas = AvailableSegmentMetadata - .from(segmentMetadata) - .withReplicas(servers) - .withRealtime(recomputeIsRealtime(servers)) - .build(); - knownSegments.put(segment.getId(), metadataWithNumReplicas); - if (server.isSegmentReplicationTarget()) { - // If a segment shows up on a replicatable (historical) server at any point, then it must be immutable, - // even if it's also available on non-replicatable (realtime) servers. - mutableSegments.remove(segment.getId()); - log.debug("Segment[%s] has become immutable.", segment.getId()); + final Map knownSegments = segmentMetadataInfo.get(segment.getDataSource()); + AvailableSegmentMetadata segmentMetadata = knownSegments != null ? knownSegments.get(segment.getId()) : null; + if (segmentMetadata == null) { + // segmentReplicatable is used to determine if segments are served by historical or realtime servers + long isRealtime = server.isSegmentReplicationTarget() ? 0 : 1; + segmentMetadata = AvailableSegmentMetadata.builder( + segment, + isRealtime, + ImmutableSet.of(server), + null, + DEFAULT_NUM_ROWS + ).build(); + // Unknown segment. + setAvailableSegmentMetadata(segment.getId(), segmentMetadata); + segmentsNeedingRefresh.add(segment.getId()); + if (!server.isSegmentReplicationTarget()) { + log.debug("Added new mutable segment[%s].", segment.getId()); + mutableSegments.add(segment.getId()); + } else { + log.debug("Added new immutable segment[%s].", segment.getId()); + } + } else { + final Set segmentServers = segmentMetadata.getReplicas(); + final ImmutableSet servers = new ImmutableSet.Builder() + .addAll(segmentServers) + .add(server) + .build(); + final AvailableSegmentMetadata metadataWithNumReplicas = AvailableSegmentMetadata + .from(segmentMetadata) + .withReplicas(servers) + .withRealtime(recomputeIsRealtime(servers)) + .build(); + knownSegments.put(segment.getId(), metadataWithNumReplicas); + if (server.isSegmentReplicationTarget()) { + // If a segment shows up on a replicatable (historical) server at any point, then it must be immutable, + // even if it's also available on non-replicatable (realtime) servers. + mutableSegments.remove(segment.getId()); + log.debug("Segment[%s] has become immutable.", segment.getId()); + } } } if (!tables.containsKey(segment.getDataSource())) { @@ -434,26 +441,28 @@ void removeSegment(final DataSegment segment) @VisibleForTesting void removeServerSegment(final DruidServerMetadata server, final DataSegment segment) { - if (server.getType().equals(ServerType.BROKER)) { - // cheese it - return; - } synchronized (lock) { log.debug("Segment[%s] is gone from server[%s]", segment.getId(), server.getName()); - final Map knownSegments = segmentMetadataInfo.get(segment.getDataSource()); - final AvailableSegmentMetadata segmentMetadata = knownSegments.get(segment.getId()); - final Set segmentServers = segmentMetadata.getReplicas(); - final ImmutableSet servers = FluentIterable - .from(segmentServers) - .filter(Predicates.not(Predicates.equalTo(server))) - .toSet(); - - final AvailableSegmentMetadata metadataWithNumReplicas = AvailableSegmentMetadata - .from(segmentMetadata) - .withReplicas(servers) - .withRealtime(recomputeIsRealtime(servers)) - .build(); - knownSegments.put(segment.getId(), metadataWithNumReplicas); + if (server.getType().equals(ServerType.BROKER)) { + // a segment on a broker means a broadcast datasource, skip metadata because we'll also see this segment on the + // historical, however mark the datasource for refresh because it might no longer be broadcast or something + dataSourcesNeedingRebuild.add(segment.getDataSource()); + } else { + final Map knownSegments = segmentMetadataInfo.get(segment.getDataSource()); + final AvailableSegmentMetadata segmentMetadata = knownSegments.get(segment.getId()); + final Set segmentServers = segmentMetadata.getReplicas(); + final ImmutableSet servers = FluentIterable + .from(segmentServers) + .filter(Predicates.not(Predicates.equalTo(server))) + .toSet(); + + final AvailableSegmentMetadata metadataWithNumReplicas = AvailableSegmentMetadata + .from(segmentMetadata) + .withReplicas(servers) + .withRealtime(recomputeIsRealtime(servers)) + .build(); + knownSegments.put(segment.getId(), metadataWithNumReplicas); + } lock.notifyAll(); } } @@ -592,7 +601,7 @@ void setAvailableSegmentMetadata(final SegmentId segmentId, final AvailableSegme } } - private DruidTable buildDruidTable(final String dataSource) + protected DruidTable buildDruidTable(final String dataSource) { synchronized (lock) { final Map segmentMap = segmentMetadataInfo.get(dataSource); @@ -616,7 +625,14 @@ private DruidTable buildDruidTable(final String dataSource) final RowSignature.Builder builder = RowSignature.builder(); columnTypes.forEach(builder::add); - return new DruidTable(new TableDataSource(dataSource), builder.build()); + + final TableDataSource tableDataSource; + if (segmentManager.getDataSourceNames().contains(dataSource)) { + tableDataSource = new GlobalTableDataSource(dataSource); + } else { + tableDataSource = new TableDataSource(dataSource); + } + return new DruidTable(tableDataSource, builder.build()); } } diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidCalciteSchemaModuleTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidCalciteSchemaModuleTest.java index edba60a35c2e..11c2b97f4244 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidCalciteSchemaModuleTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidCalciteSchemaModuleTest.java @@ -39,6 +39,7 @@ import org.apache.druid.query.lookup.LookupExtractorFactoryContainerProvider; import org.apache.druid.query.lookup.LookupReferencesManager; import org.apache.druid.server.QueryLifecycleFactory; +import org.apache.druid.server.SegmentManager; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.Escalator; import org.apache.druid.sql.calcite.planner.PlannerConfig; @@ -84,6 +85,8 @@ public class DruidCalciteSchemaModuleTest extends CalciteTestBase private ObjectMapper objectMapper; @Mock private LookupReferencesManager lookupReferencesManager; + @Mock + private SegmentManager segmentManager; private DruidCalciteSchemaModule target; private Injector injector; @@ -104,6 +107,7 @@ public void setUp() binder.bind(Escalator.class).toInstance(escalator); binder.bind(AuthorizerMapper.class).toInstance(authorizerMapper); binder.bind(InventoryView.class).toInstance(serverInventoryView); + binder.bind(SegmentManager.class).toInstance(segmentManager); binder.bind(DruidLeaderClient.class) .annotatedWith(Coordinator.class) .toInstance(coordinatorDruidLeaderClient); diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaNoDataInitTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaNoDataInitTest.java index 713dfffedf69..fd20fb594e85 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaNoDataInitTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaNoDataInitTest.java @@ -22,7 +22,9 @@ import com.google.common.collect.ImmutableMap; import org.apache.druid.java.util.common.io.Closer; import org.apache.druid.query.QueryRunnerFactoryConglomerate; +import org.apache.druid.segment.loading.SegmentLoader; import org.apache.druid.server.QueryStackTests; +import org.apache.druid.server.SegmentManager; import org.apache.druid.server.security.NoopEscalator; import org.apache.druid.sql.calcite.planner.PlannerConfig; import org.apache.druid.sql.calcite.util.CalciteTestBase; @@ -30,6 +32,7 @@ import org.apache.druid.sql.calcite.util.SpecificSegmentsQuerySegmentWalker; import org.apache.druid.sql.calcite.util.TestServerInventoryView; import org.apache.druid.sql.calcite.view.NoopViewManager; +import org.easymock.EasyMock; import org.junit.Assert; import org.junit.Test; @@ -50,6 +53,7 @@ public void testInitializationWithNoData() throws Exception conglomerate ), new TestServerInventoryView(Collections.emptyList()), + new SegmentManager(EasyMock.createMock(SegmentLoader.class)), PLANNER_CONFIG_DEFAULT, new NoopViewManager(), new NoopEscalator() diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java index ee3bca1a4f8e..8455965ef5e9 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java @@ -22,18 +22,20 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; import org.apache.calcite.jdbc.JavaTypeFactoryImpl; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.schema.Table; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.druid.client.ImmutableDruidServer; -import org.apache.druid.client.TimelineServerView; import org.apache.druid.data.input.InputRow; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.Pair; import org.apache.druid.java.util.common.io.Closer; +import org.apache.druid.query.GlobalTableDataSource; import org.apache.druid.query.QueryRunnerFactoryConglomerate; +import org.apache.druid.query.TableDataSource; import org.apache.druid.query.aggregation.CountAggregatorFactory; import org.apache.druid.query.aggregation.DoubleSumAggregatorFactory; import org.apache.druid.query.aggregation.LongSumAggregatorFactory; @@ -41,8 +43,10 @@ import org.apache.druid.segment.IndexBuilder; import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.incremental.IncrementalIndexSchema; +import org.apache.druid.segment.loading.SegmentLoader; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; import org.apache.druid.server.QueryStackTests; +import org.apache.druid.server.SegmentManager; import org.apache.druid.server.coordination.DruidServerMetadata; import org.apache.druid.server.coordination.ServerType; import org.apache.druid.server.security.NoopEscalator; @@ -57,8 +61,9 @@ import org.apache.druid.timeline.DataSegment.PruneSpecsHolder; import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.partition.LinearShardSpec; -import org.apache.druid.timeline.partition.NoneShardSpec; import org.apache.druid.timeline.partition.NumberedShardSpec; +import org.easymock.EasyMock; +import org.joda.time.Period; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; @@ -72,11 +77,21 @@ import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; public class DruidSchemaTest extends CalciteTestBase { - private static final PlannerConfig PLANNER_CONFIG_DEFAULT = new PlannerConfig(); + private static final PlannerConfig PLANNER_CONFIG_DEFAULT = new PlannerConfig() + { + @Override + public Period getMetadataRefreshPeriod() + { + return new Period("PT1S"); + } + }; private static final List ROWS1 = ImmutableList.of( CalciteTests.createRow(ImmutableMap.of("t", "2000-01-01", "m1", "1.0", "dim1", "")), @@ -93,7 +108,10 @@ public class DruidSchemaTest extends CalciteTestBase private static QueryRunnerFactoryConglomerate conglomerate; private static Closer resourceCloser; + private TestServerInventoryView serverView; private List druidServers; + private CountDownLatch getDatasourcesLatch = new CountDownLatch(1); + private CountDownLatch buildTableLatch = new CountDownLatch(1); @BeforeClass public static void setUpClass() @@ -113,10 +131,13 @@ public static void tearDownClass() throws IOException private SpecificSegmentsQuerySegmentWalker walker = null; private DruidSchema schema = null; + private SegmentManager segmentManager; + private Set dataSourceNames; @Before public void setUp() throws Exception { + dataSourceNames = Sets.newConcurrentHashSet(); final File tmpDir = temporaryFolder.newFolder(); final QueryableIndex index1 = IndexBuilder.create() .tmpDir(new File(tmpDir, "1")) @@ -146,6 +167,16 @@ public void setUp() throws Exception .rows(ROWS2) .buildMMappedIndex(); + segmentManager = new SegmentManager(EasyMock.createMock(SegmentLoader.class)) + { + @Override + public Set getDataSourceNames() + { + getDatasourcesLatch.countDown(); + return dataSourceNames; + } + }; + walker = new SpecificSegmentsQuerySegmentWalker(conglomerate).add( DataSegment.builder() .dataSource(CalciteTests.DATASOURCE1) @@ -188,16 +219,26 @@ public void setUp() throws Exception PruneSpecsHolder.DEFAULT ); final List realtimeSegments = ImmutableList.of(segment1); - final TimelineServerView serverView = new TestServerInventoryView(walker.getSegments(), realtimeSegments); + serverView = new TestServerInventoryView(walker.getSegments(), realtimeSegments); druidServers = serverView.getDruidServers(); schema = new DruidSchema( CalciteTests.createMockQueryLifecycleFactory(walker, conglomerate), serverView, + segmentManager, PLANNER_CONFIG_DEFAULT, new NoopViewManager(), new NoopEscalator() - ); + ) + { + @Override + protected DruidTable buildDruidTable(String dataSource) + { + DruidTable table = super.buildDruidTable(dataSource); + buildTableLatch.countDown(); + return table; + } + }; schema.start(); schema.awaitInitialization(); @@ -420,34 +461,60 @@ public void testAvailableSegmentMetadataIsRealtime() } @Test - public void testAvailableSegmentFromBrokerIsIgnored() + public void testLocalSegmentCacheSetsDataSourceAsGlobal() throws InterruptedException { + DruidTable fooTable = (DruidTable) schema.getTableMap().get("foo"); + Assert.assertNotNull(fooTable); + Assert.assertTrue(fooTable.getDataSource() instanceof TableDataSource); + Assert.assertFalse(fooTable.getDataSource() instanceof GlobalTableDataSource); - Assert.assertEquals(4, schema.getTotalSegments()); - - DruidServerMetadata metadata = new DruidServerMetadata( - "broker", - "localhost:0", + final DataSegment someNewBrokerSegment = new DataSegment( + "foo", + Intervals.of("2012/2013"), + "version1", + null, + ImmutableList.of("dim1", "dim2"), + ImmutableList.of("met1", "met2"), + new NumberedShardSpec(2, 3), null, - 1000L, - ServerType.BROKER, - "broken", - 0 - ); - - DataSegment segment = new DataSegment( - "test", - Intervals.of("2011-04-01/2011-04-11"), - "v1", - ImmutableMap.of(), - ImmutableList.of(), - ImmutableList.of(), - NoneShardSpec.instance(), 1, - 100L + 100L, + PruneSpecsHolder.DEFAULT ); - schema.addSegment(metadata, segment); - Assert.assertEquals(4, schema.getTotalSegments()); - + dataSourceNames.add("foo"); + serverView.addSegment(someNewBrokerSegment, ServerType.BROKER); + + // wait for build + buildTableLatch.await(1, TimeUnit.SECONDS); + buildTableLatch = new CountDownLatch(1); + buildTableLatch.await(1, TimeUnit.SECONDS); + + // wait for get again, just to make sure table has been updated (latch counts down just before tables are updated) + getDatasourcesLatch = new CountDownLatch(1); + getDatasourcesLatch.await(1, TimeUnit.SECONDS); + + fooTable = (DruidTable) schema.getTableMap().get("foo"); + Assert.assertNotNull(fooTable); + Assert.assertTrue(fooTable.getDataSource() instanceof TableDataSource); + Assert.assertTrue(fooTable.getDataSource() instanceof GlobalTableDataSource); + + // now remove it + dataSourceNames.remove("foo"); + serverView.removeSegment(someNewBrokerSegment, ServerType.BROKER); + + // wait for build + buildTableLatch.await(1, TimeUnit.SECONDS); + buildTableLatch = new CountDownLatch(1); + buildTableLatch.await(1, TimeUnit.SECONDS); + + // wait for get again, just to make sure table has been updated (latch counts down just before tables are updated) + getDatasourcesLatch = new CountDownLatch(1); + getDatasourcesLatch.await(1, TimeUnit.SECONDS); + + fooTable = (DruidTable) schema.getTableMap().get("foo"); + Assert.assertNotNull(fooTable); + Assert.assertTrue(fooTable.getDataSource() instanceof TableDataSource); + Assert.assertFalse(fooTable.getDataSource() instanceof GlobalTableDataSource); } + } diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java index 80bf83030f35..3e1356af7b98 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java @@ -67,9 +67,11 @@ import org.apache.druid.segment.column.RowSignature; import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.incremental.IncrementalIndexSchema; +import org.apache.druid.segment.loading.SegmentLoader; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; import org.apache.druid.server.DruidNode; import org.apache.druid.server.QueryStackTests; +import org.apache.druid.server.SegmentManager; import org.apache.druid.server.coordination.DruidServerMetadata; import org.apache.druid.server.coordination.ServerType; import org.apache.druid.server.coordinator.BytesAccumulatingResponseHandler; @@ -239,6 +241,7 @@ public Authorizer getAuthorizer(String name) druidSchema = new DruidSchema( CalciteTests.createMockQueryLifecycleFactory(walker, conglomerate), new TestServerInventoryView(walker.getSegments(), realtimeSegments), + new SegmentManager(EasyMock.createMock(SegmentLoader.class)), PLANNER_CONFIG_DEFAULT, new NoopViewManager(), new NoopEscalator() diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java index 588d5adb8e37..0ca415b8746a 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java @@ -73,11 +73,13 @@ import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.TestHelper; import org.apache.druid.segment.incremental.IncrementalIndexSchema; +import org.apache.druid.segment.loading.SegmentLoader; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; import org.apache.druid.server.DruidNode; import org.apache.druid.server.QueryLifecycleFactory; import org.apache.druid.server.QueryScheduler; import org.apache.druid.server.QueryStackTests; +import org.apache.druid.server.SegmentManager; import org.apache.druid.server.coordinator.BytesAccumulatingResponseHandler; import org.apache.druid.server.log.NoopRequestLogger; import org.apache.druid.server.security.Access; @@ -107,6 +109,7 @@ import org.apache.druid.sql.calcite.view.ViewManager; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.partition.LinearShardSpec; +import org.easymock.EasyMock; import org.joda.time.DateTime; import org.joda.time.Duration; import org.joda.time.chrono.ISOChronology; @@ -866,6 +869,7 @@ private static DruidSchema createMockSchema( final DruidSchema schema = new DruidSchema( CalciteTests.createMockQueryLifecycleFactory(walker, conglomerate), new TestServerInventoryView(walker.getSegments()), + new SegmentManager(EasyMock.createMock(SegmentLoader.class)), plannerConfig, viewManager, TEST_AUTHENTICATOR_ESCALATOR diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/util/TestServerInventoryView.java b/sql/src/test/java/org/apache/druid/sql/calcite/util/TestServerInventoryView.java index a8e498beb1f0..170d205f449d 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/util/TestServerInventoryView.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/util/TestServerInventoryView.java @@ -26,6 +26,7 @@ import org.apache.druid.client.ImmutableDruidServer; import org.apache.druid.client.TimelineServerView; import org.apache.druid.client.selector.ServerSelector; +import org.apache.druid.java.util.common.Pair; import org.apache.druid.query.QueryRunner; import org.apache.druid.query.planning.DataSourceAnalysis; import org.apache.druid.server.coordination.DruidServerMetadata; @@ -63,18 +64,31 @@ public class TestServerInventoryView implements TimelineServerView "dummy", 0 ); - private final List segments; + private static final DruidServerMetadata DUMMY_BROKER = new DruidServerMetadata( + "dummy3", + "dummy3", + null, + 0, + ServerType.BROKER, + "dummy", + 0 + ); + private List segments = new ArrayList<>(); private List realtimeSegments = new ArrayList<>(); + private List brokerSegments = new ArrayList<>(); + + private List> segmentCallbackExecs = new ArrayList<>(); + private List> timelineCallbackExecs = new ArrayList<>(); public TestServerInventoryView(List segments) { - this.segments = ImmutableList.copyOf(segments); + this.segments.addAll(segments); } public TestServerInventoryView(List segments, List realtimeSegments) { - this.segments = ImmutableList.copyOf(segments); - this.realtimeSegments = ImmutableList.copyOf(realtimeSegments); + this.segments.addAll(segments); + this.realtimeSegments.addAll(realtimeSegments); } @Override @@ -87,6 +101,7 @@ public Optional> getTimeline(Da @Override public List getDruidServers() { + // do not return broker on purpose to mimic behavior of BrokerServerView final ImmutableDruidDataSource dataSource = new ImmutableDruidDataSource("DUMMY", Collections.emptyMap(), segments); final ImmutableDruidServer server = new ImmutableDruidServer( DUMMY_SERVER, @@ -118,6 +133,7 @@ public void registerSegmentCallback(Executor exec, final SegmentCallback callbac exec.execute(() -> callback.segmentAdded(DUMMY_SERVER_REALTIME, segment)); } exec.execute(callback::segmentViewInitialized); + segmentCallbackExecs.add(new Pair<>(exec, callback)); } @Override @@ -130,6 +146,7 @@ public void registerTimelineCallback(final Executor exec, final TimelineCallback exec.execute(() -> callback.segmentAdded(DUMMY_SERVER_REALTIME, segment)); } exec.execute(callback::timelineInitialized); + timelineCallbackExecs.add(new Pair<>(exec, callback)); } @Override @@ -143,4 +160,57 @@ public void registerServerRemovedCallback(Executor exec, ServerRemovedCallback c { // Do nothing } + + public void addSegment(DataSegment segment, ServerType serverType) + { + final Pair> whichServerAndSegments = + getDummyServerAndSegmentsForType(serverType); + final DruidServerMetadata whichServer = whichServerAndSegments.lhs; + whichServerAndSegments.rhs.add(segment); + segmentCallbackExecs.forEach( + execAndCallback -> execAndCallback.lhs.execute(() -> execAndCallback.rhs.segmentAdded(whichServer, segment)) + ); + timelineCallbackExecs.forEach( + execAndCallback -> execAndCallback.lhs.execute(() -> execAndCallback.rhs.segmentAdded(whichServer, segment)) + ); + } + + public void removeSegment(DataSegment segment, ServerType serverType) + { + final Pair> whichServerAndSegments = + getDummyServerAndSegmentsForType(serverType); + final DruidServerMetadata whichServer = whichServerAndSegments.lhs; + whichServerAndSegments.rhs.remove(segment); + segmentCallbackExecs.forEach( + execAndCallback -> execAndCallback.lhs.execute(() -> execAndCallback.rhs.segmentRemoved(whichServer, segment)) + ); + timelineCallbackExecs.forEach( + execAndCallback -> execAndCallback.lhs.execute(() -> { + execAndCallback.rhs.serverSegmentRemoved(whichServer, segment); + // assume that all replicas have been removed and fire this one too + execAndCallback.rhs.segmentRemoved(segment); + }) + ); + } + + private Pair> getDummyServerAndSegmentsForType(ServerType serverType) + { + final DruidServerMetadata whichServer; + final List whichSegments; + switch (serverType) { + case BROKER: + whichServer = DUMMY_BROKER; + whichSegments = brokerSegments; + break; + case REALTIME: + whichServer = DUMMY_SERVER_REALTIME; + whichSegments = realtimeSegments; + break; + default: + whichServer = DUMMY_SERVER; + whichSegments = segments; + break; + } + return new Pair<>(whichServer, whichSegments); + } } From 3f9b862c93c01d7dce846871ceb60aca2c155c66 Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn <52679095+maytasm@users.noreply.github.com> Date: Tue, 16 Jun 2020 20:48:30 -1000 Subject: [PATCH 088/107] API to verify a datasource has the latest ingested data (#9965) * API to verify a datasource has the latest ingested data * API to verify a datasource has the latest ingested data * API to verify a datasource has the latest ingested data * API to verify a datasource has the latest ingested data * API to verify a datasource has the latest ingested data * fix checksyle * API to verify a datasource has the latest ingested data * API to verify a datasource has the latest ingested data * API to verify a datasource has the latest ingested data * API to verify a datasource has the latest ingested data * fix spelling * address comments * fix checkstyle * update docs * fix tests * fix doc * address comments * fix typo * fix spelling * address comments * address comments * fix typo in docs --- docs/ingestion/faq.md | 14 + docs/operations/api-reference.md | 43 ++- .../druid/client/CoordinatorServerView.java | 4 + .../metadata/SegmentsMetadataManager.java | 17 + .../metadata/SqlSegmentsMetadataManager.java | 142 ++++++-- .../server/coordinator/DruidCoordinator.java | 19 +- .../server/http/DataSourcesResource.java | 133 +++++++- .../SqlSegmentsMetadataManagerTest.java | 159 ++++++++- .../server/http/DataSourcesResourceTest.java | 315 ++++++++++++++++-- 9 files changed, 773 insertions(+), 73 deletions(-) diff --git a/docs/ingestion/faq.md b/docs/ingestion/faq.md index 1e6ffe10254f..308407fa3426 100644 --- a/docs/ingestion/faq.md +++ b/docs/ingestion/faq.md @@ -66,6 +66,20 @@ Other common reasons that hand-off fails are as follows: Make sure to include the `druid-hdfs-storage` and all the hadoop configuration, dependencies (that can be obtained by running command `hadoop classpath` on a machine where hadoop has been setup) in the classpath. And, provide necessary HDFS settings as described in [deep storage](../dependencies/deep-storage.md) . +## How do I know when I can make query to Druid after submitting batch ingestion task? + +You can verify if segments created by a recent ingestion task are loaded onto historicals and available for querying using the following workflow. +1. Submit your ingestion task. +2. Repeatedly poll the [Overlord's tasks API](../operations/api-reference.md#tasks) ( `/druid/indexer/v1/task/{taskId}/status`) until your task is shown to be successfully completed. +3. Poll the [Segment Loading by Datasource API](../operations/api-reference.md#segment-loading-by-datasource) (`/druid/coordinator/v1/datasources/{dataSourceName}/loadstatus`) with +`forceMetadataRefresh=true` and `interval=` once. +(Note: `forceMetadataRefresh=true` refreshes Coordinator's metadata cache of all datasources. This can be a heavy operation in terms of the load on the metadata store but is necessary to make sure that we verify all the latest segments' load status) +If there are segments not yet loaded, continue to step 4, otherwise you can now query the data. +4. Repeatedly poll the [Segment Loading by Datasource API](../operations/api-reference.md#segment-loading-by-datasource) (`/druid/coordinator/v1/datasources/{dataSourceName}/loadstatus`) with +`forceMetadataRefresh=false` and `interval=`. +Continue polling until all segments are loaded. Once all segments are loaded you can now query the data. +Note that this workflow only guarantees that the segments are available at the time of the [Segment Loading by Datasource API](../operations/api-reference.md#segment-loading-by-datasource) call. Segments can still become missing because of historical process failures or any other reasons afterward. + ## I don't see my Druid segments on my Historical processes You can check the Coordinator console located at `:`. Make sure that your segments have actually loaded on [Historical processes](../design/historical.md). If your segments are not present, check the Coordinator logs for messages about capacity of replication errors. One reason that segments are not downloaded is because Historical processes have maxSizes that are too small, making them incapable of downloading more data. You can change that with (for example): diff --git a/docs/operations/api-reference.md b/docs/operations/api-reference.md index a66dd649f410..a3610a8bc52a 100644 --- a/docs/operations/api-reference.md +++ b/docs/operations/api-reference.md @@ -96,11 +96,11 @@ Returns the percentage of segments actually loaded in the cluster versus segment * `/druid/coordinator/v1/loadstatus?simple` -Returns the number of segments left to load until segments that should be loaded in the cluster are available for queries. This does not include replication. +Returns the number of segments left to load until segments that should be loaded in the cluster are available for queries. This does not include segment replication counts. * `/druid/coordinator/v1/loadstatus?full` -Returns the number of segments left to load in each tier until segments that should be loaded in the cluster are all available. This includes replication. +Returns the number of segments left to load in each tier until segments that should be loaded in the cluster are all available. This includes segment replication counts. * `/druid/coordinator/v1/loadqueue` @@ -114,6 +114,45 @@ Returns the number of segments to load and drop, as well as the total segment lo Returns the serialized JSON of segments to load and drop for each Historical process. + +#### Segment Loading by Datasource + +Note that all _interval_ query parameters are ISO 8601 strings (e.g., 2016-06-27/2016-06-28). +Also note that these APIs only guarantees that the segments are available at the time of the call. +Segments can still become missing because of historical process failures or any other reasons afterward. + +##### GET + +* `/druid/coordinator/v1/datasources/{dataSourceName}/loadstatus?forceMetadataRefresh={boolean}&interval={myInterval}` + +Returns the percentage of segments actually loaded in the cluster versus segments that should be loaded in the cluster for the given +datasource over the given interval (or last 2 weeks if interval is not given). `forceMetadataRefresh` is required to be set. +Setting `forceMetadataRefresh` to true will force the coordinator to poll latest segment metadata from the metadata store +(Note: `forceMetadataRefresh=true` refreshes Coordinator's metadata cache of all datasources. This can be a heavy operation in terms +of the load on the metadata store but can be necessary to make sure that we verify all the latest segments' load status) +Setting `forceMetadataRefresh` to false will use the metadata cached on the coordinator from the last force/periodic refresh. +If no used segments are found for the given inputs, this API returns `204 No Content` + + * `/druid/coordinator/v1/datasources/{dataSourceName}/loadstatus?simple&forceMetadataRefresh={boolean}&interval={myInterval}` + +Returns the number of segments left to load until segments that should be loaded in the cluster are available for the given datasource +over the given interval (or last 2 weeks if interval is not given). This does not include segment replication counts. `forceMetadataRefresh` is required to be set. +Setting `forceMetadataRefresh` to true will force the coordinator to poll latest segment metadata from the metadata store +(Note: `forceMetadataRefresh=true` refreshes Coordinator's metadata cache of all datasources. This can be a heavy operation in terms +of the load on the metadata store but can be necessary to make sure that we verify all the latest segments' load status) +Setting `forceMetadataRefresh` to false will use the metadata cached on the coordinator from the last force/periodic refresh. +If no used segments are found for the given inputs, this API returns `204 No Content` + +* `/druid/coordinator/v1/datasources/{dataSourceName}/loadstatus?full&forceMetadataRefresh={boolean}&interval={myInterval}` + +Returns the number of segments left to load in each tier until segments that should be loaded in the cluster are all available for the given datasource +over the given interval (or last 2 weeks if interval is not given). This includes segment replication counts. `forceMetadataRefresh` is required to be set. +Setting `forceMetadataRefresh` to true will force the coordinator to poll latest segment metadata from the metadata store +(Note: `forceMetadataRefresh=true` refreshes Coordinator's metadata cache of all datasources. This can be a heavy operation in terms +of the load on the metadata store but can be necessary to make sure that we verify all the latest segments' load status) +Setting `forceMetadataRefresh` to false will use the metadata cached on the coordinator from the last force/periodic refresh. +If no used segments are found for the given inputs, this API returns `204 No Content` + #### Metadata store information ##### GET diff --git a/server/src/main/java/org/apache/druid/client/CoordinatorServerView.java b/server/src/main/java/org/apache/druid/client/CoordinatorServerView.java index 2517a8f0e9be..538cc2f526f3 100644 --- a/server/src/main/java/org/apache/druid/client/CoordinatorServerView.java +++ b/server/src/main/java/org/apache/druid/client/CoordinatorServerView.java @@ -200,6 +200,10 @@ public VersionedIntervalTimeline getTimeline(DataSource } } + public Map getSegmentLoadInfos() + { + return segmentLoadInfos; + } @Override public DruidServer getInventoryValue(String serverKey) diff --git a/server/src/main/java/org/apache/druid/metadata/SegmentsMetadataManager.java b/server/src/main/java/org/apache/druid/metadata/SegmentsMetadataManager.java index 4f97b158021e..889141a89c15 100644 --- a/server/src/main/java/org/apache/druid/metadata/SegmentsMetadataManager.java +++ b/server/src/main/java/org/apache/druid/metadata/SegmentsMetadataManager.java @@ -20,6 +20,7 @@ package org.apache.druid.metadata; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; import org.apache.druid.client.DataSourcesSnapshot; import org.apache.druid.client.ImmutableDruidDataSource; import org.apache.druid.timeline.DataSegment; @@ -113,6 +114,22 @@ int markAsUsedNonOvershadowedSegments(String dataSource, Set segmentIds) */ Iterable iterateAllUsedSegments(); + /** + * Returns an iterable to go over all used and non-overshadowed segments of given data sources over given interval. + * The order in which segments are iterated is unspecified. Note: the iteration may not be as trivially cheap as, + * for example, iteration over an ArrayList. Try (to some reasonable extent) to organize the code so that it + * iterates the returned iterable only once rather than several times. + * If {@param requiresLatest} is true then a force metadatastore poll will be triggered. This can cause a longer + * response time but will ensure that the latest segment information (at the time this method is called) is returned. + * If {@param requiresLatest} is false then segment information from stale snapshot of up to the last periodic poll + * period {@link SqlSegmentsMetadataManager#periodicPollDelay} will be used. + */ + Optional> iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval( + String datasource, + Interval interval, + boolean requiresLatest + ); + /** * Retrieves all data source names for which there are segment in the database, regardless of whether those segments * are used or not. If there are no segments in the database, returns an empty set. diff --git a/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataManager.java b/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataManager.java index 92a874863527..60f7f4b86eeb 100644 --- a/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataManager.java +++ b/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataManager.java @@ -20,6 +20,8 @@ package org.apache.druid.metadata; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.base.Throwables; @@ -44,6 +46,7 @@ import org.apache.druid.java.util.common.lifecycle.LifecycleStop; import org.apache.druid.java.util.emitter.EmittingLogger; import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.Partitions; import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.VersionedIntervalTimeline; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -95,7 +98,8 @@ private interface DatabasePoll {} /** Represents periodic {@link #poll}s happening from {@link #exec}. */ - private static class PeriodicDatabasePoll implements DatabasePoll + @VisibleForTesting + static class PeriodicDatabasePoll implements DatabasePoll { /** * This future allows to wait until {@link #dataSourcesSnapshot} is initialized in the first {@link #poll()} @@ -104,13 +108,15 @@ private static class PeriodicDatabasePoll implements DatabasePoll * leadership changes. */ final CompletableFuture firstPollCompletionFuture = new CompletableFuture<>(); + long lastPollStartTimestampInMs = -1; } /** * Represents on-demand {@link #poll} initiated at periods of time when SqlSegmentsMetadataManager doesn't poll the database * periodically. */ - private static class OnDemandDatabasePoll implements DatabasePoll + @VisibleForTesting + static class OnDemandDatabasePoll implements DatabasePoll { final long initiationTimeNanos = System.nanoTime(); final CompletableFuture pollCompletionFuture = new CompletableFuture<>(); @@ -127,7 +133,7 @@ long nanosElapsedFromInitiation() * called at the same time if two different threads are calling them. This might be possible if Coordinator gets and * drops leadership repeatedly in quick succession. * - * This lock is also used to synchronize {@link #awaitOrPerformDatabasePoll} for times when SqlSegmentsMetadataManager + * This lock is also used to synchronize {@link #useLatestIfWithinDelayOrPerformNewDatabasePoll} for times when SqlSegmentsMetadataManager * is not polling the database periodically (in other words, when the Coordinator is not the leader). */ private final ReentrantReadWriteLock startStopPollLock = new ReentrantReadWriteLock(); @@ -155,7 +161,7 @@ long nanosElapsedFromInitiation() * easy to forget to do. * * This field may be updated from {@link #exec}, or from whatever thread calling {@link #doOnDemandPoll} via {@link - * #awaitOrPerformDatabasePoll()} via one of the public methods of SqlSegmentsMetadataManager. + * #useLatestIfWithinDelayOrPerformNewDatabasePoll()} via one of the public methods of SqlSegmentsMetadataManager. */ private volatile @MonotonicNonNull DataSourcesSnapshot dataSourcesSnapshot = null; @@ -170,7 +176,7 @@ long nanosElapsedFromInitiation() * Note that if there is a happens-before relationship between a call to {@link #startPollingDatabasePeriodically()} * (on Coordinators' leadership change) and one of the methods accessing the {@link #dataSourcesSnapshot}'s state in * this class the latter is guaranteed to await for the initiated periodic poll. This is because when the latter - * method calls to {@link #awaitLatestDatabasePoll()} via {@link #awaitOrPerformDatabasePoll}, they will + * method calls to {@link #useLatestSnapshotIfWithinDelay()} via {@link #useLatestIfWithinDelayOrPerformNewDatabasePoll}, they will * see the latest {@link PeriodicDatabasePoll} value (stored in this field, latestDatabasePoll, in {@link * #startPollingDatabasePeriodically()}) and to await on its {@link PeriodicDatabasePoll#firstPollCompletionFuture}. * @@ -185,7 +191,7 @@ long nanosElapsedFromInitiation() * SegmentsMetadataManager} and guarantee that it always returns consistent and relatively up-to-date data from methods * like {@link #getImmutableDataSourceWithUsedSegments}, while avoiding excessive repetitive polls. The last part * is achieved via "hooking on" other polls by awaiting on {@link PeriodicDatabasePoll#firstPollCompletionFuture} or - * {@link OnDemandDatabasePoll#pollCompletionFuture}, see {@link #awaitOrPerformDatabasePoll} method + * {@link OnDemandDatabasePoll#pollCompletionFuture}, see {@link #useLatestIfWithinDelayOrPerformNewDatabasePoll} method * implementation for details. * * Note: the overall implementation of periodic/on-demand polls is not completely optimal: for example, when the @@ -194,7 +200,7 @@ long nanosElapsedFromInitiation() * during Coordinator leadership switches is not a priority. * * This field is {@code volatile} because it's checked and updated in a double-checked locking manner in {@link - * #awaitOrPerformDatabasePoll()}. + * #useLatestIfWithinDelayOrPerformNewDatabasePoll()}. */ private volatile @Nullable DatabasePoll latestDatabasePoll = null; @@ -311,6 +317,22 @@ public void startPollingDatabasePeriodically() private Runnable createPollTaskForStartOrder(long startOrder, PeriodicDatabasePoll periodicDatabasePoll) { return () -> { + // If latest poll was an OnDemandDatabasePoll that started less than periodicPollDelay, + // We will wait for (periodicPollDelay - currentTime - LatestOnDemandDatabasePollStartTime) then check again. + try { + long periodicPollDelayNanos = TimeUnit.MILLISECONDS.toNanos(periodicPollDelay.getMillis()); + while (latestDatabasePoll != null + && latestDatabasePoll instanceof OnDemandDatabasePoll + && ((OnDemandDatabasePoll) latestDatabasePoll).nanosElapsedFromInitiation() < periodicPollDelayNanos) { + long sleepNano = periodicPollDelayNanos + - ((OnDemandDatabasePoll) latestDatabasePoll).nanosElapsedFromInitiation(); + TimeUnit.NANOSECONDS.sleep(sleepNano); + } + } + catch (Exception e) { + log.debug(e, "Exception found while waiting for next periodic poll"); + } + // poll() is synchronized together with startPollingDatabasePeriodically(), stopPollingDatabasePeriodically() and // isPollingDatabasePeriodically() to ensure that when stopPollingDatabasePeriodically() exits, poll() won't // actually run anymore after that (it could only enter the synchronized section and exit immediately because the @@ -320,8 +342,10 @@ private Runnable createPollTaskForStartOrder(long startOrder, PeriodicDatabasePo lock.lock(); try { if (startOrder == currentStartPollingOrder) { + periodicDatabasePoll.lastPollStartTimestampInMs = System.currentTimeMillis(); poll(); periodicDatabasePoll.firstPollCompletionFuture.complete(null); + latestDatabasePoll = periodicDatabasePoll; } else { log.debug("startOrder = currentStartPollingOrder = %d, skipping poll()", startOrder); } @@ -381,16 +405,16 @@ public void stopPollingDatabasePeriodically() } } - private void awaitOrPerformDatabasePoll() + private void useLatestIfWithinDelayOrPerformNewDatabasePoll() { - // Double-checked locking with awaitLatestDatabasePoll() call playing the role of the "check". - if (awaitLatestDatabasePoll()) { + // Double-checked locking with useLatestSnapshotIfWithinDelay() call playing the role of the "check". + if (useLatestSnapshotIfWithinDelay()) { return; } ReentrantReadWriteLock.WriteLock lock = startStopPollLock.writeLock(); lock.lock(); try { - if (awaitLatestDatabasePoll()) { + if (useLatestSnapshotIfWithinDelay()) { return; } OnDemandDatabasePoll onDemandDatabasePoll = new OnDemandDatabasePoll(); @@ -403,11 +427,17 @@ private void awaitOrPerformDatabasePoll() } /** - * If the latest {@link DatabasePoll} is a {@link PeriodicDatabasePoll}, or an {@link OnDemandDatabasePoll} that is - * made not longer than {@link #periodicPollDelay} from now, awaits for it and returns true; returns false otherwise, - * meaning that a new on-demand database poll should be initiated. + * This method returns true without waiting for database poll if the latest {@link DatabasePoll} is a + * {@link PeriodicDatabasePoll} that has completed it's first poll, or an {@link OnDemandDatabasePoll} that is + * made not longer than {@link #periodicPollDelay} from current time. + * This method does wait untill completion for if the latest {@link DatabasePoll} is a + * {@link PeriodicDatabasePoll} that has not completed it's first poll, or an {@link OnDemandDatabasePoll} that is + * already in the process of polling the database. + * This means that any method using this check can read from snapshot that is + * up to {@link SqlSegmentsMetadataManager#periodicPollDelay} old. */ - private boolean awaitLatestDatabasePoll() + @VisibleForTesting + boolean useLatestSnapshotIfWithinDelay() { DatabasePoll latestDatabasePoll = this.latestDatabasePoll; if (latestDatabasePoll instanceof PeriodicDatabasePoll) { @@ -430,6 +460,49 @@ private boolean awaitLatestDatabasePoll() return false; } + /** + * This method will always force a database poll if there is no ongoing database poll. This method will then + * waits for the new poll or the ongoing poll to completes before returning. + * This means that any method using this check can be sure that the latest poll for the snapshot was completed after + * this method was called. + */ + @VisibleForTesting + void forceOrWaitOngoingDatabasePoll() + { + long checkStartTime = System.currentTimeMillis(); + ReentrantReadWriteLock.WriteLock lock = startStopPollLock.writeLock(); + lock.lock(); + try { + DatabasePoll latestDatabasePoll = this.latestDatabasePoll; + try { + //Verify if there was a periodic poll completed while we were waiting for the lock + if (latestDatabasePoll instanceof PeriodicDatabasePoll + && ((PeriodicDatabasePoll) latestDatabasePoll).lastPollStartTimestampInMs > checkStartTime) { + return; + } + // Verify if there was a on-demand poll completed while we were waiting for the lock + if (latestDatabasePoll instanceof OnDemandDatabasePoll) { + long checkStartTimeNanos = TimeUnit.MILLISECONDS.toNanos(checkStartTime); + OnDemandDatabasePoll latestOnDemandPoll = (OnDemandDatabasePoll) latestDatabasePoll; + if (latestOnDemandPoll.initiationTimeNanos > checkStartTimeNanos) { + return; + } + } + } + catch (Exception e) { + // Latest poll was unsuccessful, try to do a new poll + log.debug(e, "Latest poll was unsuccessful. Starting a new poll..."); + } + // Force a database poll + OnDemandDatabasePoll onDemandDatabasePoll = new OnDemandDatabasePoll(); + this.latestDatabasePoll = onDemandDatabasePoll; + doOnDemandPoll(onDemandDatabasePoll); + } + finally { + lock.unlock(); + } + } + private void doOnDemandPoll(OnDemandDatabasePoll onDemandPoll) { try { @@ -857,19 +930,44 @@ public Set getOvershadowedSegments() @Override public DataSourcesSnapshot getSnapshotOfDataSourcesWithAllUsedSegments() { - awaitOrPerformDatabasePoll(); + useLatestIfWithinDelayOrPerformNewDatabasePoll(); return dataSourcesSnapshot; } + @VisibleForTesting + DataSourcesSnapshot getDataSourcesSnapshot() + { + return dataSourcesSnapshot; + } + + @VisibleForTesting + DatabasePoll getLatestDatabasePoll() + { + return latestDatabasePoll; + } + + @Override public Iterable iterateAllUsedSegments() { - awaitOrPerformDatabasePoll(); - return () -> dataSourcesSnapshot - .getDataSourcesWithAllUsedSegments() - .stream() - .flatMap(dataSource -> dataSource.getSegments().stream()) - .iterator(); + useLatestIfWithinDelayOrPerformNewDatabasePoll(); + return dataSourcesSnapshot.iterateAllUsedSegmentsInSnapshot(); + } + + @Override + public Optional> iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval(String datasource, + Interval interval, + boolean requiresLatest) + { + if (requiresLatest) { + forceOrWaitOngoingDatabasePoll(); + } else { + useLatestIfWithinDelayOrPerformNewDatabasePoll(); + } + VersionedIntervalTimeline usedSegmentsTimeline + = dataSourcesSnapshot.getUsedSegmentsTimelinesPerDataSource().get(datasource); + return Optional.fromNullable(usedSegmentsTimeline) + .transform(timeline -> timeline.findNonOvershadowedObjectsInInterval(interval, Partitions.ONLY_COMPLETE)); } @Override diff --git a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java index 36a414e780d9..c4de3644c64c 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java @@ -256,6 +256,17 @@ public Map getLoadManagementPeons() * @return tier -> { dataSource -> underReplicationCount } map */ public Map> computeUnderReplicationCountsPerDataSourcePerTier() + { + final Iterable dataSegments = segmentsMetadataManager.iterateAllUsedSegments(); + return computeUnderReplicationCountsPerDataSourcePerTierForSegments(dataSegments); + } + + /** + * @return tier -> { dataSource -> underReplicationCount } map + */ + public Map> computeUnderReplicationCountsPerDataSourcePerTierForSegments( + Iterable dataSegments + ) { final Map> underReplicationCountsPerDataSourcePerTier = new HashMap<>(); @@ -263,8 +274,6 @@ public Map> computeUnderReplicationCountsPerDataS return underReplicationCountsPerDataSourcePerTier; } - final Iterable dataSegments = segmentsMetadataManager.iterateAllUsedSegments(); - final DateTime now = DateTimes.nowUtc(); for (final DataSegment segment : dataSegments) { @@ -320,7 +329,7 @@ public Map getLoadStatus() for (ImmutableDruidDataSource dataSource : dataSources) { final Set segments = Sets.newHashSet(dataSource.getSegments()); - final int numUsedSegments = segments.size(); + final int numPublishedSegments = segments.size(); // remove loaded segments for (DruidServer druidServer : serverInventoryView.getInventory()) { @@ -333,10 +342,10 @@ public Map getLoadStatus() } } } - final int numUnloadedSegments = segments.size(); + final int numUnavailableSegments = segments.size(); loadStatus.put( dataSource.getName(), - 100 * ((double) (numUsedSegments - numUnloadedSegments) / (double) numUsedSegments) + 100 * ((double) (numPublishedSegments - numUnavailableSegments) / (double) numPublishedSegments) ); } diff --git a/server/src/main/java/org/apache/druid/server/http/DataSourcesResource.java b/server/src/main/java/org/apache/druid/server/http/DataSourcesResource.java index b6d310f1ba73..f88d1c70854c 100644 --- a/server/src/main/java/org/apache/druid/server/http/DataSourcesResource.java +++ b/server/src/main/java/org/apache/druid/server/http/DataSourcesResource.java @@ -22,11 +22,13 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.inject.Inject; import com.sun.jersey.spi.container.ResourceFilters; +import it.unimi.dsi.fastutil.objects.Object2LongMap; import org.apache.commons.lang.StringUtils; import org.apache.druid.client.CoordinatorServerView; import org.apache.druid.client.DruidDataSource; @@ -49,6 +51,7 @@ import org.apache.druid.query.SegmentDescriptor; import org.apache.druid.query.TableDataSource; import org.apache.druid.server.coordination.DruidServerMetadata; +import org.apache.druid.server.coordinator.DruidCoordinator; import org.apache.druid.server.coordinator.rules.LoadRule; import org.apache.druid.server.coordinator.rules.Rule; import org.apache.druid.server.http.security.DatasourceResourceFilter; @@ -96,12 +99,14 @@ public class DataSourcesResource { private static final Logger log = new Logger(DataSourcesResource.class); + private static final long DEFAULT_LOADSTATUS_INTERVAL_OFFSET = 14 * 24 * 60 * 60 * 1000; private final CoordinatorServerView serverInventoryView; private final SegmentsMetadataManager segmentsMetadataManager; private final MetadataRuleManager metadataRuleManager; private final IndexingServiceClient indexingServiceClient; private final AuthorizerMapper authorizerMapper; + private final DruidCoordinator coordinator; @Inject public DataSourcesResource( @@ -109,7 +114,8 @@ public DataSourcesResource( SegmentsMetadataManager segmentsMetadataManager, MetadataRuleManager metadataRuleManager, @Nullable IndexingServiceClient indexingServiceClient, - AuthorizerMapper authorizerMapper + AuthorizerMapper authorizerMapper, + DruidCoordinator coordinator ) { this.serverInventoryView = serverInventoryView; @@ -117,6 +123,7 @@ public DataSourcesResource( this.metadataRuleManager = metadataRuleManager; this.indexingServiceClient = indexingServiceClient; this.authorizerMapper = authorizerMapper; + this.coordinator = coordinator; } @GET @@ -391,6 +398,130 @@ public Response getServedSegmentsInInterval( return getServedSegmentsInInterval(dataSourceName, full != null, theInterval::contains); } + @GET + @Path("/{dataSourceName}/loadstatus") + @Produces(MediaType.APPLICATION_JSON) + @ResourceFilters(DatasourceResourceFilter.class) + public Response getDatasourceLoadstatus( + @PathParam("dataSourceName") String dataSourceName, + @QueryParam("forceMetadataRefresh") final Boolean forceMetadataRefresh, + @QueryParam("interval") @Nullable final String interval, + @QueryParam("simple") @Nullable final String simple, + @QueryParam("full") @Nullable final String full + ) + { + if (forceMetadataRefresh == null) { + return Response + .status(Response.Status.BAD_REQUEST) + .entity("Invalid request. forceMetadataRefresh must be specified") + .build(); + } + final Interval theInterval; + if (interval == null) { + long currentTimeInMs = System.currentTimeMillis(); + theInterval = Intervals.utc(currentTimeInMs - DEFAULT_LOADSTATUS_INTERVAL_OFFSET, currentTimeInMs); + } else { + theInterval = Intervals.of(interval.replace('_', '/')); + } + + Optional> segments = segmentsMetadataManager.iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval( + dataSourceName, + theInterval, + forceMetadataRefresh + ); + + if (!segments.isPresent()) { + return logAndCreateDataSourceNotFoundResponse(dataSourceName); + } + + if (Iterables.size(segments.get()) == 0) { + return Response + .status(Response.Status.NO_CONTENT) + .entity("No used segment found for the given datasource and interval") + .build(); + } + + if (simple != null) { + // Calculate response for simple mode + SegmentsLoadStatistics segmentsLoadStatistics = computeSegmentLoadStatistics(segments.get()); + return Response.ok( + ImmutableMap.of( + dataSourceName, + segmentsLoadStatistics.getNumUnavailableSegments() + ) + ).build(); + } else if (full != null) { + // Calculate response for full mode + Map> segmentLoadMap + = coordinator.computeUnderReplicationCountsPerDataSourcePerTierForSegments(segments.get()); + if (segmentLoadMap.isEmpty()) { + return Response.serverError() + .entity("Coordinator segment replicant lookup is not initialized yet. Try again later.") + .build(); + } + return Response.ok(segmentLoadMap).build(); + } else { + // Calculate response for default mode + SegmentsLoadStatistics segmentsLoadStatistics = computeSegmentLoadStatistics(segments.get()); + return Response.ok( + ImmutableMap.of( + dataSourceName, + 100 * ((double) (segmentsLoadStatistics.getNumLoadedSegments()) / (double) segmentsLoadStatistics.getNumPublishedSegments()) + ) + ).build(); + } + } + + private SegmentsLoadStatistics computeSegmentLoadStatistics(Iterable segments) + { + Map segmentLoadInfos = serverInventoryView.getSegmentLoadInfos(); + int numPublishedSegments = 0; + int numUnavailableSegments = 0; + int numLoadedSegments = 0; + for (DataSegment segment : segments) { + numPublishedSegments++; + if (!segmentLoadInfos.containsKey(segment.getId())) { + numUnavailableSegments++; + } else { + numLoadedSegments++; + } + } + return new SegmentsLoadStatistics(numPublishedSegments, numUnavailableSegments, numLoadedSegments); + } + + private static class SegmentsLoadStatistics + { + private int numPublishedSegments; + private int numUnavailableSegments; + private int numLoadedSegments; + + SegmentsLoadStatistics( + int numPublishedSegments, + int numUnavailableSegments, + int numLoadedSegments + ) + { + this.numPublishedSegments = numPublishedSegments; + this.numUnavailableSegments = numUnavailableSegments; + this.numLoadedSegments = numLoadedSegments; + } + + public int getNumPublishedSegments() + { + return numPublishedSegments; + } + + public int getNumUnavailableSegments() + { + return numUnavailableSegments; + } + + public int getNumLoadedSegments() + { + return numLoadedSegments; + } + } + /** * The property names belong to the public HTTP JSON API. */ diff --git a/server/src/test/java/org/apache/druid/metadata/SqlSegmentsMetadataManagerTest.java b/server/src/test/java/org/apache/druid/metadata/SqlSegmentsMetadataManagerTest.java index be6354a63c75..57df44073460 100644 --- a/server/src/test/java/org/apache/druid/metadata/SqlSegmentsMetadataManagerTest.java +++ b/server/src/test/java/org/apache/druid/metadata/SqlSegmentsMetadataManagerTest.java @@ -20,11 +20,13 @@ package org.apache.druid.metadata; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Optional; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import org.apache.druid.client.DataSourcesSnapshot; import org.apache.druid.client.ImmutableDruidDataSource; import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.Intervals; @@ -43,6 +45,7 @@ import org.junit.Test; import java.io.IOException; +import java.util.Set; import java.util.stream.Collectors; @@ -117,7 +120,7 @@ public void setUp() throws Exception { TestDerbyConnector connector = derbyConnectorRule.getConnector(); SegmentsMetadataManagerConfig config = new SegmentsMetadataManagerConfig(); - config.setPollDuration(Period.seconds(1)); + config.setPollDuration(Period.seconds(3)); sqlSegmentsMetadataManager = new SqlSegmentsMetadataManager( jsonMapper, Suppliers.ofInstance(config), @@ -148,30 +151,124 @@ public void teardown() } @Test - public void testPoll() + public void testPollPeriodically() { + DataSourcesSnapshot dataSourcesSnapshot = sqlSegmentsMetadataManager.getDataSourcesSnapshot(); + Assert.assertNull(dataSourcesSnapshot); sqlSegmentsMetadataManager.startPollingDatabasePeriodically(); - sqlSegmentsMetadataManager.poll(); Assert.assertTrue(sqlSegmentsMetadataManager.isPollingDatabasePeriodically()); + // This call make sure that the first poll is completed + sqlSegmentsMetadataManager.useLatestSnapshotIfWithinDelay(); + Assert.assertTrue(sqlSegmentsMetadataManager.getLatestDatabasePoll() instanceof SqlSegmentsMetadataManager.PeriodicDatabasePoll); + dataSourcesSnapshot = sqlSegmentsMetadataManager.getDataSourcesSnapshot(); Assert.assertEquals( ImmutableSet.of("wikipedia"), sqlSegmentsMetadataManager.retrieveAllDataSourceNames() ); Assert.assertEquals( ImmutableList.of("wikipedia"), - sqlSegmentsMetadataManager - .getImmutableDataSourcesWithAllUsedSegments() - .stream() - .map(ImmutableDruidDataSource::getName) - .collect(Collectors.toList()) + dataSourcesSnapshot.getDataSourcesWithAllUsedSegments() + .stream() + .map(ImmutableDruidDataSource::getName) + .collect(Collectors.toList()) ); Assert.assertEquals( ImmutableSet.of(segment1, segment2), - ImmutableSet.copyOf(sqlSegmentsMetadataManager.getImmutableDataSourceWithUsedSegments("wikipedia").getSegments()) + ImmutableSet.copyOf(dataSourcesSnapshot.getDataSource("wikipedia").getSegments()) ); Assert.assertEquals( ImmutableSet.of(segment1, segment2), - ImmutableSet.copyOf(sqlSegmentsMetadataManager.iterateAllUsedSegments()) + ImmutableSet.copyOf(dataSourcesSnapshot.iterateAllUsedSegmentsInSnapshot()) + ); + } + + @Test + public void testPollOnDemand() + { + DataSourcesSnapshot dataSourcesSnapshot = sqlSegmentsMetadataManager.getDataSourcesSnapshot(); + Assert.assertNull(dataSourcesSnapshot); + // This should return false and not wait/poll anything as we did not schedule periodic poll + Assert.assertFalse(sqlSegmentsMetadataManager.useLatestSnapshotIfWithinDelay()); + Assert.assertNull(dataSourcesSnapshot); + // This call will force on demand poll + sqlSegmentsMetadataManager.forceOrWaitOngoingDatabasePoll(); + Assert.assertFalse(sqlSegmentsMetadataManager.isPollingDatabasePeriodically()); + Assert.assertTrue(sqlSegmentsMetadataManager.getLatestDatabasePoll() instanceof SqlSegmentsMetadataManager.OnDemandDatabasePoll); + dataSourcesSnapshot = sqlSegmentsMetadataManager.getDataSourcesSnapshot(); + Assert.assertEquals( + ImmutableSet.of("wikipedia"), + sqlSegmentsMetadataManager.retrieveAllDataSourceNames() + ); + Assert.assertEquals( + ImmutableList.of("wikipedia"), + dataSourcesSnapshot.getDataSourcesWithAllUsedSegments() + .stream() + .map(ImmutableDruidDataSource::getName) + .collect(Collectors.toList()) + ); + Assert.assertEquals( + ImmutableSet.of(segment1, segment2), + ImmutableSet.copyOf(dataSourcesSnapshot.getDataSource("wikipedia").getSegments()) + ); + Assert.assertEquals( + ImmutableSet.of(segment1, segment2), + ImmutableSet.copyOf(dataSourcesSnapshot.iterateAllUsedSegmentsInSnapshot()) + ); + } + + @Test(timeout = 60_000) + public void testPollPeriodicallyAndOnDemandInterleave() throws Exception + { + DataSourcesSnapshot dataSourcesSnapshot = sqlSegmentsMetadataManager.getDataSourcesSnapshot(); + Assert.assertNull(dataSourcesSnapshot); + sqlSegmentsMetadataManager.startPollingDatabasePeriodically(); + Assert.assertTrue(sqlSegmentsMetadataManager.isPollingDatabasePeriodically()); + // This call make sure that the first poll is completed + sqlSegmentsMetadataManager.useLatestSnapshotIfWithinDelay(); + Assert.assertTrue(sqlSegmentsMetadataManager.getLatestDatabasePoll() instanceof SqlSegmentsMetadataManager.PeriodicDatabasePoll); + dataSourcesSnapshot = sqlSegmentsMetadataManager.getDataSourcesSnapshot(); + Assert.assertEquals( + ImmutableList.of("wikipedia"), + dataSourcesSnapshot.getDataSourcesWithAllUsedSegments() + .stream() + .map(ImmutableDruidDataSource::getName) + .collect(Collectors.toList()) + ); + final String newDataSource2 = "wikipedia2"; + final DataSegment newSegment2 = createNewSegment1(newDataSource2); + publisher.publishSegment(newSegment2); + + // This call will force on demand poll + sqlSegmentsMetadataManager.forceOrWaitOngoingDatabasePoll(); + Assert.assertTrue(sqlSegmentsMetadataManager.isPollingDatabasePeriodically()); + Assert.assertTrue(sqlSegmentsMetadataManager.getLatestDatabasePoll() instanceof SqlSegmentsMetadataManager.OnDemandDatabasePoll); + // New datasource should now be in the snapshot since we just force on demand poll. + dataSourcesSnapshot = sqlSegmentsMetadataManager.getDataSourcesSnapshot(); + Assert.assertEquals( + ImmutableList.of("wikipedia2", "wikipedia"), + dataSourcesSnapshot.getDataSourcesWithAllUsedSegments() + .stream() + .map(ImmutableDruidDataSource::getName) + .collect(Collectors.toList()) + ); + + final String newDataSource3 = "wikipedia3"; + final DataSegment newSegment3 = createNewSegment1(newDataSource3); + publisher.publishSegment(newSegment3); + + // This time wait for periodic poll (not doing on demand poll so we have to wait a bit...) + while (sqlSegmentsMetadataManager.getDataSourcesSnapshot().getDataSource(newDataSource3) == null) { + Thread.sleep(1000); + } + Assert.assertTrue(sqlSegmentsMetadataManager.isPollingDatabasePeriodically()); + Assert.assertTrue(sqlSegmentsMetadataManager.getLatestDatabasePoll() instanceof SqlSegmentsMetadataManager.PeriodicDatabasePoll); + dataSourcesSnapshot = sqlSegmentsMetadataManager.getDataSourcesSnapshot(); + Assert.assertEquals( + ImmutableList.of("wikipedia2", "wikipedia3", "wikipedia"), + dataSourcesSnapshot.getDataSourcesWithAllUsedSegments() + .stream() + .map(ImmutableDruidDataSource::getName) + .collect(Collectors.toList()) ); } @@ -749,4 +846,46 @@ public void testStopAndStart() sqlSegmentsMetadataManager.startPollingDatabasePeriodically(); sqlSegmentsMetadataManager.stopPollingDatabasePeriodically(); } + + @Test + public void testIterateAllUsedNonOvershadowedSegmentsForDatasourceInterval() throws Exception + { + final Interval theInterval = Intervals.of("2012-03-15T00:00:00.000/2012-03-20T00:00:00.000"); + Optional> segments = sqlSegmentsMetadataManager.iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval( + "wikipedia", theInterval, true + ); + Assert.assertTrue(segments.isPresent()); + Set dataSegmentSet = ImmutableSet.copyOf(segments.get()); + Assert.assertEquals(1, dataSegmentSet.size()); + Assert.assertTrue(dataSegmentSet.contains(segment1)); + + final DataSegment newSegment2 = createSegment( + "wikipedia", + "2012-03-16T00:00:00.000/2012-03-17T00:00:00.000", + "2017-10-15T20:19:12.565Z", + "index/y=2017/m=10/d=15/2017-10-16T20:19:12.565Z/0/index.zip", + 0 + ); + publisher.publishSegment(newSegment2); + + // New segment is not returned since we call without force poll + segments = sqlSegmentsMetadataManager.iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval( + "wikipedia", theInterval, false + ); + Assert.assertTrue(segments.isPresent()); + dataSegmentSet = ImmutableSet.copyOf(segments.get()); + Assert.assertEquals(1, dataSegmentSet.size()); + Assert.assertTrue(dataSegmentSet.contains(segment1)); + + // New segment is returned since we call with force poll + segments = sqlSegmentsMetadataManager.iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval( + "wikipedia", theInterval, true + ); + Assert.assertTrue(segments.isPresent()); + dataSegmentSet = ImmutableSet.copyOf(segments.get()); + Assert.assertEquals(2, dataSegmentSet.size()); + Assert.assertTrue(dataSegmentSet.contains(segment1)); + Assert.assertTrue(dataSegmentSet.contains(newSegment2)); + } + } diff --git a/server/src/test/java/org/apache/druid/server/http/DataSourcesResourceTest.java b/server/src/test/java/org/apache/druid/server/http/DataSourcesResourceTest.java index 10a7f97300c9..39e02ae86def 100644 --- a/server/src/test/java/org/apache/druid/server/http/DataSourcesResourceTest.java +++ b/server/src/test/java/org/apache/druid/server/http/DataSourcesResourceTest.java @@ -19,11 +19,14 @@ package org.apache.druid.server.http; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import org.apache.druid.client.CoordinatorServerView; import org.apache.druid.client.DruidDataSource; import org.apache.druid.client.DruidServer; @@ -39,6 +42,7 @@ import org.apache.druid.query.TableDataSource; import org.apache.druid.server.coordination.DruidServerMetadata; import org.apache.druid.server.coordination.ServerType; +import org.apache.druid.server.coordinator.DruidCoordinator; import org.apache.druid.server.coordinator.rules.IntervalDropRule; import org.apache.druid.server.coordinator.rules.IntervalLoadRule; import org.apache.druid.server.coordinator.rules.Rule; @@ -176,7 +180,7 @@ public void testGetFullQueryableDataSources() EasyMock.replay(inventoryView, server, request); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, null, null, null, AuthTestUtils.TEST_AUTHORIZER_MAPPER); + new DataSourcesResource(inventoryView, null, null, null, AuthTestUtils.TEST_AUTHORIZER_MAPPER, null); Response response = dataSourcesResource.getQueryableDataSources("full", null, request); Set result = (Set) response.getEntity(); Assert.assertEquals(200, response.getStatus()); @@ -250,7 +254,7 @@ public Access authorize(AuthenticationResult authenticationResult1, Resource res } }; - DataSourcesResource dataSourcesResource = new DataSourcesResource(inventoryView, null, null, null, authMapper); + DataSourcesResource dataSourcesResource = new DataSourcesResource(inventoryView, null, null, null, authMapper, null); Response response = dataSourcesResource.getQueryableDataSources("full", null, request); Set result = (Set) response.getEntity(); @@ -289,7 +293,7 @@ public void testGetSimpleQueryableDataSources() EasyMock.replay(inventoryView, server, request); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, null, null, null, AuthTestUtils.TEST_AUTHORIZER_MAPPER); + new DataSourcesResource(inventoryView, null, null, null, AuthTestUtils.TEST_AUTHORIZER_MAPPER, null); Response response = dataSourcesResource.getQueryableDataSources(null, "simple", request); Assert.assertEquals(200, response.getStatus()); List> results = (List>) response.getEntity(); @@ -313,7 +317,7 @@ public void testFullGetTheDataSource() EasyMock.replay(inventoryView, server); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, null, null, null, null); + new DataSourcesResource(inventoryView, null, null, null, null, null); Response response = dataSourcesResource.getDataSource("datasource1", "full"); ImmutableDruidDataSource result = (ImmutableDruidDataSource) response.getEntity(); Assert.assertEquals(200, response.getStatus()); @@ -329,7 +333,7 @@ public void testNullGetTheDataSource() EasyMock.replay(inventoryView, server); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, null, null, null, null); + new DataSourcesResource(inventoryView, null, null, null, null, null); Assert.assertEquals(204, dataSourcesResource.getDataSource("none", null).getStatus()); EasyMock.verify(inventoryView, server); } @@ -347,7 +351,7 @@ public void testSimpleGetTheDataSource() EasyMock.replay(inventoryView, server); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, null, null, null, null); + new DataSourcesResource(inventoryView, null, null, null, null, null); Response response = dataSourcesResource.getDataSource("datasource1", null); Assert.assertEquals(200, response.getStatus()); Map> result = (Map>) response.getEntity(); @@ -380,7 +384,7 @@ public void testSimpleGetTheDataSourceManyTiers() EasyMock.expect(inventoryView.getInventory()).andReturn(ImmutableList.of(server, server2, server3)).atLeastOnce(); EasyMock.replay(inventoryView, server, server2, server3); - DataSourcesResource dataSourcesResource = new DataSourcesResource(inventoryView, null, null, null, null); + DataSourcesResource dataSourcesResource = new DataSourcesResource(inventoryView, null, null, null, null, null); Response response = dataSourcesResource.getDataSource("datasource1", null); Assert.assertEquals(200, response.getStatus()); Map> result = (Map>) response.getEntity(); @@ -418,7 +422,7 @@ public void testSimpleGetTheDataSourceWithReplicatedSegments() EasyMock.replay(inventoryView); - DataSourcesResource dataSourcesResource = new DataSourcesResource(inventoryView, null, null, null, null); + DataSourcesResource dataSourcesResource = new DataSourcesResource(inventoryView, null, null, null, null, null); Response response = dataSourcesResource.getDataSource("datasource1", null); Assert.assertEquals(200, response.getStatus()); Map> result1 = (Map>) response.getEntity(); @@ -463,7 +467,7 @@ public void testGetSegmentDataSourceIntervals() expectedIntervals.add(Intervals.of("2010-01-22T00:00:00.000Z/2010-01-23T00:00:00.000Z")); expectedIntervals.add(Intervals.of("2010-01-01T00:00:00.000Z/2010-01-02T00:00:00.000Z")); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, null, null, null, null); + new DataSourcesResource(inventoryView, null, null, null, null, null); Response response = dataSourcesResource.getIntervalsWithServedSegmentsOrAllServedSegmentsPerIntervals( "invalidDataSource", @@ -523,7 +527,7 @@ public void testGetServedSegmentsInIntervalInDataSource() EasyMock.replay(inventoryView); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, null, null, null, null); + new DataSourcesResource(inventoryView, null, null, null, null, null); Response response = dataSourcesResource.getServedSegmentsInInterval( "invalidDataSource", "2010-01-01/P1D", @@ -593,7 +597,7 @@ public void testKillSegmentsInIntervalInDataSource() EasyMock.replay(indexingServiceClient, server); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, null, null, indexingServiceClient, null); + new DataSourcesResource(inventoryView, null, null, indexingServiceClient, null, null); Response response = dataSourcesResource.killUnusedSegmentsInInterval("datasource1", interval); Assert.assertEquals(200, response.getStatus()); @@ -607,7 +611,7 @@ public void testMarkAsUnusedAllSegmentsInDataSource() IndexingServiceClient indexingServiceClient = EasyMock.createStrictMock(IndexingServiceClient.class); EasyMock.replay(indexingServiceClient, server); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, null, null, indexingServiceClient, null); + new DataSourcesResource(inventoryView, null, null, indexingServiceClient, null, null); try { Response response = dataSourcesResource.markAsUnusedAllSegmentsOrKillUnusedSegmentsInInterval("datasource", "true", "???"); @@ -630,7 +634,7 @@ public void testIsHandOffComplete() Rule loadRule = new IntervalLoadRule(Intervals.of("2013-01-02T00:00:00Z/2013-01-03T00:00:00Z"), null); Rule dropRule = new IntervalDropRule(Intervals.of("2013-01-01T00:00:00Z/2013-01-02T00:00:00Z")); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, null, databaseRuleManager, null, null); + new DataSourcesResource(inventoryView, null, databaseRuleManager, null, null, null); // test dropped EasyMock.expect(databaseRuleManager.getRulesWithDefault("dataSource1")) @@ -699,7 +703,7 @@ public void testMarkSegmentAsUsed() EasyMock.expect(segmentsMetadataManager.markSegmentAsUsed(segment.getId().toString())).andReturn(true).once(); EasyMock.replay(segmentsMetadataManager); - DataSourcesResource dataSourcesResource = new DataSourcesResource(null, segmentsMetadataManager, null, null, null); + DataSourcesResource dataSourcesResource = new DataSourcesResource(null, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markSegmentAsUsed(segment.getDataSource(), segment.getId().toString()); Assert.assertEquals(200, response.getStatus()); @@ -713,7 +717,7 @@ public void testMarkSegmentAsUsedNoChange() EasyMock.expect(segmentsMetadataManager.markSegmentAsUsed(segment.getId().toString())).andReturn(false).once(); EasyMock.replay(segmentsMetadataManager); - DataSourcesResource dataSourcesResource = new DataSourcesResource(null, segmentsMetadataManager, null, null, null); + DataSourcesResource dataSourcesResource = new DataSourcesResource(null, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markSegmentAsUsed(segment.getDataSource(), segment.getId().toString()); Assert.assertEquals(200, response.getStatus()); @@ -734,7 +738,7 @@ public void testMarkAsUsedNonOvershadowedSegmentsInterval() EasyMock.replay(segmentsMetadataManager, inventoryView, server); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markAsUsedNonOvershadowedSegments( "datasource1", @@ -757,7 +761,7 @@ public void testMarkAsUsedNonOvershadowedSegmentsIntervalNoneUpdated() EasyMock.replay(segmentsMetadataManager, inventoryView, server); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markAsUsedNonOvershadowedSegments( "datasource1", @@ -780,7 +784,7 @@ public void testMarkAsUsedNonOvershadowedSegmentsSet() throws UnknownSegmentIdsE EasyMock.replay(segmentsMetadataManager, inventoryView, server); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markAsUsedNonOvershadowedSegments( "datasource1", @@ -803,7 +807,7 @@ public void testMarkAsUsedNonOvershadowedSegmentsIntervalException() EasyMock.replay(segmentsMetadataManager, inventoryView, server); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markAsUsedNonOvershadowedSegments( "datasource1", @@ -821,7 +825,7 @@ public void testMarkAsUsedNonOvershadowedSegmentsNoDataSource() EasyMock.replay(segmentsMetadataManager, inventoryView, server); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markAsUsedNonOvershadowedSegments( "datasource1", @@ -835,7 +839,7 @@ public void testMarkAsUsedNonOvershadowedSegmentsNoDataSource() public void testMarkAsUsedNonOvershadowedSegmentsInvalidPayloadNoArguments() { DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markAsUsedNonOvershadowedSegments( "datasource1", @@ -848,7 +852,7 @@ public void testMarkAsUsedNonOvershadowedSegmentsInvalidPayloadNoArguments() public void testMarkAsUsedNonOvershadowedSegmentsInvalidPayloadBothArguments() { DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markAsUsedNonOvershadowedSegments( "datasource1", @@ -861,7 +865,7 @@ public void testMarkAsUsedNonOvershadowedSegmentsInvalidPayloadBothArguments() public void testMarkAsUsedNonOvershadowedSegmentsInvalidPayloadEmptyArray() { DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markAsUsedNonOvershadowedSegments( "datasource1", @@ -874,7 +878,7 @@ public void testMarkAsUsedNonOvershadowedSegmentsInvalidPayloadEmptyArray() public void testMarkAsUsedNonOvershadowedSegmentsNoPayload() { DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markAsUsedNonOvershadowedSegments("datasource1", null); Assert.assertEquals(400, response.getStatus()); @@ -1026,7 +1030,7 @@ public void testMarkSegmentsAsUnused() new DataSourcesResource.MarkDataSourceSegmentsPayload(null, segmentIds); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markSegmentsAsUnused("datasource1", payload); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(ImmutableMap.of("numChangedSegments", 1), response.getEntity()); @@ -1049,7 +1053,7 @@ public void testMarkSegmentsAsUnusedNoChanges() new DataSourcesResource.MarkDataSourceSegmentsPayload(null, segmentIds); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markSegmentsAsUnused("datasource1", payload); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(ImmutableMap.of("numChangedSegments", 0), response.getEntity()); @@ -1074,7 +1078,7 @@ public void testMarkSegmentsAsUnusedException() new DataSourcesResource.MarkDataSourceSegmentsPayload(null, segmentIds); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markSegmentsAsUnused("datasource1", payload); Assert.assertEquals(500, response.getStatus()); Assert.assertNotNull(response.getEntity()); @@ -1096,7 +1100,7 @@ public void testMarkAsUnusedSegmentsInInterval() new DataSourcesResource.MarkDataSourceSegmentsPayload(theInterval, null); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markSegmentsAsUnused("datasource1", payload); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(ImmutableMap.of("numChangedSegments", 1), response.getEntity()); @@ -1119,7 +1123,7 @@ public void testMarkAsUnusedSegmentsInIntervalNoChanges() new DataSourcesResource.MarkDataSourceSegmentsPayload(theInterval, null); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markSegmentsAsUnused("datasource1", payload); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(ImmutableMap.of("numChangedSegments", 0), response.getEntity()); @@ -1143,7 +1147,7 @@ public void testMarkAsUnusedSegmentsInIntervalException() new DataSourcesResource.MarkDataSourceSegmentsPayload(theInterval, null); DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markSegmentsAsUnused("datasource1", payload); Assert.assertEquals(500, response.getStatus()); Assert.assertNotNull(response.getEntity()); @@ -1154,7 +1158,7 @@ public void testMarkAsUnusedSegmentsInIntervalException() public void testMarkSegmentsAsUnusedNullPayload() { DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); Response response = dataSourcesResource.markSegmentsAsUnused("datasource1", null); Assert.assertEquals(400, response.getStatus()); @@ -1169,7 +1173,7 @@ public void testMarkSegmentsAsUnusedNullPayload() public void testMarkSegmentsAsUnusedInvalidPayload() { DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); final DataSourcesResource.MarkDataSourceSegmentsPayload payload = new DataSourcesResource.MarkDataSourceSegmentsPayload(null, null); @@ -1183,7 +1187,7 @@ public void testMarkSegmentsAsUnusedInvalidPayload() public void testMarkSegmentsAsUnusedInvalidPayloadBothArguments() { DataSourcesResource dataSourcesResource = - new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null); + new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); final DataSourcesResource.MarkDataSourceSegmentsPayload payload = new DataSourcesResource.MarkDataSourceSegmentsPayload(Intervals.of("2010-01-01/P1D"), ImmutableSet.of()); @@ -1193,6 +1197,251 @@ public void testMarkSegmentsAsUnusedInvalidPayloadBothArguments() Assert.assertNotNull(response.getEntity()); } + @Test + public void testGetDatasourceLoadstatusForceMetadataRefreshNull() + { + DataSourcesResource dataSourcesResource = new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); + Response response = dataSourcesResource.getDatasourceLoadstatus("datasource1", null, null, null, null); + Assert.assertEquals(400, response.getStatus()); + } + + @Test + public void testGetDatasourceLoadstatusNoSegmentForInterval() + { + List segments = ImmutableList.of(); + // Test when datasource fully loaded + EasyMock.expect(segmentsMetadataManager.iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval(EasyMock.eq( + "datasource1"), EasyMock.anyObject(Interval.class), EasyMock.anyBoolean())) + .andReturn(Optional.of(segments)).once(); + EasyMock.replay(segmentsMetadataManager); + + DataSourcesResource dataSourcesResource = new DataSourcesResource( + inventoryView, + segmentsMetadataManager, + null, + null, + null, + null + ); + Response response = dataSourcesResource.getDatasourceLoadstatus("datasource1", true, null, null, null); + Assert.assertEquals(204, response.getStatus()); + } + + @Test + public void testGetDatasourceLoadstatusDefault() + { + DataSegment datasource1Segment1 = new DataSegment( + "datasource1", + Intervals.of("2010-01-01/P1D"), + "", + null, + null, + null, + null, + 0x9, + 10 + ); + + DataSegment datasource1Segment2 = new DataSegment( + "datasource1", + Intervals.of("2010-01-22/P1D"), + "", + null, + null, + null, + null, + 0x9, + 20 + ); + DataSegment datasource2Segment1 = new DataSegment( + "datasource2", + Intervals.of("2010-01-01/P1D"), + "", + null, + null, + null, + null, + 0x9, + 30 + ); + List segments = ImmutableList.of(datasource1Segment1, datasource1Segment2); + Map completedLoadInfoMap = ImmutableMap.of( + datasource1Segment1.getId(), new SegmentLoadInfo(datasource1Segment1), + datasource1Segment2.getId(), new SegmentLoadInfo(datasource1Segment2), + datasource2Segment1.getId(), new SegmentLoadInfo(datasource2Segment1) + ); + Map halfLoadedInfoMap = ImmutableMap.of( + datasource1Segment1.getId(), new SegmentLoadInfo(datasource1Segment1) + ); + + // Test when datasource fully loaded + EasyMock.expect(segmentsMetadataManager.iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval(EasyMock.eq("datasource1"), EasyMock.anyObject(Interval.class), EasyMock.anyBoolean())) + .andReturn(Optional.of(segments)).once(); + EasyMock.expect(inventoryView.getSegmentLoadInfos()).andReturn(completedLoadInfoMap).once(); + EasyMock.replay(segmentsMetadataManager, inventoryView); + + DataSourcesResource dataSourcesResource = new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); + Response response = dataSourcesResource.getDatasourceLoadstatus("datasource1", true, null, null, null); + Assert.assertEquals(200, response.getStatus()); + Assert.assertNotNull(response.getEntity()); + Assert.assertEquals(1, ((Map) response.getEntity()).size()); + Assert.assertTrue(((Map) response.getEntity()).containsKey("datasource1")); + Assert.assertEquals(100.0, ((Map) response.getEntity()).get("datasource1")); + EasyMock.verify(segmentsMetadataManager, inventoryView); + EasyMock.reset(segmentsMetadataManager, inventoryView); + + // Test when datasource half loaded + EasyMock.expect(segmentsMetadataManager.iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval(EasyMock.eq("datasource1"), EasyMock.anyObject(Interval.class), EasyMock.anyBoolean())) + .andReturn(Optional.of(segments)).once(); + EasyMock.expect(inventoryView.getSegmentLoadInfos()).andReturn(halfLoadedInfoMap).once(); + EasyMock.replay(segmentsMetadataManager, inventoryView); + + dataSourcesResource = new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); + response = dataSourcesResource.getDatasourceLoadstatus("datasource1", true, null, null, null); + Assert.assertEquals(200, response.getStatus()); + Assert.assertNotNull(response.getEntity()); + Assert.assertEquals(1, ((Map) response.getEntity()).size()); + Assert.assertTrue(((Map) response.getEntity()).containsKey("datasource1")); + Assert.assertEquals(50.0, ((Map) response.getEntity()).get("datasource1")); + EasyMock.verify(segmentsMetadataManager, inventoryView); + } + + @Test + public void testGetDatasourceLoadstatusSimple() + { + DataSegment datasource1Segment1 = new DataSegment( + "datasource1", + Intervals.of("2010-01-01/P1D"), + "", + null, + null, + null, + null, + 0x9, + 10 + ); + + DataSegment datasource1Segment2 = new DataSegment( + "datasource1", + Intervals.of("2010-01-22/P1D"), + "", + null, + null, + null, + null, + 0x9, + 20 + ); + DataSegment datasource2Segment1 = new DataSegment( + "datasource2", + Intervals.of("2010-01-01/P1D"), + "", + null, + null, + null, + null, + 0x9, + 30 + ); + List segments = ImmutableList.of(datasource1Segment1, datasource1Segment2); + Map completedLoadInfoMap = ImmutableMap.of( + datasource1Segment1.getId(), new SegmentLoadInfo(datasource1Segment1), + datasource1Segment2.getId(), new SegmentLoadInfo(datasource1Segment2), + datasource2Segment1.getId(), new SegmentLoadInfo(datasource2Segment1) + ); + Map halfLoadedInfoMap = ImmutableMap.of( + datasource1Segment1.getId(), new SegmentLoadInfo(datasource1Segment1) + ); + + // Test when datasource fully loaded + EasyMock.expect(segmentsMetadataManager.iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval(EasyMock.eq("datasource1"), EasyMock.anyObject(Interval.class), EasyMock.anyBoolean())) + .andReturn(Optional.of(segments)).once(); + EasyMock.expect(inventoryView.getSegmentLoadInfos()).andReturn(completedLoadInfoMap).once(); + EasyMock.replay(segmentsMetadataManager, inventoryView); + + DataSourcesResource dataSourcesResource = new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); + Response response = dataSourcesResource.getDatasourceLoadstatus("datasource1", true, null, "simple", null); + Assert.assertEquals(200, response.getStatus()); + Assert.assertNotNull(response.getEntity()); + Assert.assertEquals(1, ((Map) response.getEntity()).size()); + Assert.assertTrue(((Map) response.getEntity()).containsKey("datasource1")); + Assert.assertEquals(0, ((Map) response.getEntity()).get("datasource1")); + EasyMock.verify(segmentsMetadataManager, inventoryView); + EasyMock.reset(segmentsMetadataManager, inventoryView); + + // Test when datasource half loaded + EasyMock.expect(segmentsMetadataManager.iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval(EasyMock.eq("datasource1"), EasyMock.anyObject(Interval.class), EasyMock.anyBoolean())) + .andReturn(Optional.of(segments)).once(); + EasyMock.expect(inventoryView.getSegmentLoadInfos()).andReturn(halfLoadedInfoMap).once(); + EasyMock.replay(segmentsMetadataManager, inventoryView); + + dataSourcesResource = new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, null); + response = dataSourcesResource.getDatasourceLoadstatus("datasource1", true, null, "simple", null); + Assert.assertEquals(200, response.getStatus()); + Assert.assertNotNull(response.getEntity()); + Assert.assertEquals(1, ((Map) response.getEntity()).size()); + Assert.assertTrue(((Map) response.getEntity()).containsKey("datasource1")); + Assert.assertEquals(1, ((Map) response.getEntity()).get("datasource1")); + EasyMock.verify(segmentsMetadataManager, inventoryView); + } + + @Test + public void testGetDatasourceLoadstatusFull() + { + DataSegment datasource1Segment1 = new DataSegment( + "datasource1", + Intervals.of("2010-01-01/P1D"), + "", + null, + null, + null, + null, + 0x9, + 10 + ); + + DataSegment datasource1Segment2 = new DataSegment( + "datasource1", + Intervals.of("2010-01-22/P1D"), + "", + null, + null, + null, + null, + 0x9, + 20 + ); + List segments = ImmutableList.of(datasource1Segment1, datasource1Segment2); + + final Map> underReplicationCountsPerDataSourcePerTier = new HashMap<>(); + Object2LongMap tier1 = new Object2LongOpenHashMap<>(); + tier1.put("datasource1", 0L); + Object2LongMap tier2 = new Object2LongOpenHashMap<>(); + tier2.put("datasource1", 3L); + underReplicationCountsPerDataSourcePerTier.put("tier1", tier1); + underReplicationCountsPerDataSourcePerTier.put("tier2", tier2); + + // Test when datasource fully loaded + EasyMock.expect(segmentsMetadataManager.iterateAllUsedNonOvershadowedSegmentsForDatasourceInterval(EasyMock.eq("datasource1"), EasyMock.anyObject(Interval.class), EasyMock.anyBoolean())) + .andReturn(Optional.of(segments)).once(); + DruidCoordinator druidCoordinator = EasyMock.createMock(DruidCoordinator.class); + EasyMock.expect(druidCoordinator.computeUnderReplicationCountsPerDataSourcePerTierForSegments(segments)) + .andReturn(underReplicationCountsPerDataSourcePerTier).once(); + + EasyMock.replay(segmentsMetadataManager, druidCoordinator); + + DataSourcesResource dataSourcesResource = new DataSourcesResource(inventoryView, segmentsMetadataManager, null, null, null, druidCoordinator); + Response response = dataSourcesResource.getDatasourceLoadstatus("datasource1", true, null, null, "full"); + Assert.assertEquals(200, response.getStatus()); + Assert.assertNotNull(response.getEntity()); + Assert.assertEquals(2, ((Map) response.getEntity()).size()); + Assert.assertEquals(1, ((Map) ((Map) response.getEntity()).get("tier1")).size()); + Assert.assertEquals(1, ((Map) ((Map) response.getEntity()).get("tier2")).size()); + Assert.assertEquals(0L, ((Map) ((Map) response.getEntity()).get("tier1")).get("datasource1")); + Assert.assertEquals(3L, ((Map) ((Map) response.getEntity()).get("tier2")).get("datasource1")); + EasyMock.verify(segmentsMetadataManager); + } + private DruidServerMetadata createRealtimeServerMetadata(String name) { return createServerMetadata(name, ServerType.REALTIME); From f5c14095aa985b31e8efdfd8805f6b399ac9c321 Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn <52679095+maytasm@users.noreply.github.com> Date: Wed, 17 Jun 2020 01:52:02 -1000 Subject: [PATCH 089/107] All aggregators should check if column can be vectorize (#10026) * All aggregators should use vectorization-aware column processor * All aggregators should use vectorization-aware column processor * fix canVectorize * fix canVectorize * add tests * revert back default * address comment * address comments * address comment * address comment --- .../query/aggregation/AggregatorFactory.java | 3 +- .../aggregation/CountAggregatorFactory.java | 3 +- .../DoubleSumAggregatorFactory.java | 9 ++++- .../FilteredAggregatorFactory.java | 7 ++-- .../FloatSumAggregatorFactory.java | 9 ++++- .../aggregation/LongSumAggregatorFactory.java | 8 +++- .../NullableNumericAggregatorFactory.java | 7 ++-- .../SuppressedAggregatorFactory.java | 5 ++- .../HyperUniquesAggregatorFactory.java | 3 +- .../mean/DoubleMeanAggregatorFactory.java | 7 +++- .../vector/VectorGroupByEngine.java | 3 +- .../timeseries/TimeseriesQueryEngine.java | 2 +- .../apache/druid/segment/ColumnInspector.java | 37 ++++++++++++++++++ .../druid/segment/ColumnSelectorFactory.java | 3 +- .../apache/druid/segment/StorageAdapter.java | 3 +- .../vector/VectorColumnSelectorFactory.java | 4 +- .../druid/query/SchemaEvolutionTest.java | 39 +++++++++++++++---- .../mean/DoubleMeanAggregationTest.java | 25 ++++++++++-- 18 files changed, 141 insertions(+), 36 deletions(-) create mode 100644 processing/src/main/java/org/apache/druid/segment/ColumnInspector.java diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/AggregatorFactory.java b/processing/src/main/java/org/apache/druid/query/aggregation/AggregatorFactory.java index 93f221771208..41e55a303fa7 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/AggregatorFactory.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/AggregatorFactory.java @@ -24,6 +24,7 @@ import org.apache.druid.java.util.common.UOE; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.query.PerSegmentQueryOptimizationContext; +import org.apache.druid.segment.ColumnInspector; import org.apache.druid.segment.ColumnSelectorFactory; import org.apache.druid.segment.vector.VectorColumnSelectorFactory; @@ -68,7 +69,7 @@ public VectorAggregator factorizeVector(VectorColumnSelectorFactory selectorFact /** * Returns whether or not this aggregation class supports vectorization. The default implementation returns false. */ - public boolean canVectorize() + public boolean canVectorize(ColumnInspector columnInspector) { return false; } diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/CountAggregatorFactory.java b/processing/src/main/java/org/apache/druid/query/aggregation/CountAggregatorFactory.java index 599a2c492020..b7e9fcb14a77 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/CountAggregatorFactory.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/CountAggregatorFactory.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.apache.druid.segment.ColumnInspector; import org.apache.druid.segment.ColumnSelectorFactory; import org.apache.druid.segment.vector.VectorColumnSelectorFactory; @@ -72,7 +73,7 @@ public Comparator getComparator() } @Override - public boolean canVectorize() + public boolean canVectorize(ColumnInspector columnInspector) { return true; } diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/DoubleSumAggregatorFactory.java b/processing/src/main/java/org/apache/druid/query/aggregation/DoubleSumAggregatorFactory.java index 00bc89bfc26e..0e5867cf6f00 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/DoubleSumAggregatorFactory.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/DoubleSumAggregatorFactory.java @@ -25,6 +25,9 @@ import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.math.expr.ExprMacroTable; import org.apache.druid.segment.BaseDoubleColumnValueSelector; +import org.apache.druid.segment.ColumnInspector; +import org.apache.druid.segment.column.ColumnCapabilities; +import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.vector.VectorColumnSelectorFactory; import org.apache.druid.segment.vector.VectorValueSelector; @@ -79,8 +82,12 @@ protected VectorValueSelector vectorSelector(VectorColumnSelectorFactory columnS } @Override - public boolean canVectorize() + public boolean canVectorize(ColumnInspector columnInspector) { + if (fieldName != null) { + final ColumnCapabilities capabilities = columnInspector.getColumnCapabilities(fieldName); + return expression == null && (capabilities == null || ValueType.isNumeric(capabilities.getType())); + } return expression == null; } diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/FilteredAggregatorFactory.java b/processing/src/main/java/org/apache/druid/query/aggregation/FilteredAggregatorFactory.java index f76cd3d51516..f00710bb78ec 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/FilteredAggregatorFactory.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/FilteredAggregatorFactory.java @@ -29,6 +29,7 @@ import org.apache.druid.query.filter.IntervalDimFilter; import org.apache.druid.query.filter.ValueMatcher; import org.apache.druid.query.filter.vector.VectorValueMatcher; +import org.apache.druid.segment.ColumnInspector; import org.apache.druid.segment.ColumnSelectorFactory; import org.apache.druid.segment.column.ColumnHolder; import org.apache.druid.segment.vector.VectorColumnSelectorFactory; @@ -98,7 +99,7 @@ public BufferAggregator factorizeBuffered(ColumnSelectorFactory columnSelectorFa @Override public VectorAggregator factorizeVector(VectorColumnSelectorFactory columnSelectorFactory) { - Preconditions.checkState(canVectorize(), "Cannot vectorize"); + Preconditions.checkState(canVectorize(columnSelectorFactory), "Cannot vectorize"); final VectorValueMatcher valueMatcher = filter.makeVectorMatcher(columnSelectorFactory); return new FilteredVectorAggregator( valueMatcher, @@ -107,9 +108,9 @@ public VectorAggregator factorizeVector(VectorColumnSelectorFactory columnSelect } @Override - public boolean canVectorize() + public boolean canVectorize(ColumnInspector columnInspector) { - return delegate.canVectorize() && filter.canVectorizeMatcher(); + return delegate.canVectorize(columnInspector) && filter.canVectorizeMatcher(); } @Override diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/FloatSumAggregatorFactory.java b/processing/src/main/java/org/apache/druid/query/aggregation/FloatSumAggregatorFactory.java index d9ed43ab22cf..0c61920fc28d 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/FloatSumAggregatorFactory.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/FloatSumAggregatorFactory.java @@ -25,6 +25,8 @@ import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.math.expr.ExprMacroTable; import org.apache.druid.segment.BaseFloatColumnValueSelector; +import org.apache.druid.segment.ColumnInspector; +import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.vector.VectorColumnSelectorFactory; import org.apache.druid.segment.vector.VectorValueSelector; @@ -78,12 +80,15 @@ protected VectorValueSelector vectorSelector(VectorColumnSelectorFactory columnS } @Override - public boolean canVectorize() + public boolean canVectorize(ColumnInspector columnInspector) { + if (fieldName != null) { + final ColumnCapabilities capabilities = columnInspector.getColumnCapabilities(fieldName); + return expression == null && (capabilities == null || capabilities.getType().isNumeric()); + } return expression == null; } - @Override protected VectorAggregator factorizeVector( VectorColumnSelectorFactory columnSelectorFactory, diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/LongSumAggregatorFactory.java b/processing/src/main/java/org/apache/druid/query/aggregation/LongSumAggregatorFactory.java index e7945a507cc1..337ce1817b63 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/LongSumAggregatorFactory.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/LongSumAggregatorFactory.java @@ -25,6 +25,8 @@ import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.math.expr.ExprMacroTable; import org.apache.druid.segment.BaseLongColumnValueSelector; +import org.apache.druid.segment.ColumnInspector; +import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.vector.VectorColumnSelectorFactory; import org.apache.druid.segment.vector.VectorValueSelector; @@ -87,8 +89,12 @@ protected VectorAggregator factorizeVector( } @Override - public boolean canVectorize() + public boolean canVectorize(ColumnInspector columnInspector) { + if (fieldName != null) { + final ColumnCapabilities capabilities = columnInspector.getColumnCapabilities(fieldName); + return expression == null && (capabilities == null || capabilities.getType().isNumeric()); + } return expression == null; } diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericAggregatorFactory.java b/processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericAggregatorFactory.java index d9d66e37599a..615f0b57db1a 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericAggregatorFactory.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/NullableNumericAggregatorFactory.java @@ -65,7 +65,7 @@ public final BufferAggregator factorizeBuffered(ColumnSelectorFactory columnSele @Override public final VectorAggregator factorizeVector(VectorColumnSelectorFactory columnSelectorFactory) { - Preconditions.checkState(canVectorize(), "Cannot vectorize"); + Preconditions.checkState(canVectorize(columnSelectorFactory), "Cannot vectorize"); VectorValueSelector selector = vectorSelector(columnSelectorFactory); VectorAggregator aggregator = factorizeVector(columnSelectorFactory, selector); return NullHandling.replaceWithDefault() ? aggregator : new NullableNumericVectorAggregator(aggregator, selector); @@ -135,12 +135,11 @@ protected abstract BufferAggregator factorizeBuffered( * @see BufferAggregator */ protected VectorAggregator factorizeVector( - // Not used by current aggregators, but here for parity with "factorizeBuffered". - @SuppressWarnings("unused") VectorColumnSelectorFactory columnSelectorFactory, + VectorColumnSelectorFactory columnSelectorFactory, VectorValueSelector selector ) { - if (!canVectorize()) { + if (!canVectorize(columnSelectorFactory)) { throw new UnsupportedOperationException("Cannot vectorize"); } else { throw new UnsupportedOperationException("canVectorize returned true but 'factorizeVector' is not implemented"); diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/SuppressedAggregatorFactory.java b/processing/src/main/java/org/apache/druid/query/aggregation/SuppressedAggregatorFactory.java index 35e7938c8b5c..6468b6771976 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/SuppressedAggregatorFactory.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/SuppressedAggregatorFactory.java @@ -22,6 +22,7 @@ import org.apache.druid.query.PerSegmentQueryOptimizationContext; import org.apache.druid.query.cache.CacheKeyBuilder; import org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector; +import org.apache.druid.segment.ColumnInspector; import org.apache.druid.segment.ColumnSelectorFactory; import org.apache.druid.segment.vector.VectorColumnSelectorFactory; @@ -70,9 +71,9 @@ public VectorAggregator factorizeVector(VectorColumnSelectorFactory columnSelect } @Override - public boolean canVectorize() + public boolean canVectorize(ColumnInspector columnInspector) { - return delegate.canVectorize(); + return delegate.canVectorize(columnInspector); } @Override diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactory.java b/processing/src/main/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactory.java index b3ab8c3b4ef8..ceb82d5247e5 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactory.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactory.java @@ -38,6 +38,7 @@ import org.apache.druid.query.aggregation.cardinality.HyperLogLogCollectorAggregateCombiner; import org.apache.druid.query.cache.CacheKeyBuilder; import org.apache.druid.segment.BaseObjectColumnValueSelector; +import org.apache.druid.segment.ColumnInspector; import org.apache.druid.segment.ColumnSelectorFactory; import org.apache.druid.segment.NilColumnValueSelector; import org.apache.druid.segment.column.ColumnCapabilities; @@ -140,7 +141,7 @@ public VectorAggregator factorizeVector(final VectorColumnSelectorFactory select } @Override - public boolean canVectorize() + public boolean canVectorize(ColumnInspector columnInspector) { return true; } diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregatorFactory.java b/processing/src/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregatorFactory.java index d1da4d3189f0..782a2aad71a0 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregatorFactory.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregatorFactory.java @@ -31,7 +31,9 @@ import org.apache.druid.query.aggregation.BufferAggregator; import org.apache.druid.query.aggregation.VectorAggregator; import org.apache.druid.query.cache.CacheKeyBuilder; +import org.apache.druid.segment.ColumnInspector; import org.apache.druid.segment.ColumnSelectorFactory; +import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.vector.VectorColumnSelectorFactory; import javax.annotation.Nullable; @@ -106,9 +108,10 @@ public VectorAggregator factorizeVector(final VectorColumnSelectorFactory select } @Override - public boolean canVectorize() + public boolean canVectorize(ColumnInspector columnInspector) { - return true; + final ColumnCapabilities capabilities = columnInspector.getColumnCapabilities(fieldName); + return capabilities == null || capabilities.getType().isNumeric(); } @Override diff --git a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/vector/VectorGroupByEngine.java b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/vector/VectorGroupByEngine.java index 3fa85040cea4..10408fe2d7a8 100644 --- a/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/vector/VectorGroupByEngine.java +++ b/processing/src/main/java/org/apache/druid/query/groupby/epinephelinae/vector/VectorGroupByEngine.java @@ -28,7 +28,6 @@ import org.apache.druid.java.util.common.parsers.CloseableIterator; import org.apache.druid.query.QueryConfig; import org.apache.druid.query.aggregation.AggregatorAdapters; -import org.apache.druid.query.aggregation.AggregatorFactory; import org.apache.druid.query.dimension.DimensionSpec; import org.apache.druid.query.filter.Filter; import org.apache.druid.query.groupby.GroupByQuery; @@ -85,7 +84,7 @@ public static boolean canVectorize( return GroupByQueryEngineV2.isAllSingleValueDims(adapter::getColumnCapabilities, query.getDimensions(), true) && query.getDimensions().stream().allMatch(DimensionSpec::canVectorize) - && query.getAggregatorSpecs().stream().allMatch(AggregatorFactory::canVectorize) + && query.getAggregatorSpecs().stream().allMatch(aggregatorFactory -> aggregatorFactory.canVectorize(adapter)) && adapter.canVectorize(filter, query.getVirtualColumns(), false); } diff --git a/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQueryEngine.java b/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQueryEngine.java index 1be36b2f10ef..f420f782f805 100644 --- a/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQueryEngine.java +++ b/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQueryEngine.java @@ -102,7 +102,7 @@ public Sequence> process(final TimeseriesQuery que final boolean doVectorize = queryConfigToUse.getVectorize().shouldVectorize( adapter.canVectorize(filter, query.getVirtualColumns(), descending) - && query.getAggregatorSpecs().stream().allMatch(AggregatorFactory::canVectorize) + && query.getAggregatorSpecs().stream().allMatch(aggregatorFactory -> aggregatorFactory.canVectorize(adapter)) ); final Sequence> result; diff --git a/processing/src/main/java/org/apache/druid/segment/ColumnInspector.java b/processing/src/main/java/org/apache/druid/segment/ColumnInspector.java new file mode 100644 index 000000000000..3090455833e3 --- /dev/null +++ b/processing/src/main/java/org/apache/druid/segment/ColumnInspector.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment; + +import org.apache.druid.segment.column.ColumnCapabilities; + +import javax.annotation.Nullable; + +public interface ColumnInspector +{ + /** + * Returns capabilities of a particular column. + * + * @param column column name + * + * @return capabilities, or null + */ + @Nullable + ColumnCapabilities getColumnCapabilities(String column); +} diff --git a/processing/src/main/java/org/apache/druid/segment/ColumnSelectorFactory.java b/processing/src/main/java/org/apache/druid/segment/ColumnSelectorFactory.java index 07e66a672259..f99f0eade621 100644 --- a/processing/src/main/java/org/apache/druid/segment/ColumnSelectorFactory.java +++ b/processing/src/main/java/org/apache/druid/segment/ColumnSelectorFactory.java @@ -31,7 +31,7 @@ * @see org.apache.druid.segment.vector.VectorColumnSelectorFactory, the vectorized version */ @PublicApi -public interface ColumnSelectorFactory +public interface ColumnSelectorFactory extends ColumnInspector { DimensionSelector makeDimensionSelector(DimensionSpec dimensionSpec); @@ -50,6 +50,7 @@ public interface ColumnSelectorFactory * * @return capabilities, or null */ + @Override @Nullable ColumnCapabilities getColumnCapabilities(String column); } diff --git a/processing/src/main/java/org/apache/druid/segment/StorageAdapter.java b/processing/src/main/java/org/apache/druid/segment/StorageAdapter.java index 6fa16a6bcf26..e7905b2902e8 100644 --- a/processing/src/main/java/org/apache/druid/segment/StorageAdapter.java +++ b/processing/src/main/java/org/apache/druid/segment/StorageAdapter.java @@ -30,7 +30,7 @@ /** */ @PublicApi -public interface StorageAdapter extends CursorFactory +public interface StorageAdapter extends CursorFactory, ColumnInspector { Interval getInterval(); Indexed getAvailableDimensions(); @@ -62,6 +62,7 @@ public interface StorageAdapter extends CursorFactory * * @return capabilities, or null */ + @Override @Nullable ColumnCapabilities getColumnCapabilities(String column); diff --git a/processing/src/main/java/org/apache/druid/segment/vector/VectorColumnSelectorFactory.java b/processing/src/main/java/org/apache/druid/segment/vector/VectorColumnSelectorFactory.java index bea845f7f5fb..ffa23cec433f 100644 --- a/processing/src/main/java/org/apache/druid/segment/vector/VectorColumnSelectorFactory.java +++ b/processing/src/main/java/org/apache/druid/segment/vector/VectorColumnSelectorFactory.java @@ -20,6 +20,7 @@ package org.apache.druid.segment.vector; import org.apache.druid.query.dimension.DimensionSpec; +import org.apache.druid.segment.ColumnInspector; import org.apache.druid.segment.column.ColumnCapabilities; import javax.annotation.Nullable; @@ -29,7 +30,7 @@ * * @see org.apache.druid.segment.ColumnSelectorFactory, the non-vectorized version. */ -public interface VectorColumnSelectorFactory +public interface VectorColumnSelectorFactory extends ColumnInspector { /** * Returns a {@link VectorSizeInspector} for the {@link VectorCursor} that generated this object. @@ -72,6 +73,7 @@ default int getMaxVectorSize() * * @return capabilities, or null if the column doesn't exist. */ + @Override @Nullable ColumnCapabilities getColumnCapabilities(String column); } diff --git a/processing/src/test/java/org/apache/druid/query/SchemaEvolutionTest.java b/processing/src/test/java/org/apache/druid/query/SchemaEvolutionTest.java index 8aeb6f20f8dc..7e829e00a89a 100644 --- a/processing/src/test/java/org/apache/druid/query/SchemaEvolutionTest.java +++ b/processing/src/test/java/org/apache/druid/query/SchemaEvolutionTest.java @@ -21,7 +21,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import com.google.common.io.Closeables; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; import org.apache.druid.common.config.NullHandling; import org.apache.druid.data.input.InputRow; import org.apache.druid.data.input.impl.DimensionsSpec; @@ -35,6 +38,7 @@ import org.apache.druid.java.util.common.guava.Sequence; import org.apache.druid.query.aggregation.CountAggregatorFactory; import org.apache.druid.query.aggregation.DoubleSumAggregatorFactory; +import org.apache.druid.query.aggregation.FloatSumAggregatorFactory; import org.apache.druid.query.aggregation.LongSumAggregatorFactory; import org.apache.druid.query.aggregation.hyperloglog.HyperUniquesAggregatorFactory; import org.apache.druid.query.expression.TestExprMacroTable; @@ -55,6 +59,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; import java.io.IOException; import java.util.HashMap; @@ -64,12 +69,18 @@ /** * Tests designed to exercise changing column types, adding columns, removing columns, etc. */ +@RunWith(JUnitParamsRunner.class) public class SchemaEvolutionTest { private static final String DATA_SOURCE = "foo"; private static final String TIMESTAMP_COLUMN = "t"; private static final double THIRTY_ONE_POINT_ONE = 31.1d; + public Object[] doVectorize() + { + return Lists.newArrayList(true, false).toArray(); + } + public static List> timeseriesResult(final Map map) { return ImmutableList.of(new Result<>(DateTimes.of("2000"), new TimeseriesResultValue((Map) map))); @@ -137,6 +148,8 @@ public static > List runQuery( @Before public void setUp() throws IOException { + NullHandling.initializeForTests(); + // Index1: c1 is a string, c2 nonexistent, "uniques" nonexistent index1 = IndexBuilder.create() .tmpDir(temporaryFolder.newFolder()) @@ -209,7 +222,8 @@ public void tearDown() throws IOException } @Test - public void testHyperUniqueEvolutionTimeseries() + @Parameters(method = "doVectorize") + public void testHyperUniqueEvolutionTimeseries(boolean doVectorize) { final TimeseriesQueryRunnerFactory factory = QueryRunnerTestHelper.newTimeseriesQueryRunnerFactory(); @@ -222,6 +236,7 @@ public void testHyperUniqueEvolutionTimeseries() new HyperUniquesAggregatorFactory("uniques", "uniques") ) ) + .context(ImmutableMap.of(QueryContexts.VECTORIZE_KEY, doVectorize)) .build(); // index1 has no "uniques" column @@ -238,7 +253,8 @@ public void testHyperUniqueEvolutionTimeseries() } @Test - public void testNumericEvolutionTimeseriesAggregation() + @Parameters(method = "doVectorize") + public void testNumericEvolutionTimeseriesAggregation(boolean doVectorize) { final TimeseriesQueryRunnerFactory factory = QueryRunnerTestHelper.newTimeseriesQueryRunnerFactory(); @@ -256,6 +272,7 @@ public void testNumericEvolutionTimeseriesAggregation() new DoubleSumAggregatorFactory("d", null, "c1 * 1", TestExprMacroTable.INSTANCE) ) ) + .context(ImmutableMap.of(QueryContexts.VECTORIZE_KEY, doVectorize)) .build(); // Only string(1) @@ -313,7 +330,8 @@ public void testNumericEvolutionTimeseriesAggregation() } @Test - public void testNumericEvolutionFiltering() + @Parameters(method = "doVectorize") + public void testNumericEvolutionFiltering(boolean doVectorize) { final TimeseriesQueryRunnerFactory factory = QueryRunnerTestHelper.newTimeseriesQueryRunnerFactory(); @@ -328,26 +346,28 @@ public void testNumericEvolutionFiltering() ImmutableList.of( new LongSumAggregatorFactory("a", "c1"), new DoubleSumAggregatorFactory("b", "c1"), + new FloatSumAggregatorFactory("d", "c1"), new CountAggregatorFactory("c") ) ) + .context(ImmutableMap.of(QueryContexts.VECTORIZE_KEY, doVectorize)) .build(); // Only string(1) -- which we can filter but not aggregate Assert.assertEquals( - timeseriesResult(ImmutableMap.of("a", 19L, "b", 19.1, "c", 2L)), + timeseriesResult(ImmutableMap.of("a", 19L, "b", 19.1, "c", 2L, "d", 19.1f)), runQuery(query, factory, ImmutableList.of(index1)) ); // Only long(2) -- which we can filter and aggregate Assert.assertEquals( - timeseriesResult(ImmutableMap.of("a", 19L, "b", 19.0, "c", 2L)), + timeseriesResult(ImmutableMap.of("a", 19L, "b", 19.0, "c", 2L, "d", 19.0f)), runQuery(query, factory, ImmutableList.of(index2)) ); // Only float(3) -- which we can't filter, but can aggregate Assert.assertEquals( - timeseriesResult(ImmutableMap.of("a", 19L, "b", 19.1, "c", 2L)), + timeseriesResult(ImmutableMap.of("a", 19L, "b", 19.1, "c", 2L, "d", 19.1f)), runQuery(query, factory, ImmutableList.of(index3)) ); @@ -359,7 +379,9 @@ public void testNumericEvolutionFiltering() "b", NullHandling.defaultDoubleValue(), "c", - 0L + 0L, + "d", + NullHandling.defaultFloatValue() )), runQuery(query, factory, ImmutableList.of(index4)) ); @@ -369,7 +391,8 @@ public void testNumericEvolutionFiltering() timeseriesResult(ImmutableMap.of( "a", 57L, "b", 57.2, - "c", 6L + "c", 6L, + "d", 57.20000076293945 )), runQuery(query, factory, ImmutableList.of(index1, index2, index3, index4)) ); diff --git a/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java b/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java index 2f0709e5bb45..8b0d57b09cdd 100644 --- a/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java +++ b/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java @@ -21,12 +21,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; import org.apache.druid.data.input.Row; import org.apache.druid.java.util.common.granularity.Granularities; import org.apache.druid.java.util.common.guava.Sequence; import org.apache.druid.query.Druids; import org.apache.druid.query.Query; +import org.apache.druid.query.QueryContexts; import org.apache.druid.query.Result; import org.apache.druid.query.aggregation.AggregationTestHelper; import org.apache.druid.query.groupby.GroupByQuery; @@ -42,12 +47,19 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; import java.util.Collections; import java.util.List; +@RunWith(JUnitParamsRunner.class) public class DoubleMeanAggregationTest { + public Object[] doVectorize() + { + return Lists.newArrayList(true, false).toArray(); + } + @Rule public final TemporaryFolder tempFolder = new TemporaryFolder(); @@ -77,7 +89,8 @@ public DoubleMeanAggregationTest() } @Test - public void testBufferAggretatorUsingGroupByQuery() throws Exception + @Parameters(method = "doVectorize") + public void testBufferAggretatorUsingGroupByQuery(boolean doVectorize) throws Exception { GroupByQuery query = new GroupByQuery.Builder() .setDataSource("test") @@ -88,6 +101,7 @@ public void testBufferAggretatorUsingGroupByQuery() throws Exception new DoubleMeanAggregatorFactory("meanOnString", SimpleTestIndex.SINGLE_VALUE_DOUBLE_AS_STRING_DIM), new DoubleMeanAggregatorFactory("meanOnMultiValue", SimpleTestIndex.MULTI_VALUE_DOUBLE_AS_STRING_DIM) ) + .setContext(ImmutableMap.of(QueryContexts.VECTORIZE_KEY, doVectorize)) .build(); // do json serialization and deserialization of query to ensure there are no serde issues @@ -103,7 +117,8 @@ public void testBufferAggretatorUsingGroupByQuery() throws Exception } @Test - public void testVectorAggretatorUsingGroupByQueryOnDoubleColumn() throws Exception + @Parameters(method = "doVectorize") + public void testVectorAggretatorUsingGroupByQueryOnDoubleColumn(boolean doVectorize) throws Exception { GroupByQuery query = new GroupByQuery.Builder() .setDataSource("test") @@ -112,7 +127,7 @@ public void testVectorAggretatorUsingGroupByQueryOnDoubleColumn() throws Excepti .setAggregatorSpecs( new DoubleMeanAggregatorFactory("meanOnDouble", SimpleTestIndex.DOUBLE_COL) ) - .setContext(Collections.singletonMap(GroupByQueryConfig.CTX_KEY_VECTORIZE, true)) + .setContext(Collections.singletonMap(GroupByQueryConfig.CTX_KEY_VECTORIZE, doVectorize)) .build(); // do json serialization and deserialization of query to ensure there are no serde issues @@ -126,7 +141,8 @@ public void testVectorAggretatorUsingGroupByQueryOnDoubleColumn() throws Excepti } @Test - public void testAggretatorUsingTimeseriesQuery() throws Exception + @Parameters(method = "doVectorize") + public void testAggretatorUsingTimeseriesQuery(boolean doVectorize) throws Exception { TimeseriesQuery query = Druids.newTimeseriesQueryBuilder() .dataSource("test") @@ -143,6 +159,7 @@ public void testAggretatorUsingTimeseriesQuery() throws Exception SimpleTestIndex.MULTI_VALUE_DOUBLE_AS_STRING_DIM ) ) + .context(ImmutableMap.of(QueryContexts.VECTORIZE_KEY, doVectorize)) .build(); // do json serialization and deserialization of query to ensure there are no serde issues From 1d0571dd289e0bd925fc3761d6d74035d3bcf8a4 Mon Sep 17 00:00:00 2001 From: Samarth Jain Date: Wed, 17 Jun 2020 20:01:31 -0700 Subject: [PATCH 090/107] Druid Avatica - Handle escaping of search characters correctly (#10040) Fix Avatica based metadata queries by appending ESCAPE '\' clause to the LIKE expressions --- .../apache/druid/sql/avatica/DruidMeta.java | 18 +- .../sql/avatica/DruidAvaticaHandlerTest.java | 230 +++++++++++++++++- .../druid/sql/calcite/CalciteQueryTest.java | 4 + .../druid/sql/calcite/util/CalciteTests.java | 109 +++++++++ 4 files changed, 353 insertions(+), 8 deletions(-) diff --git a/sql/src/main/java/org/apache/druid/sql/avatica/DruidMeta.java b/sql/src/main/java/org/apache/druid/sql/avatica/DruidMeta.java index d5b27aad6489..738bed4dba7d 100644 --- a/sql/src/main/java/org/apache/druid/sql/avatica/DruidMeta.java +++ b/sql/src/main/java/org/apache/druid/sql/avatica/DruidMeta.java @@ -364,7 +364,7 @@ public MetaResultSet getSchemas( } if (schemaPattern.s != null) { - whereBuilder.add("SCHEMATA.SCHEMA_NAME LIKE " + Calcites.escapeStringLiteral(schemaPattern.s)); + whereBuilder.add("SCHEMATA.SCHEMA_NAME LIKE " + withEscapeClause(schemaPattern.s)); } final String where = whereBuilder.isEmpty() ? "" : "WHERE " + Joiner.on(" AND ").join(whereBuilder); @@ -395,11 +395,11 @@ public MetaResultSet getTables( } if (schemaPattern.s != null) { - whereBuilder.add("TABLES.TABLE_SCHEMA LIKE " + Calcites.escapeStringLiteral(schemaPattern.s)); + whereBuilder.add("TABLES.TABLE_SCHEMA LIKE " + withEscapeClause(schemaPattern.s)); } if (tableNamePattern.s != null) { - whereBuilder.add("TABLES.TABLE_NAME LIKE " + Calcites.escapeStringLiteral(tableNamePattern.s)); + whereBuilder.add("TABLES.TABLE_NAME LIKE " + withEscapeClause(tableNamePattern.s)); } if (typeList != null) { @@ -446,15 +446,16 @@ public MetaResultSet getColumns( } if (schemaPattern.s != null) { - whereBuilder.add("COLUMNS.TABLE_SCHEMA LIKE " + Calcites.escapeStringLiteral(schemaPattern.s)); + whereBuilder.add("COLUMNS.TABLE_SCHEMA LIKE " + withEscapeClause(schemaPattern.s)); } if (tableNamePattern.s != null) { - whereBuilder.add("COLUMNS.TABLE_NAME LIKE " + Calcites.escapeStringLiteral(tableNamePattern.s)); + whereBuilder.add("COLUMNS.TABLE_NAME LIKE " + withEscapeClause(tableNamePattern.s)); } if (columnNamePattern.s != null) { - whereBuilder.add("COLUMNS.COLUMN_NAME LIKE " + Calcites.escapeStringLiteral(columnNamePattern.s)); + whereBuilder.add("COLUMNS.COLUMN_NAME LIKE " + + withEscapeClause(columnNamePattern.s)); } final String where = whereBuilder.isEmpty() ? "" : "WHERE " + Joiner.on(" AND ").join(whereBuilder); @@ -638,4 +639,9 @@ private int getEffectiveMaxRowsPerFrame(int clientMaxRowsPerFrame) } return Math.min(clientMaxRowsPerFrame, config.getMaxRowsPerFrame()); } + + private static String withEscapeClause(String toEscape) + { + return Calcites.escapeStringLiteral(toEscape) + " ESCAPE '\\'"; + } } diff --git a/sql/src/test/java/org/apache/druid/sql/avatica/DruidAvaticaHandlerTest.java b/sql/src/test/java/org/apache/druid/sql/avatica/DruidAvaticaHandlerTest.java index 97c0f7c4bfb6..6b836b83ea4d 100644 --- a/sql/src/test/java/org/apache/druid/sql/avatica/DruidAvaticaHandlerTest.java +++ b/sql/src/test/java/org/apache/druid/sql/avatica/DruidAvaticaHandlerTest.java @@ -414,8 +414,19 @@ public void testDatabaseMetaDataTables() throws Exception Pair.of("TABLE_NAME", CalciteTests.DATASOURCE3), Pair.of("TABLE_SCHEM", "druid"), Pair.of("TABLE_TYPE", "TABLE") + ), + row( + Pair.of("TABLE_CAT", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_TYPE", "TABLE") + ), + row( + Pair.of("TABLE_CAT", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE), + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_TYPE", "TABLE") ) - ), getRows( metaData.getTables(null, "druid", "%", null), @@ -465,8 +476,19 @@ public void testDatabaseMetaDataTablesAsSuperuser() throws Exception Pair.of("TABLE_NAME", CalciteTests.DATASOURCE3), Pair.of("TABLE_SCHEM", "druid"), Pair.of("TABLE_TYPE", "TABLE") + ), + row( + Pair.of("TABLE_CAT", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_TYPE", "TABLE") + ), + row( + Pair.of("TABLE_CAT", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE), + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_TYPE", "TABLE") ) - ), getRows( metaData.getTables(null, "druid", "%", null), @@ -957,6 +979,210 @@ public void testSysTableParameterBinding() throws Exception ); } + @Test + public void testEscapingForGetColumns() throws Exception + { + final DatabaseMetaData metaData = client.getMetaData(); + + ImmutableList> someDatasourceColumns = ImmutableList.of( + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("COLUMN_NAME", "__time") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("COLUMN_NAME", "cnt") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("COLUMN_NAME", "dim1") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("COLUMN_NAME", "dim2") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("COLUMN_NAME", "dim3") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("COLUMN_NAME", "m1") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("COLUMN_NAME", "m2") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("COLUMN_NAME", "unique_dim1") + ) + ); + // If the escape clause wasn't correctly set, rows for potentially none or more than + // one datasource (some_datasource and somexdatasource) would have been returned + Assert.assertEquals( + someDatasourceColumns, + getRows( + metaData.getColumns(null, "dr_id", CalciteTests.SOME_DATSOURCE_ESCAPED, null), + ImmutableSet.of("TABLE_NAME", "TABLE_SCHEM", "COLUMN_NAME") + ) + ); + + ImmutableList> someXDatasourceColumns = ImmutableList.of( + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE), + Pair.of("COLUMN_NAME", "__time") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE), + Pair.of("COLUMN_NAME", "cnt_x") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE), + Pair.of("COLUMN_NAME", "m1_x") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE), + Pair.of("COLUMN_NAME", "m2_x") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE), + Pair.of("COLUMN_NAME", "unique_dim1_x") + ) + ); + Assert.assertEquals( + someXDatasourceColumns, + getRows( + metaData.getColumns(null, "dr_id", "somexdatasource", null), + ImmutableSet.of("TABLE_NAME", "TABLE_SCHEM", "COLUMN_NAME") + ) + ); + + List> columnsOfBothTables = new ArrayList<>(someDatasourceColumns); + columnsOfBothTables.addAll(someXDatasourceColumns); + // Assert that the pattern matching still works when no escape string is provided + Assert.assertEquals( + columnsOfBothTables, + getRows( + metaData.getColumns(null, "dr_id", "some_datasource", null), + ImmutableSet.of("TABLE_NAME", "TABLE_SCHEM", "COLUMN_NAME") + ) + ); + + // Assert column name pattern works correctly when _ is in the column names + Assert.assertEquals( + ImmutableList.of( + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE), + Pair.of("COLUMN_NAME", "m1_x") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE), + Pair.of("COLUMN_NAME", "m2_x") + ) + ), + getRows( + metaData.getColumns("druid", "dr_id", CalciteTests.SOMEXDATASOURCE, "m_\\_x"), + ImmutableSet.of("TABLE_NAME", "TABLE_SCHEM", "COLUMN_NAME") + ) + ); + + // Assert column name pattern with % works correctly for column names starting with m + Assert.assertEquals( + ImmutableList.of( + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("COLUMN_NAME", "m1") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE), + Pair.of("COLUMN_NAME", "m2") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE), + Pair.of("COLUMN_NAME", "m1_x") + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE), + Pair.of("COLUMN_NAME", "m2_x") + ) + ), + getRows( + metaData.getColumns("druid", "dr_id", CalciteTests.SOME_DATASOURCE, "m%"), + ImmutableSet.of("TABLE_NAME", "TABLE_SCHEM", "COLUMN_NAME") + ) + ); + } + + @Test + public void testEscapingForGetTables() throws Exception + { + final DatabaseMetaData metaData = client.getMetaData(); + + Assert.assertEquals( + ImmutableList.of( + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE) + ) + ), + getRows( + metaData.getTables("druid", "dr_id", CalciteTests.SOME_DATSOURCE_ESCAPED, null), + ImmutableSet.of("TABLE_SCHEM", "TABLE_NAME") + ) + ); + + Assert.assertEquals( + ImmutableList.of( + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE) + ) + ), + getRows( + metaData.getTables("druid", "dr_id", CalciteTests.SOMEXDATASOURCE, null), + ImmutableSet.of("TABLE_SCHEM", "TABLE_NAME") + ) + ); + + // Assert that some_datasource is treated as a pattern that matches some_datasource and somexdatasource + Assert.assertEquals( + ImmutableList.of( + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOME_DATASOURCE) + ), + row( + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_NAME", CalciteTests.SOMEXDATASOURCE) + ) + ), + getRows( + metaData.getTables("druid", "dr_id", CalciteTests.SOME_DATASOURCE, null), + ImmutableSet.of("TABLE_SCHEM", "TABLE_NAME") + ) + ); + } + private static List> getRows(final ResultSet resultSet) throws SQLException { return getRows(resultSet, null); diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index 9b391a716f1b..1ad92cb538f5 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -718,6 +718,8 @@ public void testInformationSchemaTables() throws Exception .add(new Object[]{"druid", CalciteTests.DATASOURCE4, "TABLE"}) .add(new Object[]{"druid", CalciteTests.DATASOURCE5, "TABLE"}) .add(new Object[]{"druid", CalciteTests.DATASOURCE3, "TABLE"}) + .add(new Object[]{"druid", CalciteTests.SOME_DATASOURCE, "TABLE"}) + .add(new Object[]{"druid", CalciteTests.SOMEXDATASOURCE, "TABLE"}) .add(new Object[]{"druid", "aview", "VIEW"}) .add(new Object[]{"druid", "bview", "VIEW"}) .add(new Object[]{"INFORMATION_SCHEMA", "COLUMNS", "SYSTEM_TABLE"}) @@ -746,6 +748,8 @@ public void testInformationSchemaTables() throws Exception .add(new Object[]{"druid", CalciteTests.FORBIDDEN_DATASOURCE, "TABLE"}) .add(new Object[]{"druid", CalciteTests.DATASOURCE5, "TABLE"}) .add(new Object[]{"druid", CalciteTests.DATASOURCE3, "TABLE"}) + .add(new Object[]{"druid", CalciteTests.SOME_DATASOURCE, "TABLE"}) + .add(new Object[]{"druid", CalciteTests.SOMEXDATASOURCE, "TABLE"}) .add(new Object[]{"druid", "aview", "VIEW"}) .add(new Object[]{"druid", "bview", "VIEW"}) .add(new Object[]{"INFORMATION_SCHEMA", "COLUMNS", "SYSTEM_TABLE"}) diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java index 0ca415b8746a..b7d56f5632d2 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java @@ -140,6 +140,9 @@ public class CalciteTests public static final String DATASOURCE4 = "foo4"; public static final String DATASOURCE5 = "lotsocolumns"; public static final String FORBIDDEN_DATASOURCE = "forbiddenDatasource"; + public static final String SOME_DATASOURCE = "some_datasource"; + public static final String SOME_DATSOURCE_ESCAPED = "some\\_datasource"; + public static final String SOMEXDATASOURCE = "somexdatasource"; public static final String DRUID_SCHEMA_NAME = "druid"; public static final String INFORMATION_SCHEMA_NAME = "INFORMATION_SCHEMA"; public static final String SYSTEM_SCHEMA_NAME = "sys"; @@ -294,6 +297,16 @@ public AuthenticationResult createEscalatedAuthenticationResult() .withRollup(false) .build(); + private static final IncrementalIndexSchema INDEX_SCHEMA_WITH_X_COLUMNS = new IncrementalIndexSchema.Builder() + .withMetrics( + new CountAggregatorFactory("cnt_x"), + new FloatSumAggregatorFactory("m1_x", "m1_x"), + new DoubleSumAggregatorFactory("m2_x", "m2_x"), + new HyperUniquesAggregatorFactory("unique_dim1_x", "dim1_x") + ) + .withRollup(false) + .build(); + private static final IncrementalIndexSchema INDEX_SCHEMA_NUMERIC_DIMS = new IncrementalIndexSchema.Builder() .withMetrics( new CountAggregatorFactory("cnt"), @@ -361,6 +374,68 @@ public AuthenticationResult createEscalatedAuthenticationResult() .put("dim1", "abc") .build() ); + + public static final List RAW_ROWS1_X = ImmutableList.of( + createRow( + ImmutableMap.builder() + .put("t", "2000-01-01") + .put("m1_x", "1.0") + .put("m2_x", "1.0") + .put("dim1_x", "") + .put("dim2_x", ImmutableList.of("a")) + .put("dim3_x", ImmutableList.of("a", "b")) + .build() + ), + createRow( + ImmutableMap.builder() + .put("t", "2000-01-02") + .put("m1_x", "2.0") + .put("m2_x", "2.0") + .put("dim1_x", "10.1") + .put("dim2_x", ImmutableList.of()) + .put("dim3_x", ImmutableList.of("b", "c")) + .build() + ), + createRow( + ImmutableMap.builder() + .put("t", "2000-01-03") + .put("m1_x", "3.0") + .put("m2_x", "3.0") + .put("dim1_x", "2") + .put("dim2_x", ImmutableList.of("")) + .put("dim3_x", ImmutableList.of("d")) + .build() + ), + createRow( + ImmutableMap.builder() + .put("t", "2001-01-01") + .put("m1_x", "4.0") + .put("m2_x", "4.0") + .put("dim1_x", "1") + .put("dim2_x", ImmutableList.of("a")) + .put("dim3_x", ImmutableList.of("")) + .build() + ), + createRow( + ImmutableMap.builder() + .put("t", "2001-01-02") + .put("m1_x", "5.0") + .put("m2_x", "5.0") + .put("dim1_x", "def") + .put("dim2_x", ImmutableList.of("abc")) + .put("dim3_x", ImmutableList.of()) + .build() + ), + createRow( + ImmutableMap.builder() + .put("t", "2001-01-03") + .put("m1_x", "6.0") + .put("m2_x", "6.0") + .put("dim1_x", "abc") + .build() + ) + ); + public static final List ROWS1 = RAW_ROWS1.stream().map(CalciteTests::createRow).collect(Collectors.toList()); @@ -635,6 +710,22 @@ public static SpecificSegmentsQuerySegmentWalker createMockWalker( .rows(ROWS_LOTS_OF_COLUMNS) .buildMMappedIndex(); + final QueryableIndex someDatasourceIndex = IndexBuilder + .create() + .tmpDir(new File(tmpDir, "1")) + .segmentWriteOutMediumFactory(OffHeapMemorySegmentWriteOutMediumFactory.instance()) + .schema(INDEX_SCHEMA) + .rows(ROWS1) + .buildMMappedIndex(); + + final QueryableIndex someXDatasourceIndex = IndexBuilder + .create() + .tmpDir(new File(tmpDir, "1")) + .segmentWriteOutMediumFactory(OffHeapMemorySegmentWriteOutMediumFactory.instance()) + .schema(INDEX_SCHEMA_WITH_X_COLUMNS) + .rows(RAW_ROWS1_X) + .buildMMappedIndex(); + return new SpecificSegmentsQuerySegmentWalker( conglomerate, @@ -695,6 +786,24 @@ public static SpecificSegmentsQuerySegmentWalker createMockWalker( .size(0) .build(), indexLotsOfColumns + ).add( + DataSegment.builder() + .dataSource(SOME_DATASOURCE) + .interval(indexLotsOfColumns.getDataInterval()) + .version("1") + .shardSpec(new LinearShardSpec(0)) + .size(0) + .build(), + someDatasourceIndex + ).add( + DataSegment.builder() + .dataSource(SOMEXDATASOURCE) + .interval(indexLotsOfColumns.getDataInterval()) + .version("1") + .shardSpec(new LinearShardSpec(0)) + .size(0) + .build(), + someXDatasourceIndex ); } From 0e5911dc5a1e5098696e75d178c80915213993f9 Mon Sep 17 00:00:00 2001 From: Aleksey Plekhanov Date: Thu, 18 Jun 2020 21:47:07 +0500 Subject: [PATCH 091/107] IntelliJ inspection and checkstyle rule for "Collection.EMPTY_* field accesses replaceable with Collections.empty*()" (#9690) * IntelliJ inspection and checkstyle rule for "Collection.EMPTY_* field accesses replaceable with Collections.empty*()" * Reverted checkstyle rule * Added tests to pass CI * Codestyle --- .idea/inspectionProfiles/Druid.xml | 1 + .../AmbariMetricsEmitterConfigTest.java | 2 +- .../dropwizard/DropwizardEmitterModule.java | 2 +- .../graphite/GraphiteEmitterModule.java | 2 +- .../graphite/GraphiteEmitterConfigTest.java | 10 ++++- .../influxdb/InfluxdbEmitterModule.java | 2 +- .../emitter/influxdb/InfluxdbEmitterTest.java | 6 +++ .../emitter/kafka/KafkaEmitterModule.java | 2 +- .../emitter/kafka/KafkaEmitterConfigTest.java | 6 +++ .../opentsdb/OpentsdbEmitterModule.java | 2 +- .../opentsdb/OpentsdbEmitterConfigTest.java | 5 +++ .../emitter/statsd/StatsDEmitterModule.java | 2 +- .../emitter/statsd/StatsDEmitterTest.java | 6 +++ .../kafka/supervisor/KafkaSupervisorTest.java | 28 ++++++------- .../supervisor/KinesisSupervisorTest.java | 40 +++++++++---------- .../server/lookup/PollingLookupTest.java | 2 +- .../cache/loading/LoadingCacheTest.java | 2 +- .../lookup/jdbc/JdbcDataFetcherTest.java | 2 +- .../druid/indexer/InputRowSerdeTest.java | 2 +- .../overlord/setup/WorkerCategorySpec.java | 4 +- .../guice/ITTLSCertificateCheckerModule.java | 2 +- .../druid/query/lookup/LookupsState.java | 6 +-- .../StringColumnAggregationTest.java | 4 +- .../mean/DoubleMeanAggregationTest.java | 4 +- .../extraction/MapLookupExtractorTest.java | 2 +- .../query/groupby/GroupByQueryRunnerTest.java | 2 +- .../groupby/orderby/TopNSequenceTest.java | 2 +- .../virtual/DummyStringVirtualColumnTest.java | 4 +- .../emitter/ComposingEmitterModule.java | 2 +- .../AuthenticatorMapperModule.java | 3 +- .../AuthorizerMapperModule.java | 3 +- .../query/lookup/LookupSnapshotTakerTest.java | 4 +- .../cache/LookupCoordinatorManagerTest.java | 4 +- .../server/shard/NumberedShardSpecTest.java | 10 ++--- 34 files changed, 104 insertions(+), 76 deletions(-) diff --git a/.idea/inspectionProfiles/Druid.xml b/.idea/inspectionProfiles/Druid.xml index cf4e53758479..2da1cb7f2e13 100644 --- a/.idea/inspectionProfiles/Druid.xml +++ b/.idea/inspectionProfiles/Druid.xml @@ -29,6 +29,7 @@ + diff --git a/extensions-contrib/ambari-metrics-emitter/src/test/java/org/apache/druid/emitter/ambari/metrics/AmbariMetricsEmitterConfigTest.java b/extensions-contrib/ambari-metrics-emitter/src/test/java/org/apache/druid/emitter/ambari/metrics/AmbariMetricsEmitterConfigTest.java index 83fad58ade7d..f2ea37a40fc7 100644 --- a/extensions-contrib/ambari-metrics-emitter/src/test/java/org/apache/druid/emitter/ambari/metrics/AmbariMetricsEmitterConfigTest.java +++ b/extensions-contrib/ambari-metrics-emitter/src/test/java/org/apache/druid/emitter/ambari/metrics/AmbariMetricsEmitterConfigTest.java @@ -56,7 +56,7 @@ public void testSerDeAmbariMetricsEmitterConfig() throws IOException 1000L, 100, new SendAllTimelineEventConverter("prefix", "druid"), - Collections.EMPTY_LIST, + Collections.emptyList(), 500L, 400L ); diff --git a/extensions-contrib/dropwizard-emitter/src/main/java/org/apache/druid/emitter/dropwizard/DropwizardEmitterModule.java b/extensions-contrib/dropwizard-emitter/src/main/java/org/apache/druid/emitter/dropwizard/DropwizardEmitterModule.java index a700b88050cb..524dad165e38 100644 --- a/extensions-contrib/dropwizard-emitter/src/main/java/org/apache/druid/emitter/dropwizard/DropwizardEmitterModule.java +++ b/extensions-contrib/dropwizard-emitter/src/main/java/org/apache/druid/emitter/dropwizard/DropwizardEmitterModule.java @@ -42,7 +42,7 @@ public class DropwizardEmitterModule implements DruidModule @Override public List getJacksonModules() { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } @Override diff --git a/extensions-contrib/graphite-emitter/src/main/java/org/apache/druid/emitter/graphite/GraphiteEmitterModule.java b/extensions-contrib/graphite-emitter/src/main/java/org/apache/druid/emitter/graphite/GraphiteEmitterModule.java index bca977211f5b..3f9904529f12 100644 --- a/extensions-contrib/graphite-emitter/src/main/java/org/apache/druid/emitter/graphite/GraphiteEmitterModule.java +++ b/extensions-contrib/graphite-emitter/src/main/java/org/apache/druid/emitter/graphite/GraphiteEmitterModule.java @@ -44,7 +44,7 @@ public class GraphiteEmitterModule implements DruidModule @Override public List getJacksonModules() { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } @Override diff --git a/extensions-contrib/graphite-emitter/src/test/java/org/apache/druid/emitter/graphite/GraphiteEmitterConfigTest.java b/extensions-contrib/graphite-emitter/src/test/java/org/apache/druid/emitter/graphite/GraphiteEmitterConfigTest.java index a1ebdaaeafe7..7bde9f3c1627 100644 --- a/extensions-contrib/graphite-emitter/src/test/java/org/apache/druid/emitter/graphite/GraphiteEmitterConfigTest.java +++ b/extensions-contrib/graphite-emitter/src/test/java/org/apache/druid/emitter/graphite/GraphiteEmitterConfigTest.java @@ -53,8 +53,8 @@ public void testSerDeserGraphiteEmitterConfig() throws IOException 1000L, 100, new SendAllGraphiteEventConverter("prefix", true, true, false), - Collections.EMPTY_LIST, - Collections.EMPTY_LIST, + Collections.emptyList(), + Collections.emptyList(), null, null ); @@ -92,4 +92,10 @@ public void testSerDeserDruidToGraphiteEventConverter() throws IOException .readValue(whiteListBasedConverterString); Assert.assertEquals(druidToGraphiteEventConverter, whiteListBasedConverter); } + + @Test + public void testJacksonModules() + { + Assert.assertTrue(new GraphiteEmitterModule().getJacksonModules().isEmpty()); + } } diff --git a/extensions-contrib/influxdb-emitter/src/main/java/org/apache/druid/emitter/influxdb/InfluxdbEmitterModule.java b/extensions-contrib/influxdb-emitter/src/main/java/org/apache/druid/emitter/influxdb/InfluxdbEmitterModule.java index f6e6fa49efad..b286a972c182 100644 --- a/extensions-contrib/influxdb-emitter/src/main/java/org/apache/druid/emitter/influxdb/InfluxdbEmitterModule.java +++ b/extensions-contrib/influxdb-emitter/src/main/java/org/apache/druid/emitter/influxdb/InfluxdbEmitterModule.java @@ -42,7 +42,7 @@ public class InfluxdbEmitterModule implements DruidModule @Override public List getJacksonModules() { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } @Override diff --git a/extensions-contrib/influxdb-emitter/src/test/java/org/apache/druid/emitter/influxdb/InfluxdbEmitterTest.java b/extensions-contrib/influxdb-emitter/src/test/java/org/apache/druid/emitter/influxdb/InfluxdbEmitterTest.java index 2095a2f70876..318f38f65c33 100644 --- a/extensions-contrib/influxdb-emitter/src/test/java/org/apache/druid/emitter/influxdb/InfluxdbEmitterTest.java +++ b/extensions-contrib/influxdb-emitter/src/test/java/org/apache/druid/emitter/influxdb/InfluxdbEmitterTest.java @@ -205,4 +205,10 @@ public void testMetricIsInDefaultDimensionWhitelist() String actual = influxdbEmitter.transformForInfluxSystems(event); Assert.assertEquals(expected, actual); } + + @Test + public void testJacksonModules() + { + Assert.assertTrue(new InfluxdbEmitterModule().getJacksonModules().isEmpty()); + } } diff --git a/extensions-contrib/kafka-emitter/src/main/java/org/apache/druid/emitter/kafka/KafkaEmitterModule.java b/extensions-contrib/kafka-emitter/src/main/java/org/apache/druid/emitter/kafka/KafkaEmitterModule.java index 795d723d10b6..f83932ed78e9 100644 --- a/extensions-contrib/kafka-emitter/src/main/java/org/apache/druid/emitter/kafka/KafkaEmitterModule.java +++ b/extensions-contrib/kafka-emitter/src/main/java/org/apache/druid/emitter/kafka/KafkaEmitterModule.java @@ -39,7 +39,7 @@ public class KafkaEmitterModule implements DruidModule @Override public List getJacksonModules() { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } @Override diff --git a/extensions-contrib/kafka-emitter/src/test/java/org/apache/druid/emitter/kafka/KafkaEmitterConfigTest.java b/extensions-contrib/kafka-emitter/src/test/java/org/apache/druid/emitter/kafka/KafkaEmitterConfigTest.java index 761cbc4160fc..89e75fc28089 100644 --- a/extensions-contrib/kafka-emitter/src/test/java/org/apache/druid/emitter/kafka/KafkaEmitterConfigTest.java +++ b/extensions-contrib/kafka-emitter/src/test/java/org/apache/druid/emitter/kafka/KafkaEmitterConfigTest.java @@ -68,4 +68,10 @@ public void testSerDeNotRequiredKafkaProducerConfig() Assert.fail(); } } + + @Test + public void testJacksonModules() + { + Assert.assertTrue(new KafkaEmitterModule().getJacksonModules().isEmpty()); + } } diff --git a/extensions-contrib/opentsdb-emitter/src/main/java/org/apache/druid/emitter/opentsdb/OpentsdbEmitterModule.java b/extensions-contrib/opentsdb-emitter/src/main/java/org/apache/druid/emitter/opentsdb/OpentsdbEmitterModule.java index b107161fe8e0..a46d7150a54f 100644 --- a/extensions-contrib/opentsdb-emitter/src/main/java/org/apache/druid/emitter/opentsdb/OpentsdbEmitterModule.java +++ b/extensions-contrib/opentsdb-emitter/src/main/java/org/apache/druid/emitter/opentsdb/OpentsdbEmitterModule.java @@ -39,7 +39,7 @@ public class OpentsdbEmitterModule implements DruidModule @Override public List getJacksonModules() { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } @Override diff --git a/extensions-contrib/opentsdb-emitter/src/test/java/org/apache/druid/emitter/opentsdb/OpentsdbEmitterConfigTest.java b/extensions-contrib/opentsdb-emitter/src/test/java/org/apache/druid/emitter/opentsdb/OpentsdbEmitterConfigTest.java index b533877faa32..66c8f62e3320 100644 --- a/extensions-contrib/opentsdb-emitter/src/test/java/org/apache/druid/emitter/opentsdb/OpentsdbEmitterConfigTest.java +++ b/extensions-contrib/opentsdb-emitter/src/test/java/org/apache/druid/emitter/opentsdb/OpentsdbEmitterConfigTest.java @@ -76,4 +76,9 @@ public void testSerDeserOpentsdbEmitterConfigWithEmptyNamespacePrefix() throws E Assert.assertEquals(expectedOpentsdbEmitterConfig, opentsdbEmitterConfig); } + @Test + public void testJacksonModules() + { + Assert.assertTrue(new OpentsdbEmitterModule().getJacksonModules().isEmpty()); + } } diff --git a/extensions-contrib/statsd-emitter/src/main/java/org/apache/druid/emitter/statsd/StatsDEmitterModule.java b/extensions-contrib/statsd-emitter/src/main/java/org/apache/druid/emitter/statsd/StatsDEmitterModule.java index 0af90124f639..50bebdf0c447 100644 --- a/extensions-contrib/statsd-emitter/src/main/java/org/apache/druid/emitter/statsd/StatsDEmitterModule.java +++ b/extensions-contrib/statsd-emitter/src/main/java/org/apache/druid/emitter/statsd/StatsDEmitterModule.java @@ -41,7 +41,7 @@ public class StatsDEmitterModule implements DruidModule @Override public List getJacksonModules() { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } @Override diff --git a/extensions-contrib/statsd-emitter/src/test/java/org/apache/druid/emitter/statsd/StatsDEmitterTest.java b/extensions-contrib/statsd-emitter/src/test/java/org/apache/druid/emitter/statsd/StatsDEmitterTest.java index 360f511eb9dc..ebab54e47a55 100644 --- a/extensions-contrib/statsd-emitter/src/test/java/org/apache/druid/emitter/statsd/StatsDEmitterTest.java +++ b/extensions-contrib/statsd-emitter/src/test/java/org/apache/druid/emitter/statsd/StatsDEmitterTest.java @@ -236,4 +236,10 @@ public void testAlertEvent() Assert.assertEquals(expectedEvent.getTitle(), actualEvent.getTitle()); Assert.assertEquals(expectedEvent.getText(), actualEvent.getText()); } + + @Test + public void testJacksonModules() + { + Assert.assertTrue(new StatsDEmitterModule().getJacksonModules().isEmpty()); + } } diff --git a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/supervisor/KafkaSupervisorTest.java b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/supervisor/KafkaSupervisorTest.java index 086a9e9d10a0..9d8635dd39a9 100644 --- a/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/supervisor/KafkaSupervisorTest.java +++ b/extensions-core/kafka-indexing-service/src/test/java/org/apache/druid/indexing/kafka/supervisor/KafkaSupervisorTest.java @@ -780,7 +780,7 @@ public void testDontKillTasksWithMismatchedType() throws Exception EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(existingTasks).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(Status.NOT_STARTED)) @@ -928,7 +928,7 @@ public void testRequeueTaskWhenFailed() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(Status.NOT_STARTED)) @@ -1032,7 +1032,7 @@ public void testRequeueAdoptedTaskWhenFailed() throws Exception Capture captured = Capture.newInstance(); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(existingTasks).anyTimes(); EasyMock.expect(taskStorage.getStatus("id1")).andReturn(Optional.of(TaskStatus.running("id1"))).anyTimes(); EasyMock.expect(taskStorage.getTask("id1")).andReturn(Optional.of(id1)).anyTimes(); @@ -1121,7 +1121,7 @@ public void testQueueNextTasksOnSuccess() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(Status.NOT_STARTED)) @@ -1224,7 +1224,7 @@ public void testBeginPublishAndQueueNextTasks() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(indexerMetadataStorageCoordinator.retrieveDataSourceMetadata(DATASOURCE)).andReturn( new KafkaDataSourceMetadata( @@ -1722,7 +1722,7 @@ public void testKillUnresponsiveTasksWhilePausing() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(indexerMetadataStorageCoordinator.retrieveDataSourceMetadata(DATASOURCE)).andReturn( new KafkaDataSourceMetadata( @@ -1808,7 +1808,7 @@ public void testKillUnresponsiveTasksWhileSettingEndOffsets() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(indexerMetadataStorageCoordinator.retrieveDataSourceMetadata(DATASOURCE)).andReturn( new KafkaDataSourceMetadata( @@ -2032,7 +2032,7 @@ public void testResetNoTasks() { EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); taskRunner.registerListener(EasyMock.anyObject(TaskRunnerListener.class), EasyMock.anyObject(Executor.class)); replayAll(); @@ -2057,7 +2057,7 @@ public void testResetDataSourceMetadata() throws Exception supervisor = getTestableSupervisor(1, 1, true, "PT1H", null, null); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); taskRunner.registerListener(EasyMock.anyObject(TaskRunnerListener.class), EasyMock.anyObject(Executor.class)); replayAll(); @@ -2113,7 +2113,7 @@ public void testResetNoDataSourceMetadata() supervisor = getTestableSupervisor(1, 1, true, "PT1H", null, null); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); taskRunner.registerListener(EasyMock.anyObject(TaskRunnerListener.class), EasyMock.anyObject(Executor.class)); replayAll(); @@ -2146,7 +2146,7 @@ public void testGetOffsetFromStorageForPartitionWithResetOffsetAutomatically() t supervisor = getTestableSupervisor(1, 1, true, true, "PT1H", null, null); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); taskRunner.registerListener(EasyMock.anyObject(TaskRunnerListener.class), EasyMock.anyObject(Executor.class)); @@ -2749,7 +2749,7 @@ public void testResetSuspended() { EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); taskRunner.registerListener(EasyMock.anyObject(TaskRunnerListener.class), EasyMock.anyObject(Executor.class)); replayAll(); @@ -2960,7 +2960,7 @@ public void testDoNotKillCompatibleTasks() EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(existingTasks).anyTimes(); EasyMock.expect(taskStorage.getStatus("id1")).andReturn(Optional.of(TaskStatus.running("id1"))).anyTimes(); EasyMock.expect(taskStorage.getTask("id1")).andReturn(Optional.of(task)).anyTimes(); @@ -3029,7 +3029,7 @@ public void testKillIncompatibleTasks() List existingTasks = ImmutableList.of(task); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(existingTasks).anyTimes(); EasyMock.expect(taskStorage.getStatus("id1")).andReturn(Optional.of(TaskStatus.running("id1"))).anyTimes(); EasyMock.expect(taskStorage.getTask("id1")).andReturn(Optional.of(task)).anyTimes(); diff --git a/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/supervisor/KinesisSupervisorTest.java b/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/supervisor/KinesisSupervisorTest.java index f3a21d9b2278..6bd309c8b7d3 100644 --- a/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/supervisor/KinesisSupervisorTest.java +++ b/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/supervisor/KinesisSupervisorTest.java @@ -721,7 +721,7 @@ public void testDontKillTasksWithMismatchedType() throws Exception EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(existingTasks).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(SeekableStreamIndexTaskRunner.Status.NOT_STARTED)) @@ -815,7 +815,7 @@ public void testKillBadPartitionAssignment() throws Exception EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(existingTasks).anyTimes(); EasyMock.expect(taskStorage.getStatus("id1")).andReturn(Optional.of(TaskStatus.running("id1"))).anyTimes(); EasyMock.expect(taskStorage.getStatus("id2")).andReturn(Optional.of(TaskStatus.running("id2"))).anyTimes(); @@ -886,7 +886,7 @@ public void testRequeueTaskWhenFailed() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(SeekableStreamIndexTaskRunner.Status.NOT_STARTED)) @@ -1134,7 +1134,7 @@ public void testQueueNextTasksOnSuccess() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(SeekableStreamIndexTaskRunner.Status.NOT_STARTED)) @@ -1260,7 +1260,7 @@ public void testBeginPublishAndQueueNextTasks() throws Exception final Capture firstTasks = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(indexerMetadataStorageCoordinator.retrieveDataSourceMetadata(DATASOURCE)).andReturn( new KinesisDataSourceMetadata(null) @@ -1882,7 +1882,7 @@ public void testKillUnresponsiveTasksWhileGettingStartTime() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(indexerMetadataStorageCoordinator.retrieveDataSourceMetadata(DATASOURCE)).andReturn( new KinesisDataSourceMetadata( @@ -1964,7 +1964,7 @@ public void testKillUnresponsiveTasksWhilePausing() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(indexerMetadataStorageCoordinator.retrieveDataSourceMetadata(DATASOURCE)).andReturn( new KinesisDataSourceMetadata( @@ -2072,7 +2072,7 @@ public void testKillUnresponsiveTasksWhileSettingEndOffsets() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(indexerMetadataStorageCoordinator.retrieveDataSourceMetadata(DATASOURCE)).andReturn( new KinesisDataSourceMetadata( @@ -2380,7 +2380,7 @@ public void testResetNoTasks() EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); taskRunner.registerListener(EasyMock.anyObject(TaskRunnerListener.class), EasyMock.anyObject(Executor.class)); replayAll(); @@ -2410,7 +2410,7 @@ public void testResetDataSourceMetadata() throws Exception supervisor = getTestableSupervisor(1, 1, true, "PT1H", null, null); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); taskRunner.registerListener(EasyMock.anyObject(TaskRunnerListener.class), EasyMock.anyObject(Executor.class)); replayAll(); @@ -2538,7 +2538,7 @@ public void testGetOffsetFromStorageForPartitionWithResetOffsetAutomatically() t EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); taskRunner.registerListener(EasyMock.anyObject(TaskRunnerListener.class), EasyMock.anyObject(Executor.class)); EasyMock.reset(indexerMetadataStorageCoordinator); @@ -3409,7 +3409,7 @@ public void testResetSuspended() .anyTimes(); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); taskRunner.registerListener(EasyMock.anyObject(TaskRunnerListener.class), EasyMock.anyObject(Executor.class)); replayAll(); @@ -3536,7 +3536,7 @@ public void testDoNotKillCompatibleTasks() EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(existingTasks).anyTimes(); EasyMock.expect(taskStorage.getStatus("id2")).andReturn(Optional.of(TaskStatus.running("id2"))).anyTimes(); EasyMock.expect(taskStorage.getTask("id2")).andReturn(Optional.of(task)).anyTimes(); @@ -3633,7 +3633,7 @@ public void testKillIncompatibleTasks() EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(existingTasks).anyTimes(); EasyMock.expect(taskStorage.getStatus("id1")).andReturn(Optional.of(TaskStatus.running("id1"))).anyTimes(); EasyMock.expect(taskStorage.getTask("id1")).andReturn(Optional.of(task)).anyTimes(); @@ -3850,7 +3850,7 @@ private List testShardSplitPhaseOne() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(SeekableStreamIndexTaskRunner.Status.NOT_STARTED)) @@ -3959,7 +3959,7 @@ private List testShardSplitPhaseTwo(List phaseOneTasks) throws Excep EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(SeekableStreamIndexTaskRunner.Status.NOT_STARTED)) @@ -4134,7 +4134,7 @@ private void testShardSplitPhaseThree(List phaseTwoTasks) throws Exception EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(SeekableStreamIndexTaskRunner.Status.NOT_STARTED)) @@ -4296,7 +4296,7 @@ private List testShardMergePhaseOne() throws Exception Capture captured = Capture.newInstance(CaptureType.ALL); EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(SeekableStreamIndexTaskRunner.Status.NOT_STARTED)) @@ -4416,7 +4416,7 @@ private List testShardMergePhaseTwo(List phaseOneTasks) throws Excep EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(SeekableStreamIndexTaskRunner.Status.NOT_STARTED)) @@ -4570,7 +4570,7 @@ private void testShardMergePhaseThree(List phaseTwoTasks) throws Exception EasyMock.expect(taskMaster.getTaskQueue()).andReturn(Optional.of(taskQueue)).anyTimes(); EasyMock.expect(taskMaster.getTaskRunner()).andReturn(Optional.of(taskRunner)).anyTimes(); - EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.EMPTY_LIST).anyTimes(); + EasyMock.expect(taskRunner.getRunningTasks()).andReturn(Collections.emptyList()).anyTimes(); EasyMock.expect(taskStorage.getActiveTasksByDatasource(DATASOURCE)).andReturn(ImmutableList.of()).anyTimes(); EasyMock.expect(taskClient.getStatusAsync(EasyMock.anyString())) .andReturn(Futures.immediateFuture(SeekableStreamIndexTaskRunner.Status.NOT_STARTED)) diff --git a/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/PollingLookupTest.java b/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/PollingLookupTest.java index cb31eb404f05..c276b742b101 100644 --- a/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/PollingLookupTest.java +++ b/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/PollingLookupTest.java @@ -177,7 +177,7 @@ public void testUnapply() ); Assert.assertEquals( "reverse lookup of none existing value should be empty list", - Collections.EMPTY_LIST, + Collections.emptyList(), pollingLookup.unapply("does't exist") ); } diff --git a/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/cache/loading/LoadingCacheTest.java b/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/cache/loading/LoadingCacheTest.java index f0fc6aff287e..61c81e719ebf 100644 --- a/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/cache/loading/LoadingCacheTest.java +++ b/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/cache/loading/LoadingCacheTest.java @@ -141,7 +141,7 @@ public Object call() return "value2"; } }); - Assert.assertEquals(loadingCache.getAllPresent(IMMUTABLE_MAP.keySet()), Collections.EMPTY_MAP); + Assert.assertEquals(loadingCache.getAllPresent(IMMUTABLE_MAP.keySet()), Collections.emptyMap()); } @Test diff --git a/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/jdbc/JdbcDataFetcherTest.java b/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/jdbc/JdbcDataFetcherTest.java index 7fc50e484238..9cb363052ca4 100644 --- a/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/jdbc/JdbcDataFetcherTest.java +++ b/extensions-core/lookups-cached-single/src/test/java/org/apache/druid/server/lookup/jdbc/JdbcDataFetcherTest.java @@ -147,7 +147,7 @@ public void testReverseFetch() ); Assert.assertEquals( "reverse lookup of none existing value should be empty list", - Collections.EMPTY_LIST, + Collections.emptyList(), jdbcDataFetcher.reverseFetchKeys("does't exist") ); } diff --git a/indexing-hadoop/src/test/java/org/apache/druid/indexer/InputRowSerdeTest.java b/indexing-hadoop/src/test/java/org/apache/druid/indexer/InputRowSerdeTest.java index 3eb03b6807ff..b99468b13969 100644 --- a/indexing-hadoop/src/test/java/org/apache/druid/indexer/InputRowSerdeTest.java +++ b/indexing-hadoop/src/test/java/org/apache/druid/indexer/InputRowSerdeTest.java @@ -147,7 +147,7 @@ public void testSerde() Assert.assertEquals(timestamp, out.getTimestampFromEpoch()); Assert.assertEquals(dims, out.getDimensions()); - Assert.assertEquals(Collections.EMPTY_LIST, out.getDimension("dim_non_existing")); + Assert.assertEquals(Collections.emptyList(), out.getDimension("dim_non_existing")); Assert.assertEquals(ImmutableList.of("d1v"), out.getDimension("d1")); Assert.assertEquals(ImmutableList.of("d2v1", "d2v2"), out.getDimension("d2")); Assert.assertEquals(200L, out.getRaw("d3")); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/setup/WorkerCategorySpec.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/setup/WorkerCategorySpec.java index 57340bc8f48b..9f58d62d05a9 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/setup/WorkerCategorySpec.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/setup/WorkerCategorySpec.java @@ -38,7 +38,7 @@ public WorkerCategorySpec( @JsonProperty("strong") boolean strong ) { - this.categoryMap = categoryMap == null ? Collections.EMPTY_MAP : categoryMap; + this.categoryMap = categoryMap == null ? Collections.emptyMap() : categoryMap; this.strong = strong; } @@ -96,7 +96,7 @@ public CategoryConfig( ) { this.defaultCategory = defaultCategory; - this.categoryAffinity = categoryAffinity == null ? Collections.EMPTY_MAP : categoryAffinity; + this.categoryAffinity = categoryAffinity == null ? Collections.emptyMap() : categoryAffinity; } @JsonProperty diff --git a/integration-tests/src/main/java/org/apache/druid/testing/guice/ITTLSCertificateCheckerModule.java b/integration-tests/src/main/java/org/apache/druid/testing/guice/ITTLSCertificateCheckerModule.java index 6166359693a1..5d2132322290 100644 --- a/integration-tests/src/main/java/org/apache/druid/testing/guice/ITTLSCertificateCheckerModule.java +++ b/integration-tests/src/main/java/org/apache/druid/testing/guice/ITTLSCertificateCheckerModule.java @@ -46,7 +46,7 @@ public void configure(Binder binder) @Override public List getJacksonModules() { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } } diff --git a/processing/src/main/java/org/apache/druid/query/lookup/LookupsState.java b/processing/src/main/java/org/apache/druid/query/lookup/LookupsState.java index 685fd61c4467..35037594a131 100644 --- a/processing/src/main/java/org/apache/druid/query/lookup/LookupsState.java +++ b/processing/src/main/java/org/apache/druid/query/lookup/LookupsState.java @@ -42,9 +42,9 @@ public LookupsState( @JsonProperty("toDrop") Set toDrop ) { - this.current = current == null ? Collections.EMPTY_MAP : current; - this.toLoad = toLoad == null ? Collections.EMPTY_MAP : toLoad; - this.toDrop = toDrop == null ? Collections.EMPTY_SET : toDrop; + this.current = current == null ? Collections.emptyMap() : current; + this.toLoad = toLoad == null ? Collections.emptyMap() : toLoad; + this.toDrop = toDrop == null ? Collections.emptySet() : toDrop; } @JsonProperty diff --git a/processing/src/test/java/org/apache/druid/query/aggregation/StringColumnAggregationTest.java b/processing/src/test/java/org/apache/druid/query/aggregation/StringColumnAggregationTest.java index b465925a8532..30419b04740f 100644 --- a/processing/src/test/java/org/apache/druid/query/aggregation/StringColumnAggregationTest.java +++ b/processing/src/test/java/org/apache/druid/query/aggregation/StringColumnAggregationTest.java @@ -96,7 +96,7 @@ public void setup() throws Exception } aggregationTestHelper = AggregationTestHelper.createGroupByQueryAggregationTestHelper( - Collections.EMPTY_LIST, + Collections.emptyList(), new GroupByQueryConfig(), tempFolder ); @@ -231,7 +231,7 @@ public void testTimeseries() ) .build(); - Sequence seq = AggregationTestHelper.createTimeseriesQueryAggregationTestHelper(Collections.EMPTY_LIST, tempFolder) + Sequence seq = AggregationTestHelper.createTimeseriesQueryAggregationTestHelper(Collections.emptyList(), tempFolder) .runQueryOnSegmentsObjs(segments, query); TimeseriesResultValue result = ((Result) Iterables.getOnlyElement(seq.toList())).getValue(); diff --git a/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java b/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java index 8b0d57b09cdd..cf1401993c39 100644 --- a/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java +++ b/processing/src/test/java/org/apache/druid/query/aggregation/mean/DoubleMeanAggregationTest.java @@ -72,13 +72,13 @@ public DoubleMeanAggregationTest() { groupByQueryTestHelper = AggregationTestHelper.createGroupByQueryAggregationTestHelper( - Collections.EMPTY_LIST, + Collections.emptyList(), new GroupByQueryConfig(), tempFolder ); timeseriesQueryTestHelper = AggregationTestHelper.createTimeseriesQueryAggregationTestHelper( - Collections.EMPTY_LIST, + Collections.emptyList(), tempFolder ); diff --git a/processing/src/test/java/org/apache/druid/query/extraction/MapLookupExtractorTest.java b/processing/src/test/java/org/apache/druid/query/extraction/MapLookupExtractorTest.java index 80ffdc2e0603..ab180646e585 100644 --- a/processing/src/test/java/org/apache/druid/query/extraction/MapLookupExtractorTest.java +++ b/processing/src/test/java/org/apache/druid/query/extraction/MapLookupExtractorTest.java @@ -64,7 +64,7 @@ public void testUnApply() ); } Assert.assertEquals(Sets.newHashSet(""), Sets.newHashSet(fn.unapply("empty_string"))); - Assert.assertEquals("not existing value returns empty list", Collections.EMPTY_LIST, fn.unapply("not There")); + Assert.assertEquals("not existing value returns empty list", Collections.emptyList(), fn.unapply("not There")); } @Test diff --git a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java index 6719ac622328..31c438f8aab6 100644 --- a/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java +++ b/processing/src/test/java/org/apache/druid/query/groupby/GroupByQueryRunnerTest.java @@ -9879,7 +9879,7 @@ public void testGroupByLimitPushDownWithLongDimensionNotInLimitSpec() .setInterval(QueryRunnerTestHelper.FULL_ON_INTERVAL_SPEC) .setLimitSpec( new DefaultLimitSpec( - Collections.EMPTY_LIST, + Collections.emptyList(), 6 ) ).setAggregatorSpecs(QueryRunnerTestHelper.ROWS_COUNT) diff --git a/processing/src/test/java/org/apache/druid/query/groupby/orderby/TopNSequenceTest.java b/processing/src/test/java/org/apache/druid/query/groupby/orderby/TopNSequenceTest.java index 4343df5d3793..24238af0ab83 100644 --- a/processing/src/test/java/org/apache/druid/query/groupby/orderby/TopNSequenceTest.java +++ b/processing/src/test/java/org/apache/druid/query/groupby/orderby/TopNSequenceTest.java @@ -42,7 +42,7 @@ public class TopNSequenceTest private static final Ordering ASC = Ordering.natural(); private static final Ordering DESC = Ordering.natural().reverse(); - private static final List EMPTY = Collections.EMPTY_LIST; + private static final List EMPTY = Collections.emptyList(); private static final List SINGLE = Collections.singletonList("a"); private static final List RAW_ASC = Lists.newArrayList(Splitter.fixedLength(1).split("abcdefghijk")); private static final List RAW_DESC = Lists.newArrayList(Splitter.fixedLength(1).split("kjihgfedcba")); diff --git a/processing/src/test/java/org/apache/druid/segment/virtual/DummyStringVirtualColumnTest.java b/processing/src/test/java/org/apache/druid/segment/virtual/DummyStringVirtualColumnTest.java index d17200fb5b0e..c16946093723 100644 --- a/processing/src/test/java/org/apache/druid/segment/virtual/DummyStringVirtualColumnTest.java +++ b/processing/src/test/java/org/apache/druid/segment/virtual/DummyStringVirtualColumnTest.java @@ -79,12 +79,12 @@ public DummyStringVirtualColumnTest() mixedSegments = Lists.newArrayList(incrementalIndexSegment, queryableIndexSegment); topNTestHelper = AggregationTestHelper.createTopNQueryAggregationTestHelper( - Collections.EMPTY_LIST, + Collections.emptyList(), null ); groupByTestHelper = AggregationTestHelper.createGroupByQueryAggregationTestHelper( - Collections.EMPTY_LIST, + Collections.emptyList(), new GroupByQueryConfig(), null ); diff --git a/server/src/main/java/org/apache/druid/server/emitter/ComposingEmitterModule.java b/server/src/main/java/org/apache/druid/server/emitter/ComposingEmitterModule.java index 27a35fd68df5..e466319a3b07 100644 --- a/server/src/main/java/org/apache/druid/server/emitter/ComposingEmitterModule.java +++ b/server/src/main/java/org/apache/druid/server/emitter/ComposingEmitterModule.java @@ -53,7 +53,7 @@ public void configure(Binder binder) @Override public List getJacksonModules() { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } @Provides diff --git a/server/src/main/java/org/apache/druid/server/initialization/AuthenticatorMapperModule.java b/server/src/main/java/org/apache/druid/server/initialization/AuthenticatorMapperModule.java index d7139da3ec82..f2f2e990fe20 100644 --- a/server/src/main/java/org/apache/druid/server/initialization/AuthenticatorMapperModule.java +++ b/server/src/main/java/org/apache/druid/server/initialization/AuthenticatorMapperModule.java @@ -62,11 +62,10 @@ public void configure(Binder binder) LifecycleModule.register(binder, AuthenticatorMapper.class); } - @SuppressWarnings("unchecked") @Override public List getJacksonModules() { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } private static class AuthenticatorMapperProvider implements Provider diff --git a/server/src/main/java/org/apache/druid/server/initialization/AuthorizerMapperModule.java b/server/src/main/java/org/apache/druid/server/initialization/AuthorizerMapperModule.java index 48de9cbfc643..c8d9482b0450 100644 --- a/server/src/main/java/org/apache/druid/server/initialization/AuthorizerMapperModule.java +++ b/server/src/main/java/org/apache/druid/server/initialization/AuthorizerMapperModule.java @@ -62,11 +62,10 @@ public void configure(Binder binder) LifecycleModule.register(binder, AuthorizerMapper.class); } - @SuppressWarnings("unchecked") @Override public List getJacksonModules() { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } private static class AuthorizerMapperProvider implements Provider diff --git a/server/src/test/java/org/apache/druid/query/lookup/LookupSnapshotTakerTest.java b/server/src/test/java/org/apache/druid/query/lookup/LookupSnapshotTakerTest.java index 8e1cbe9c8b17..a6e752ff65dc 100644 --- a/server/src/test/java/org/apache/druid/query/lookup/LookupSnapshotTakerTest.java +++ b/server/src/test/java/org/apache/druid/query/lookup/LookupSnapshotTakerTest.java @@ -125,7 +125,7 @@ public void tesLookupPullingFromEmptyFile() throws IOException { File snapshotFile = lookupSnapshotTaker.getPersistFile(TIER1); Assert.assertTrue(snapshotFile.createNewFile()); - Assert.assertEquals(Collections.EMPTY_LIST, lookupSnapshotTaker.pullExistingSnapshot(TIER1)); + Assert.assertEquals(Collections.emptyList(), lookupSnapshotTaker.pullExistingSnapshot(TIER1)); } @Test(expected = ISE.class) @@ -144,6 +144,6 @@ public void testLookupPullingFromNonExistingFile() throws IOException File directory = temporaryFolder.newFolder(); LookupSnapshotTaker lookupSnapshotTaker = new LookupSnapshotTaker(mapper, directory.getAbsolutePath()); List actualList = lookupSnapshotTaker.pullExistingSnapshot(TIER1); - Assert.assertEquals(Collections.EMPTY_LIST, actualList); + Assert.assertEquals(Collections.emptyList(), actualList); } } diff --git a/server/src/test/java/org/apache/druid/server/lookup/cache/LookupCoordinatorManagerTest.java b/server/src/test/java/org/apache/druid/server/lookup/cache/LookupCoordinatorManagerTest.java index 962c950e7ee8..caa93cad87e6 100644 --- a/server/src/test/java/org/apache/druid/server/lookup/cache/LookupCoordinatorManagerTest.java +++ b/server/src/test/java/org/apache/druid/server/lookup/cache/LookupCoordinatorManagerTest.java @@ -108,7 +108,7 @@ public class LookupCoordinatorManagerTest private static final LookupsState LOOKUPS_STATE = new LookupsState<>( SINGLE_LOOKUP_MAP_V0, SINGLE_LOOKUP_MAP_V1, - Collections.EMPTY_SET + Collections.emptySet() ); private static final AtomicLong EVENT_EMITS = new AtomicLong(0L); @@ -1352,7 +1352,7 @@ public void testMultipleStartStop() throws Exception EasyMock.anyObject(), EasyMock.isNull() )).andReturn( - new AtomicReference<>(Collections.EMPTY_MAP)).anyTimes(); + new AtomicReference<>(Collections.emptyMap())).anyTimes(); EasyMock.replay(configManager); diff --git a/server/src/test/java/org/apache/druid/server/shard/NumberedShardSpecTest.java b/server/src/test/java/org/apache/druid/server/shard/NumberedShardSpecTest.java index bfd72b4b8851..0cbfc3c347d7 100644 --- a/server/src/test/java/org/apache/druid/server/shard/NumberedShardSpecTest.java +++ b/server/src/test/java/org/apache/druid/server/shard/NumberedShardSpecTest.java @@ -128,29 +128,29 @@ public void testVersionedIntervalTimelineBehaviorForNumberedShardSpec() //incomplete partition sets testVersionedIntervalTimelineBehaviorForNumberedShardSpec( ImmutableList.of(chunk0), - Collections.EMPTY_SET + Collections.emptySet() ); testVersionedIntervalTimelineBehaviorForNumberedShardSpec( ImmutableList.of(chunk1), - Collections.EMPTY_SET + Collections.emptySet() ); testVersionedIntervalTimelineBehaviorForNumberedShardSpec( ImmutableList.of(chunk4), - Collections.EMPTY_SET + Collections.emptySet() ); testVersionedIntervalTimelineBehaviorForNumberedShardSpec( ImmutableList.of(chunk0, chunk4), - Collections.EMPTY_SET + Collections.emptySet() ); testVersionedIntervalTimelineBehaviorForNumberedShardSpec( ImmutableList.of(chunk1, chunk4), - Collections.EMPTY_SET + Collections.emptySet() ); //complete partition sets From 1b9670a663b0f7b35438f098623037deb1cc65c6 Mon Sep 17 00:00:00 2001 From: litao <55134131+tomscut@users.noreply.github.com> Date: Fri, 19 Jun 2020 00:48:47 +0800 Subject: [PATCH 092/107] fix docs (#9114) Co-authored-by: tomscut --- docs/configuration/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/configuration/index.md b/docs/configuration/index.md index 34b458a49b2b..4632554e4e5d 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -353,6 +353,7 @@ The following monitors are available: |`org.apache.druid.segment.realtime.RealtimeMetricsMonitor`|Reports statistics on Realtime processes.| |`org.apache.druid.server.metrics.EventReceiverFirehoseMonitor`|Reports how many events have been queued in the EventReceiverFirehose.| |`org.apache.druid.server.metrics.QueryCountStatsMonitor`|Reports how many queries have been successful/failed/interrupted.| +|`org.apache.druid.server.metrics.TaskCountStatsMonitor`|Reports how many tasks are success/failed/running/pending/waiting.| |`org.apache.druid.server.emitter.HttpEmittingMonitor`|Reports internal metrics of `http` or `parametrized` emitter (see below). Must not be used with another emitter type. See the description of the metrics here: https://github.com/apache/druid/pull/4973.| |`org.apache.druid.server.metrics.TaskCountStatsMonitor`|Reports how many ingestion tasks are currently running/pending/waiting and also the number of successful/failed tasks per emission period.| From 4cf2a8f00b380a6deaff3e48e3377bc233172f1e Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Thu, 18 Jun 2020 17:32:10 -0700 Subject: [PATCH 093/107] global table only if joinable (#10041) * global table if only joinable * oops * fix style, add more tests * Update sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java * better information schema columns, distinguish broadcast from joinable * fix javadoc * fix mistake Co-authored-by: Jihoon Son --- .../movingaverage/MovingAverageQueryTest.java | 3 + .../org/apache/druid/query/DataSource.java | 9 ++ .../query/planning/DataSourceAnalysis.java | 2 +- .../druid/segment/join/JoinableFactory.java | 7 + .../segment/join/MapJoinableFactory.java | 11 ++ .../druid/segment/join/JoinablesTest.java | 26 +++- .../segment/join/MapJoinableFactoryTest.java | 16 +++ .../segment/join/NoopJoinableFactory.java | 6 + .../segment/join/InlineJoinableFactory.java | 9 ++ .../segment/join/LookupJoinableFactory.java | 7 + .../server/ClientQuerySegmentWalker.java | 47 ++++++- .../join/InlineJoinableFactoryTest.java | 7 + .../join/LookupJoinableFactoryTest.java | 7 + .../server/ClientQuerySegmentWalkerTest.java | 57 ++++++++- .../apache/druid/server/QueryStackTests.java | 2 + .../sql/calcite/rel/DruidJoinQueryRel.java | 2 + .../druid/sql/calcite/schema/DruidSchema.java | 24 +++- .../sql/calcite/schema/InformationSchema.java | 43 +++++-- .../sql/calcite/schema/LookupSchema.java | 4 +- .../druid/sql/calcite/table/DruidTable.java | 18 ++- .../druid/sql/calcite/CalciteQueryTest.java | 78 ++++++------ .../schema/DruidCalciteSchemaModuleTest.java | 4 + .../schema/DruidSchemaNoDataInitTest.java | 2 + .../sql/calcite/schema/DruidSchemaTest.java | 120 ++++++++++++++++-- .../sql/calcite/schema/SystemSchemaTest.java | 2 + .../druid/sql/calcite/util/CalciteTests.java | 2 + .../SpecificSegmentsQuerySegmentWalker.java | 1 + 27 files changed, 435 insertions(+), 81 deletions(-) diff --git a/extensions-contrib/moving-average-query/src/test/java/org/apache/druid/query/movingaverage/MovingAverageQueryTest.java b/extensions-contrib/moving-average-query/src/test/java/org/apache/druid/query/movingaverage/MovingAverageQueryTest.java index 971f8ed8b264..23dc65f5235c 100644 --- a/extensions-contrib/moving-average-query/src/test/java/org/apache/druid/query/movingaverage/MovingAverageQueryTest.java +++ b/extensions-contrib/moving-average-query/src/test/java/org/apache/druid/query/movingaverage/MovingAverageQueryTest.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.inject.Injector; import com.google.inject.Module; @@ -64,6 +65,7 @@ import org.apache.druid.query.planning.DataSourceAnalysis; import org.apache.druid.query.timeseries.TimeseriesQuery; import org.apache.druid.query.timeseries.TimeseriesResultValue; +import org.apache.druid.segment.join.MapJoinableFactory; import org.apache.druid.server.ClientQuerySegmentWalker; import org.apache.druid.server.QueryStackTests; import org.apache.druid.server.initialization.ServerConfig; @@ -377,6 +379,7 @@ public void emit(Event event) baseClient, null /* local client; unused in this test, so pass in null */, warehouse, + new MapJoinableFactory(ImmutableMap.of()), retryConfig, jsonMapper, serverConfig, diff --git a/processing/src/main/java/org/apache/druid/query/DataSource.java b/processing/src/main/java/org/apache/druid/query/DataSource.java index c3edd1c79647..d12a8eeabab8 100644 --- a/processing/src/main/java/org/apache/druid/query/DataSource.java +++ b/processing/src/main/java/org/apache/druid/query/DataSource.java @@ -71,6 +71,15 @@ public interface DataSource /** * Returns true if all servers have a full copy of this datasource. True for things like inline, lookup, etc, or * for queries of those. + * + * Currently this is coupled with joinability - if this returns true then the query engine expects there exists a + * {@link org.apache.druid.segment.join.JoinableFactory} which might build a + * {@link org.apache.druid.segment.join.Joinable} for this datasource directly. If a subquery 'inline' join is + * required to join this datasource on the right hand side, then this value must be false for now. + * + * In the future, instead of directly using this method, the query planner and engine should consider + * {@link org.apache.druid.segment.join.JoinableFactory#isDirectlyJoinable(DataSource)} when determining if the + * right hand side is directly joinable, which would allow decoupling this property from joins. */ boolean isGlobal(); diff --git a/processing/src/main/java/org/apache/druid/query/planning/DataSourceAnalysis.java b/processing/src/main/java/org/apache/druid/query/planning/DataSourceAnalysis.java index 4237e50dc473..5b34f7623fed 100644 --- a/processing/src/main/java/org/apache/druid/query/planning/DataSourceAnalysis.java +++ b/processing/src/main/java/org/apache/druid/query/planning/DataSourceAnalysis.java @@ -205,7 +205,7 @@ public List getPreJoinableClauses() /** * Returns true if all servers have the ability to compute this datasource. These datasources depend only on - * globally broadcast data, like lookups or inline data. + * globally broadcast data, like lookups or inline data or broadcast segments. */ public boolean isGlobal() { diff --git a/processing/src/main/java/org/apache/druid/segment/join/JoinableFactory.java b/processing/src/main/java/org/apache/druid/segment/join/JoinableFactory.java index fc63f1cfbee3..723aba57faa9 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/JoinableFactory.java +++ b/processing/src/main/java/org/apache/druid/segment/join/JoinableFactory.java @@ -30,6 +30,13 @@ */ public interface JoinableFactory { + /** + * Returns true if a {@link Joinable} **may** be created for a given {@link DataSource}, but is not a guarantee that + * {@link #build} will return a non-empty result. Successfully building a {@link Joinable} might require specific + * criteria of the {@link JoinConditionAnalysis}. + */ + boolean isDirectlyJoinable(DataSource dataSource); + /** * Create a Joinable object. This may be an expensive operation involving loading data, creating a hash table, etc. * diff --git a/processing/src/main/java/org/apache/druid/segment/join/MapJoinableFactory.java b/processing/src/main/java/org/apache/druid/segment/join/MapJoinableFactory.java index beb8106225d4..abf4b6ae4d06 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/MapJoinableFactory.java +++ b/processing/src/main/java/org/apache/druid/segment/join/MapJoinableFactory.java @@ -43,6 +43,17 @@ public MapJoinableFactory(Map, JoinableFactory> join this.joinableFactories = new IdentityHashMap<>(joinableFactories); } + @Override + public boolean isDirectlyJoinable(DataSource dataSource) + { + JoinableFactory factory = joinableFactories.get(dataSource.getClass()); + if (factory == null) { + return false; + } else { + return factory.isDirectlyJoinable(dataSource); + } + } + @Override public Optional build(DataSource dataSource, JoinConditionAnalysis condition) { diff --git a/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java b/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java index ae36d175ff16..f7c4f4b415b8 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java @@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap; import org.apache.druid.java.util.common.IAE; import org.apache.druid.math.expr.ExprMacroTable; +import org.apache.druid.query.DataSource; import org.apache.druid.query.LookupDataSource; import org.apache.druid.query.QueryContexts; import org.apache.druid.query.extraction.MapLookupExtractor; @@ -155,13 +156,24 @@ public void test_createSegmentMapFn_usableClause() final Function segmentMapFn = Joinables.createSegmentMapFn( ImmutableList.of(clause), - (dataSource, condition) -> { - if (dataSource.equals(lookupDataSource) && condition.equals(conditionAnalysis)) { - return Optional.of( - LookupJoinable.wrap(new MapLookupExtractor(ImmutableMap.of("k", "v"), false)) - ); - } else { - return Optional.empty(); + new JoinableFactory() + { + @Override + public boolean isDirectlyJoinable(DataSource dataSource) + { + return dataSource.equals(lookupDataSource); + } + + @Override + public Optional build(DataSource dataSource, JoinConditionAnalysis condition) + { + if (dataSource.equals(lookupDataSource) && condition.equals(conditionAnalysis)) { + return Optional.of( + LookupJoinable.wrap(new MapLookupExtractor(ImmutableMap.of("k", "v"), false)) + ); + } else { + return Optional.empty(); + } } }, new AtomicLong(), diff --git a/processing/src/test/java/org/apache/druid/segment/join/MapJoinableFactoryTest.java b/processing/src/test/java/org/apache/druid/segment/join/MapJoinableFactoryTest.java index cf0336061409..1d00e711c190 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/MapJoinableFactoryTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/MapJoinableFactoryTest.java @@ -65,6 +65,8 @@ public void setUp() target = new MapJoinableFactory( ImmutableMap.of(NoopDataSource.class, noopJoinableFactory)); } + + @Test public void testBuildDataSourceNotRegisteredShouldReturnAbsent() { @@ -89,4 +91,18 @@ public void testBuildDataSourceIsRegisteredShouldReturnJoinableFromFactory() Optional joinable = target.build(noopDataSource, condition); Assert.assertEquals(mockJoinable, joinable.get()); } + + @Test + public void testIsDirectShouldBeFalseForNotRegistered() + { + Assert.assertFalse(target.isDirectlyJoinable(inlineDataSource)); + } + + @Test + public void testIsDirectlyJoinableShouldBeTrueForRegisteredThatIsJoinable() + { + EasyMock.expect(noopJoinableFactory.isDirectlyJoinable(noopDataSource)).andReturn(true).anyTimes(); + EasyMock.replay(noopJoinableFactory); + Assert.assertTrue(target.isDirectlyJoinable(noopDataSource)); + } } diff --git a/processing/src/test/java/org/apache/druid/segment/join/NoopJoinableFactory.java b/processing/src/test/java/org/apache/druid/segment/join/NoopJoinableFactory.java index ff138041f131..1583b02b5d5d 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/NoopJoinableFactory.java +++ b/processing/src/test/java/org/apache/druid/segment/join/NoopJoinableFactory.java @@ -32,6 +32,12 @@ private NoopJoinableFactory() // Singleton. } + @Override + public boolean isDirectlyJoinable(DataSource dataSource) + { + return false; + } + @Override public Optional build(DataSource dataSource, JoinConditionAnalysis condition) { diff --git a/server/src/main/java/org/apache/druid/segment/join/InlineJoinableFactory.java b/server/src/main/java/org/apache/druid/segment/join/InlineJoinableFactory.java index 5945d42957cf..4eee53fde5dc 100644 --- a/server/src/main/java/org/apache/druid/segment/join/InlineJoinableFactory.java +++ b/server/src/main/java/org/apache/druid/segment/join/InlineJoinableFactory.java @@ -36,6 +36,15 @@ */ public class InlineJoinableFactory implements JoinableFactory { + @Override + public boolean isDirectlyJoinable(DataSource dataSource) + { + // this should always be true if this is access through MapJoinableFactory, but check just in case... + // further, this should not ever be legitimately called, because this method is used to avoid subquery joins + // which use the InlineJoinableFactory + return dataSource instanceof InlineDataSource; + } + @Override public Optional build(final DataSource dataSource, final JoinConditionAnalysis condition) { diff --git a/server/src/main/java/org/apache/druid/segment/join/LookupJoinableFactory.java b/server/src/main/java/org/apache/druid/segment/join/LookupJoinableFactory.java index a6fd209b1d12..2dab0a616639 100644 --- a/server/src/main/java/org/apache/druid/segment/join/LookupJoinableFactory.java +++ b/server/src/main/java/org/apache/druid/segment/join/LookupJoinableFactory.java @@ -42,6 +42,13 @@ public LookupJoinableFactory(LookupExtractorFactoryContainerProvider lookupProvi this.lookupProvider = lookupProvider; } + @Override + public boolean isDirectlyJoinable(DataSource dataSource) + { + // this should always be true if this is access through MapJoinableFactory, but check just in case... + return dataSource instanceof LookupDataSource; + } + @Override public Optional build(final DataSource dataSource, final JoinConditionAnalysis condition) { diff --git a/server/src/main/java/org/apache/druid/server/ClientQuerySegmentWalker.java b/server/src/main/java/org/apache/druid/server/ClientQuerySegmentWalker.java index fa35ff7b8a9c..c7318a60f7f6 100644 --- a/server/src/main/java/org/apache/druid/server/ClientQuerySegmentWalker.java +++ b/server/src/main/java/org/apache/druid/server/ClientQuerySegmentWalker.java @@ -32,6 +32,7 @@ import org.apache.druid.java.util.emitter.service.ServiceEmitter; import org.apache.druid.query.DataSource; import org.apache.druid.query.FluentQueryRunnerBuilder; +import org.apache.druid.query.GlobalTableDataSource; import org.apache.druid.query.InlineDataSource; import org.apache.druid.query.PostProcessingOperator; import org.apache.druid.query.Query; @@ -47,9 +48,11 @@ import org.apache.druid.query.RetryQueryRunner; import org.apache.druid.query.RetryQueryRunnerConfig; import org.apache.druid.query.SegmentDescriptor; +import org.apache.druid.query.TableDataSource; import org.apache.druid.query.context.ResponseContext; import org.apache.druid.query.planning.DataSourceAnalysis; import org.apache.druid.segment.column.RowSignature; +import org.apache.druid.segment.join.JoinableFactory; import org.apache.druid.server.initialization.ServerConfig; import org.joda.time.Interval; @@ -77,6 +80,7 @@ public class ClientQuerySegmentWalker implements QuerySegmentWalker private final QuerySegmentWalker clusterClient; private final QuerySegmentWalker localClient; private final QueryToolChestWarehouse warehouse; + private final JoinableFactory joinableFactory; private final RetryQueryRunnerConfig retryConfig; private final ObjectMapper objectMapper; private final ServerConfig serverConfig; @@ -88,6 +92,7 @@ public ClientQuerySegmentWalker( QuerySegmentWalker clusterClient, QuerySegmentWalker localClient, QueryToolChestWarehouse warehouse, + JoinableFactory joinableFactory, RetryQueryRunnerConfig retryConfig, ObjectMapper objectMapper, ServerConfig serverConfig, @@ -99,6 +104,7 @@ public ClientQuerySegmentWalker( this.clusterClient = clusterClient; this.localClient = localClient; this.warehouse = warehouse; + this.joinableFactory = joinableFactory; this.retryConfig = retryConfig; this.objectMapper = objectMapper; this.serverConfig = serverConfig; @@ -112,6 +118,7 @@ public ClientQuerySegmentWalker( CachingClusteredClient clusterClient, LocalQuerySegmentWalker localClient, QueryToolChestWarehouse warehouse, + JoinableFactory joinableFactory, RetryQueryRunnerConfig retryConfig, ObjectMapper objectMapper, ServerConfig serverConfig, @@ -124,6 +131,7 @@ public ClientQuerySegmentWalker( (QuerySegmentWalker) clusterClient, (QuerySegmentWalker) localClient, warehouse, + joinableFactory, retryConfig, objectMapper, serverConfig, @@ -137,10 +145,13 @@ public QueryRunner getQueryRunnerForIntervals(Query query, Iterable> toolChest = warehouse.getToolChest(query); - // First, do an inlining dry run to see if any inlining is necessary, without actually running the queries. + // transform TableDataSource to GlobalTableDataSource when eligible + // before further transformation to potentially inline + final DataSource freeTradeDataSource = globalizeIfPossible(query.getDataSource()); + // do an inlining dry run to see if any inlining is necessary, without actually running the queries. final int maxSubqueryRows = QueryContexts.getMaxSubqueryRows(query, serverConfig.getMaxSubqueryRows()); final DataSource inlineDryRun = inlineIfNecessary( - query.getDataSource(), + freeTradeDataSource, toolChest, new AtomicInteger(), maxSubqueryRows, @@ -156,7 +167,7 @@ public QueryRunner getQueryRunnerForIntervals(Query query, Iterable newQuery = query.withDataSource( inlineIfNecessary( - query.getDataSource(), + freeTradeDataSource, toolChest, new AtomicInteger(), maxSubqueryRows, @@ -187,10 +198,15 @@ public QueryRunner getQueryRunnerForIntervals(Query query, Iterable QueryRunner getQueryRunnerForSegments(Query query, Iterable specs) { - // Inlining isn't done for segments-based queries. + // Inlining isn't done for segments-based queries, but we still globalify the table datasources if possible + final Query freeTradeQuery = query.withDataSource(globalizeIfPossible(query.getDataSource())); if (canRunQueryUsingClusterWalker(query)) { - return decorateClusterRunner(query, clusterClient.getQueryRunnerForSegments(query, specs)); + return new QuerySwappingQueryRunner<>( + decorateClusterRunner(freeTradeQuery, clusterClient.getQueryRunnerForSegments(freeTradeQuery, specs)), + query, + freeTradeQuery + ); } else { // We don't expect end-users to see this message, since it only happens when specific segments are requested; // this is not typical end-user behavior. @@ -235,6 +251,27 @@ private boolean canRunQueryUsingClusterWalker(Query query) || toolChest.canPerformSubquery(((QueryDataSource) analysis.getDataSource()).getQuery())); } + + private DataSource globalizeIfPossible( + final DataSource dataSource + ) + { + if (dataSource instanceof TableDataSource) { + GlobalTableDataSource maybeGlobal = new GlobalTableDataSource(((TableDataSource) dataSource).getName()); + if (joinableFactory.isDirectlyJoinable(maybeGlobal)) { + return maybeGlobal; + } + return dataSource; + } else { + List currentChildren = dataSource.getChildren(); + List newChildren = new ArrayList<>(currentChildren.size()); + for (DataSource child : currentChildren) { + newChildren.add(globalizeIfPossible(child)); + } + return dataSource.withChildren(newChildren); + } + } + /** * Replace QueryDataSources with InlineDataSources when necessary and possible. "Necessary" is defined as: * diff --git a/server/src/test/java/org/apache/druid/segment/join/InlineJoinableFactoryTest.java b/server/src/test/java/org/apache/druid/segment/join/InlineJoinableFactoryTest.java index d1be69840430..2a5bf3e5c7ce 100644 --- a/server/src/test/java/org/apache/druid/segment/join/InlineJoinableFactoryTest.java +++ b/server/src/test/java/org/apache/druid/segment/join/InlineJoinableFactoryTest.java @@ -80,6 +80,13 @@ public void testBuild() Assert.assertEquals(3, joinable.getCardinality("long")); } + @Test + public void testIsDirectlyJoinable() + { + Assert.assertTrue(factory.isDirectlyJoinable(inlineDataSource)); + Assert.assertFalse(factory.isDirectlyJoinable(new TableDataSource("foo"))); + } + private static JoinConditionAnalysis makeCondition(final String condition) { return JoinConditionAnalysis.forExpression(condition, PREFIX, ExprMacroTable.nil()); diff --git a/server/src/test/java/org/apache/druid/segment/join/LookupJoinableFactoryTest.java b/server/src/test/java/org/apache/druid/segment/join/LookupJoinableFactoryTest.java index 44ed4b2810af..6e0e737db9ed 100644 --- a/server/src/test/java/org/apache/druid/segment/join/LookupJoinableFactoryTest.java +++ b/server/src/test/java/org/apache/druid/segment/join/LookupJoinableFactoryTest.java @@ -125,6 +125,13 @@ public void testBuild() Assert.assertEquals(Joinable.CARDINALITY_UNKNOWN, joinable.getCardinality("v")); } + @Test + public void testIsDirectlyJoinable() + { + Assert.assertTrue(factory.isDirectlyJoinable(lookupDataSource)); + Assert.assertFalse(factory.isDirectlyJoinable(new TableDataSource("foo"))); + } + private static JoinConditionAnalysis makeCondition(final String condition) { return JoinConditionAnalysis.forExpression(condition, PREFIX, ExprMacroTable.nil()); diff --git a/server/src/test/java/org/apache/druid/server/ClientQuerySegmentWalkerTest.java b/server/src/test/java/org/apache/druid/server/ClientQuerySegmentWalkerTest.java index f224b5f580e4..565fee9fddd9 100644 --- a/server/src/test/java/org/apache/druid/server/ClientQuerySegmentWalkerTest.java +++ b/server/src/test/java/org/apache/druid/server/ClientQuerySegmentWalkerTest.java @@ -33,6 +33,7 @@ import org.apache.druid.query.BaseQuery; import org.apache.druid.query.DataSource; import org.apache.druid.query.Druids; +import org.apache.druid.query.GlobalTableDataSource; import org.apache.druid.query.InlineDataSource; import org.apache.druid.query.JoinDataSource; import org.apache.druid.query.Query; @@ -70,7 +71,9 @@ import org.apache.druid.segment.column.RowSignature; import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.join.InlineJoinableFactory; +import org.apache.druid.segment.join.JoinConditionAnalysis; import org.apache.druid.segment.join.JoinType; +import org.apache.druid.segment.join.Joinable; import org.apache.druid.segment.join.JoinableFactory; import org.apache.druid.segment.join.MapJoinableFactory; import org.apache.druid.server.initialization.ServerConfig; @@ -96,6 +99,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; /** * Tests ClientQuerySegmentWalker. @@ -112,6 +116,7 @@ public class ClientQuerySegmentWalkerTest private static final String FOO = "foo"; private static final String BAR = "bar"; private static final String MULTI = "multi"; + private static final String GLOBAL = "broadcast"; private static final Interval INTERVAL = Intervals.of("2000/P1Y"); private static final String VERSION = "A"; @@ -218,6 +223,40 @@ public void testTimeseriesOnTable() Assert.assertEquals(1, scheduler.getTotalReleased().get()); } + @Test + public void testTimeseriesOnAutomaticGlobalTable() + { + final TimeseriesQuery query = + Druids.newTimeseriesQueryBuilder() + .dataSource(GLOBAL) + .granularity(Granularities.ALL) + .intervals(Collections.singletonList(INTERVAL)) + .aggregators(new LongSumAggregatorFactory("sum", "n")) + .context(ImmutableMap.of(TimeseriesQuery.CTX_GRAND_TOTAL, false)) + .build(); + + // expect global/joinable datasource to be automatically translated into a GlobalTableDataSource + final TimeseriesQuery expectedClusterQuery = + Druids.newTimeseriesQueryBuilder() + .dataSource(new GlobalTableDataSource(GLOBAL)) + .granularity(Granularities.ALL) + .intervals(Collections.singletonList(INTERVAL)) + .aggregators(new LongSumAggregatorFactory("sum", "n")) + .context(ImmutableMap.of(TimeseriesQuery.CTX_GRAND_TOTAL, false)) + .build(); + + testQuery( + query, + ImmutableList.of(ExpectedQuery.cluster(expectedClusterQuery)), + ImmutableList.of(new Object[]{INTERVAL.getStartMillis(), 10L}) + ); + + Assert.assertEquals(1, scheduler.getTotalRun().get()); + Assert.assertEquals(1, scheduler.getTotalPrioritizedAndLaned().get()); + Assert.assertEquals(1, scheduler.getTotalAcquired().get()); + Assert.assertEquals(1, scheduler.getTotalReleased().get()); + } + @Test public void testTimeseriesOnInline() { @@ -606,6 +645,20 @@ private void initWalker(final Map serverProperties, QuerySchedul final JoinableFactory joinableFactory = new MapJoinableFactory( ImmutableMap., JoinableFactory>builder() .put(InlineDataSource.class, new InlineJoinableFactory()) + .put(GlobalTableDataSource.class, new JoinableFactory() + { + @Override + public boolean isDirectlyJoinable(DataSource dataSource) + { + return ((GlobalTableDataSource) dataSource).getName().equals(GLOBAL); + } + + @Override + public Optional build(DataSource dataSource, JoinConditionAnalysis condition) + { + return Optional.empty(); + } + }) .build() ); @@ -651,7 +704,8 @@ public QueryRunner getQueryRunnerForSegments(Query query, Iterable> QueryToolChest getToolChest return conglomerate.findFactory(query).getToolchest(); } }, + joinableFactory, new RetryQueryRunnerConfig(), TestHelper.makeJsonMapper(), serverConfig, diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidJoinQueryRel.java b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidJoinQueryRel.java index 1655d73e5445..6faf2198627a 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidJoinQueryRel.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/rel/DruidJoinQueryRel.java @@ -337,6 +337,8 @@ private static boolean computeLeftRequiresSubquery(final DruidRel left) private static boolean computeRightRequiresSubquery(final DruidRel right) { // Right requires a subquery unless it's a scan or mapping on top of a global datasource. + // ideally this would involve JoinableFactory.isDirectlyJoinable to check that the global datasources + // are in fact possibly joinable, but for now isGlobal is coupled to joinability return !(DruidRels.isScanOrMapping(right, false) && DruidRels.dataSourceIfLeafRel(right).filter(DataSource::isGlobal).isPresent()); } diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java b/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java index 6762aaf13c94..b264749308c6 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/schema/DruidSchema.java @@ -56,6 +56,7 @@ import org.apache.druid.query.spec.MultipleSpecificSegmentSpec; import org.apache.druid.segment.column.RowSignature; import org.apache.druid.segment.column.ValueType; +import org.apache.druid.segment.join.JoinableFactory; import org.apache.druid.server.QueryLifecycleFactory; import org.apache.druid.server.SegmentManager; import org.apache.druid.server.coordination.DruidServerMetadata; @@ -104,6 +105,7 @@ public class DruidSchema extends AbstractSchema private final PlannerConfig config; private final SegmentManager segmentManager; private final ViewManager viewManager; + private final JoinableFactory joinableFactory; private final ExecutorService cacheExec; private final ConcurrentMap tables; @@ -148,6 +150,7 @@ public DruidSchema( final QueryLifecycleFactory queryLifecycleFactory, final TimelineServerView serverView, final SegmentManager segmentManager, + final JoinableFactory joinableFactory, final PlannerConfig config, final ViewManager viewManager, final Escalator escalator @@ -156,6 +159,7 @@ public DruidSchema( this.queryLifecycleFactory = Preconditions.checkNotNull(queryLifecycleFactory, "queryLifecycleFactory"); Preconditions.checkNotNull(serverView, "serverView"); this.segmentManager = segmentManager; + this.joinableFactory = joinableFactory; this.config = Preconditions.checkNotNull(config, "config"); this.viewManager = Preconditions.checkNotNull(viewManager, "viewManager"); this.cacheExec = Execs.singleThreaded("DruidSchema-Cache-%d"); @@ -278,10 +282,11 @@ public void start() throws InterruptedException for (String dataSource : dataSourcesToRebuild) { final DruidTable druidTable = buildDruidTable(dataSource); final DruidTable oldTable = tables.put(dataSource, druidTable); + final String description = druidTable.getDataSource().isGlobal() ? "global dataSource" : "dataSource"; if (oldTable == null || !oldTable.getRowSignature().equals(druidTable.getRowSignature())) { - log.info("dataSource [%s] has new signature: %s.", dataSource, druidTable.getRowSignature()); + log.info("%s [%s] has new signature: %s.", description, dataSource, druidTable.getRowSignature()); } else { - log.debug("dataSource [%s] signature is unchanged.", dataSource); + log.debug("%s [%s] signature is unchanged.", description, dataSource); } } @@ -627,12 +632,21 @@ protected DruidTable buildDruidTable(final String dataSource) columnTypes.forEach(builder::add); final TableDataSource tableDataSource; - if (segmentManager.getDataSourceNames().contains(dataSource)) { - tableDataSource = new GlobalTableDataSource(dataSource); + + // to be a GlobalTableDataSource instead of a TableDataSource, it must appear on all servers (inferred by existing + // in the segment cache, which in this case belongs to the broker meaning only broadcast segments live here) + // to be joinable, it must be possibly joinable according to the factory. we only consider broadcast datasources + // at this time, and isGlobal is currently strongly coupled with joinable, so only make a global table datasource + // if also joinable + final GlobalTableDataSource maybeGlobal = new GlobalTableDataSource(dataSource); + final boolean isJoinable = joinableFactory.isDirectlyJoinable(maybeGlobal); + final boolean isBroadcast = segmentManager.getDataSourceNames().contains(dataSource); + if (isBroadcast && isJoinable) { + tableDataSource = maybeGlobal; } else { tableDataSource = new TableDataSource(dataSource); } - return new DruidTable(tableDataSource, builder.build()); + return new DruidTable(tableDataSource, builder.build(), isJoinable, isBroadcast); } } diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/schema/InformationSchema.java b/sql/src/main/java/org/apache/druid/sql/calcite/schema/InformationSchema.java index bf84ea131c8c..8ee93a21e631 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/schema/InformationSchema.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/schema/InformationSchema.java @@ -53,6 +53,7 @@ import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ResourceAction; import org.apache.druid.sql.calcite.planner.PlannerContext; +import org.apache.druid.sql.calcite.table.DruidTable; import org.apache.druid.sql.calcite.table.RowSignatures; import javax.annotation.Nullable; @@ -83,6 +84,8 @@ public class InformationSchema extends AbstractSchema .add("TABLE_SCHEMA", ValueType.STRING) .add("TABLE_NAME", ValueType.STRING) .add("TABLE_TYPE", ValueType.STRING) + .add("IS_JOINABLE", ValueType.STRING) + .add("IS_BROADCAST", ValueType.STRING) .build(); private static final RowSignature COLUMNS_SIGNATURE = RowSignature .builder() @@ -109,6 +112,9 @@ public class InformationSchema extends AbstractSchema return Collections.singletonList(AuthorizationUtils.DATASOURCE_READ_RA_GENERATOR.apply(datasourceName)); }; + private static final String INFO_TRUE = "YES"; + private static final String INFO_FALSE = "NO"; + private final SchemaPlus rootSchema; private final Map tableMap; private final AuthorizerMapper authorizerMapper; @@ -217,18 +223,27 @@ public Iterable apply(final String schemaName) return Iterables.filter( Iterables.concat( FluentIterable.from(authorizedTableNames).transform( - new Function() - { - @Override - public Object[] apply(final String tableName) - { - return new Object[]{ - CATALOG_NAME, // TABLE_CATALOG - schemaName, // TABLE_SCHEMA - tableName, // TABLE_NAME - subSchema.getTable(tableName).getJdbcTableType().toString() // TABLE_TYPE - }; + tableName -> { + final Table table = subSchema.getTable(tableName); + final boolean isJoinable; + final boolean isBroadcast; + if (table instanceof DruidTable) { + DruidTable druidTable = (DruidTable) table; + isJoinable = druidTable.isJoinable(); + isBroadcast = druidTable.isBroadcast(); + } else { + isJoinable = false; + isBroadcast = false; } + + return new Object[]{ + CATALOG_NAME, // TABLE_CATALOG + schemaName, // TABLE_SCHEMA + tableName, // TABLE_NAME + table.getJdbcTableType().toString(), // TABLE_TYPE + isJoinable ? INFO_TRUE : INFO_FALSE, // IS_JOINABLE + isBroadcast ? INFO_TRUE : INFO_FALSE // IS_BROADCAST + }; } ), FluentIterable.from(authorizedFunctionNames).transform( @@ -242,7 +257,9 @@ public Object[] apply(final String functionName) CATALOG_NAME, // TABLE_CATALOG schemaName, // TABLE_SCHEMA functionName, // TABLE_NAME - "VIEW" // TABLE_TYPE + "VIEW", // TABLE_TYPE + INFO_FALSE, // IS_JOINABLE + INFO_FALSE // IS_BROADCAST }; } else { return null; @@ -406,7 +423,7 @@ public Object[] apply(final RelDataTypeField field) field.getName(), // COLUMN_NAME String.valueOf(field.getIndex()), // ORDINAL_POSITION "", // COLUMN_DEFAULT - type.isNullable() ? "YES" : "NO", // IS_NULLABLE + type.isNullable() ? INFO_TRUE : INFO_FALSE, // IS_NULLABLE type.getSqlTypeName().toString(), // DATA_TYPE null, // CHARACTER_MAXIMUM_LENGTH null, // CHARACTER_OCTET_LENGTH diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/schema/LookupSchema.java b/sql/src/main/java/org/apache/druid/sql/calcite/schema/LookupSchema.java index b3f331471791..6ddeaabb445d 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/schema/LookupSchema.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/schema/LookupSchema.java @@ -57,7 +57,9 @@ protected Map getTableMap() final ImmutableMap.Builder tableMapBuilder = ImmutableMap.builder(); for (final String lookupName : lookupProvider.getAllLookupNames()) { - tableMapBuilder.put(lookupName, new DruidTable(new LookupDataSource(lookupName), ROW_SIGNATURE)); + // all lookups should be also joinable through lookup joinable factory, and lookups are effectively broadcast + // (if we ignore lookup tiers...) + tableMapBuilder.put(lookupName, new DruidTable(new LookupDataSource(lookupName), ROW_SIGNATURE, true, true)); } return tableMapBuilder.build(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/table/DruidTable.java b/sql/src/main/java/org/apache/druid/sql/calcite/table/DruidTable.java index 521a051b7c98..94da5ede16e5 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/table/DruidTable.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/table/DruidTable.java @@ -41,14 +41,20 @@ public class DruidTable implements TranslatableTable { private final DataSource dataSource; private final RowSignature rowSignature; + private final boolean joinable; + private final boolean broadcast; public DruidTable( final DataSource dataSource, - final RowSignature rowSignature + final RowSignature rowSignature, + final boolean isJoinable, + final boolean isBroadcast ) { this.dataSource = Preconditions.checkNotNull(dataSource, "dataSource"); this.rowSignature = Preconditions.checkNotNull(rowSignature, "rowSignature"); + this.joinable = isJoinable; + this.broadcast = isBroadcast; } public DataSource getDataSource() @@ -61,6 +67,16 @@ public RowSignature getRowSignature() return rowSignature; } + public boolean isJoinable() + { + return joinable; + } + + public boolean isBroadcast() + { + return broadcast; + } + @Override public Schema.TableType getJdbcTableType() { diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index 1ad92cb538f5..62c1519473fe 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -708,59 +708,59 @@ public void testInformationSchemaSchemata() throws Exception public void testInformationSchemaTables() throws Exception { testQuery( - "SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE\n" + "SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, IS_JOINABLE, IS_BROADCAST\n" + "FROM INFORMATION_SCHEMA.TABLES\n" + "WHERE TABLE_TYPE IN ('SYSTEM_TABLE', 'TABLE', 'VIEW')", ImmutableList.of(), ImmutableList.builder() - .add(new Object[]{"druid", CalciteTests.DATASOURCE1, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.DATASOURCE2, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.DATASOURCE4, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.DATASOURCE5, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.DATASOURCE3, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.SOME_DATASOURCE, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.SOMEXDATASOURCE, "TABLE"}) - .add(new Object[]{"druid", "aview", "VIEW"}) - .add(new Object[]{"druid", "bview", "VIEW"}) - .add(new Object[]{"INFORMATION_SCHEMA", "COLUMNS", "SYSTEM_TABLE"}) - .add(new Object[]{"INFORMATION_SCHEMA", "SCHEMATA", "SYSTEM_TABLE"}) - .add(new Object[]{"INFORMATION_SCHEMA", "TABLES", "SYSTEM_TABLE"}) - .add(new Object[]{"lookup", "lookyloo", "TABLE"}) - .add(new Object[]{"sys", "segments", "SYSTEM_TABLE"}) - .add(new Object[]{"sys", "server_segments", "SYSTEM_TABLE"}) - .add(new Object[]{"sys", "servers", "SYSTEM_TABLE"}) - .add(new Object[]{"sys", "supervisors", "SYSTEM_TABLE"}) - .add(new Object[]{"sys", "tasks", "SYSTEM_TABLE"}) + .add(new Object[]{"druid", CalciteTests.DATASOURCE1, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.DATASOURCE2, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.DATASOURCE4, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.DATASOURCE5, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.DATASOURCE3, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.SOME_DATASOURCE, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.SOMEXDATASOURCE, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", "aview", "VIEW", "NO", "NO"}) + .add(new Object[]{"druid", "bview", "VIEW", "NO", "NO"}) + .add(new Object[]{"INFORMATION_SCHEMA", "COLUMNS", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"INFORMATION_SCHEMA", "SCHEMATA", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"INFORMATION_SCHEMA", "TABLES", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"lookup", "lookyloo", "TABLE", "YES", "YES"}) + .add(new Object[]{"sys", "segments", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"sys", "server_segments", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"sys", "servers", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"sys", "supervisors", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"sys", "tasks", "SYSTEM_TABLE", "NO", "NO"}) .build() ); testQuery( PLANNER_CONFIG_DEFAULT, - "SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE\n" + "SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, IS_JOINABLE, IS_BROADCAST\n" + "FROM INFORMATION_SCHEMA.TABLES\n" + "WHERE TABLE_TYPE IN ('SYSTEM_TABLE', 'TABLE', 'VIEW')", CalciteTests.SUPER_USER_AUTH_RESULT, ImmutableList.of(), ImmutableList.builder() - .add(new Object[]{"druid", CalciteTests.DATASOURCE1, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.DATASOURCE2, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.DATASOURCE4, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.FORBIDDEN_DATASOURCE, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.DATASOURCE5, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.DATASOURCE3, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.SOME_DATASOURCE, "TABLE"}) - .add(new Object[]{"druid", CalciteTests.SOMEXDATASOURCE, "TABLE"}) - .add(new Object[]{"druid", "aview", "VIEW"}) - .add(new Object[]{"druid", "bview", "VIEW"}) - .add(new Object[]{"INFORMATION_SCHEMA", "COLUMNS", "SYSTEM_TABLE"}) - .add(new Object[]{"INFORMATION_SCHEMA", "SCHEMATA", "SYSTEM_TABLE"}) - .add(new Object[]{"INFORMATION_SCHEMA", "TABLES", "SYSTEM_TABLE"}) - .add(new Object[]{"lookup", "lookyloo", "TABLE"}) - .add(new Object[]{"sys", "segments", "SYSTEM_TABLE"}) - .add(new Object[]{"sys", "server_segments", "SYSTEM_TABLE"}) - .add(new Object[]{"sys", "servers", "SYSTEM_TABLE"}) - .add(new Object[]{"sys", "supervisors", "SYSTEM_TABLE"}) - .add(new Object[]{"sys", "tasks", "SYSTEM_TABLE"}) + .add(new Object[]{"druid", CalciteTests.DATASOURCE1, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.DATASOURCE2, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.DATASOURCE4, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.FORBIDDEN_DATASOURCE, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.DATASOURCE5, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.DATASOURCE3, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.SOME_DATASOURCE, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", CalciteTests.SOMEXDATASOURCE, "TABLE", "NO", "NO"}) + .add(new Object[]{"druid", "aview", "VIEW", "NO", "NO"}) + .add(new Object[]{"druid", "bview", "VIEW", "NO", "NO"}) + .add(new Object[]{"INFORMATION_SCHEMA", "COLUMNS", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"INFORMATION_SCHEMA", "SCHEMATA", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"INFORMATION_SCHEMA", "TABLES", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"lookup", "lookyloo", "TABLE", "YES", "YES"}) + .add(new Object[]{"sys", "segments", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"sys", "server_segments", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"sys", "servers", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"sys", "supervisors", "SYSTEM_TABLE", "NO", "NO"}) + .add(new Object[]{"sys", "tasks", "SYSTEM_TABLE", "NO", "NO"}) .build() ); } diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidCalciteSchemaModuleTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidCalciteSchemaModuleTest.java index 11c2b97f4244..a5e883176039 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidCalciteSchemaModuleTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidCalciteSchemaModuleTest.java @@ -20,6 +20,7 @@ package org.apache.druid.sql.calcite.schema; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.inject.Guice; import com.google.inject.Injector; @@ -38,6 +39,8 @@ import org.apache.druid.guice.LifecycleModule; import org.apache.druid.query.lookup.LookupExtractorFactoryContainerProvider; import org.apache.druid.query.lookup.LookupReferencesManager; +import org.apache.druid.segment.join.JoinableFactory; +import org.apache.druid.segment.join.MapJoinableFactory; import org.apache.druid.server.QueryLifecycleFactory; import org.apache.druid.server.SegmentManager; import org.apache.druid.server.security.AuthorizerMapper; @@ -102,6 +105,7 @@ public void setUp() binder -> { binder.bind(QueryLifecycleFactory.class).toInstance(queryLifecycleFactory); binder.bind(TimelineServerView.class).toInstance(serverView); + binder.bind(JoinableFactory.class).toInstance(new MapJoinableFactory(ImmutableMap.of())); binder.bind(PlannerConfig.class).toInstance(plannerConfig); binder.bind(ViewManager.class).toInstance(viewManager); binder.bind(Escalator.class).toInstance(escalator); diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaNoDataInitTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaNoDataInitTest.java index fd20fb594e85..0ba26ea9d3ea 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaNoDataInitTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaNoDataInitTest.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableMap; import org.apache.druid.java.util.common.io.Closer; import org.apache.druid.query.QueryRunnerFactoryConglomerate; +import org.apache.druid.segment.join.MapJoinableFactory; import org.apache.druid.segment.loading.SegmentLoader; import org.apache.druid.server.QueryStackTests; import org.apache.druid.server.SegmentManager; @@ -54,6 +55,7 @@ public void testInitializationWithNoData() throws Exception ), new TestServerInventoryView(Collections.emptyList()), new SegmentManager(EasyMock.createMock(SegmentLoader.class)), + new MapJoinableFactory(ImmutableMap.of()), PLANNER_CONFIG_DEFAULT, new NoopViewManager(), new NoopEscalator() diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java index 8455965ef5e9..adb3626c54af 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/DruidSchemaTest.java @@ -33,6 +33,7 @@ import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.Pair; import org.apache.druid.java.util.common.io.Closer; +import org.apache.druid.query.DataSource; import org.apache.druid.query.GlobalTableDataSource; import org.apache.druid.query.QueryRunnerFactoryConglomerate; import org.apache.druid.query.TableDataSource; @@ -43,6 +44,10 @@ import org.apache.druid.segment.IndexBuilder; import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.incremental.IncrementalIndexSchema; +import org.apache.druid.segment.join.JoinConditionAnalysis; +import org.apache.druid.segment.join.Joinable; +import org.apache.druid.segment.join.JoinableFactory; +import org.apache.druid.segment.join.MapJoinableFactory; import org.apache.druid.segment.loading.SegmentLoader; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; import org.apache.druid.server.QueryStackTests; @@ -77,6 +82,7 @@ import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -132,12 +138,15 @@ public static void tearDownClass() throws IOException private SpecificSegmentsQuerySegmentWalker walker = null; private DruidSchema schema = null; private SegmentManager segmentManager; - private Set dataSourceNames; + private Set segmentDataSourceNames; + private Set joinableDataSourceNames; @Before public void setUp() throws Exception { - dataSourceNames = Sets.newConcurrentHashSet(); + segmentDataSourceNames = Sets.newConcurrentHashSet(); + joinableDataSourceNames = Sets.newConcurrentHashSet(); + final File tmpDir = temporaryFolder.newFolder(); final QueryableIndex index1 = IndexBuilder.create() .tmpDir(new File(tmpDir, "1")) @@ -173,7 +182,7 @@ public void setUp() throws Exception public Set getDataSourceNames() { getDatasourcesLatch.countDown(); - return dataSourceNames; + return segmentDataSourceNames; } }; @@ -222,10 +231,30 @@ public Set getDataSourceNames() serverView = new TestServerInventoryView(walker.getSegments(), realtimeSegments); druidServers = serverView.getDruidServers(); + final JoinableFactory globalTableJoinable = new JoinableFactory() + { + @Override + public boolean isDirectlyJoinable(DataSource dataSource) + { + return dataSource instanceof GlobalTableDataSource && + joinableDataSourceNames.contains(((GlobalTableDataSource) dataSource).getName()); + } + + @Override + public Optional build( + DataSource dataSource, + JoinConditionAnalysis condition + ) + { + return Optional.empty(); + } + }; + schema = new DruidSchema( CalciteTests.createMockQueryLifecycleFactory(walker, conglomerate), serverView, segmentManager, + new MapJoinableFactory(ImmutableMap.of(GlobalTableDataSource.class, globalTableJoinable)), PLANNER_CONFIG_DEFAULT, new NoopViewManager(), new NoopEscalator() @@ -461,12 +490,16 @@ public void testAvailableSegmentMetadataIsRealtime() } @Test - public void testLocalSegmentCacheSetsDataSourceAsGlobal() throws InterruptedException + public void testLocalSegmentCacheSetsDataSourceAsGlobalAndJoinable() throws InterruptedException { DruidTable fooTable = (DruidTable) schema.getTableMap().get("foo"); Assert.assertNotNull(fooTable); Assert.assertTrue(fooTable.getDataSource() instanceof TableDataSource); Assert.assertFalse(fooTable.getDataSource() instanceof GlobalTableDataSource); + Assert.assertFalse(fooTable.isJoinable()); + Assert.assertFalse(fooTable.isBroadcast()); + + buildTableLatch.await(1, TimeUnit.SECONDS); final DataSegment someNewBrokerSegment = new DataSegment( "foo", @@ -481,9 +514,30 @@ public void testLocalSegmentCacheSetsDataSourceAsGlobal() throws InterruptedExce 100L, PruneSpecsHolder.DEFAULT ); - dataSourceNames.add("foo"); + segmentDataSourceNames.add("foo"); + joinableDataSourceNames.add("foo"); serverView.addSegment(someNewBrokerSegment, ServerType.BROKER); + // wait for build twice + buildTableLatch = new CountDownLatch(2); + buildTableLatch.await(1, TimeUnit.SECONDS); + + // wait for get again, just to make sure table has been updated (latch counts down just before tables are updated) + getDatasourcesLatch = new CountDownLatch(1); + getDatasourcesLatch.await(1, TimeUnit.SECONDS); + + fooTable = (DruidTable) schema.getTableMap().get("foo"); + Assert.assertNotNull(fooTable); + Assert.assertTrue(fooTable.getDataSource() instanceof TableDataSource); + Assert.assertTrue(fooTable.getDataSource() instanceof GlobalTableDataSource); + Assert.assertTrue(fooTable.isJoinable()); + Assert.assertTrue(fooTable.isBroadcast()); + + // now remove it + joinableDataSourceNames.remove("foo"); + segmentDataSourceNames.remove("foo"); + serverView.removeSegment(someNewBrokerSegment, ServerType.BROKER); + // wait for build buildTableLatch.await(1, TimeUnit.SECONDS); buildTableLatch = new CountDownLatch(1); @@ -496,10 +550,59 @@ public void testLocalSegmentCacheSetsDataSourceAsGlobal() throws InterruptedExce fooTable = (DruidTable) schema.getTableMap().get("foo"); Assert.assertNotNull(fooTable); Assert.assertTrue(fooTable.getDataSource() instanceof TableDataSource); - Assert.assertTrue(fooTable.getDataSource() instanceof GlobalTableDataSource); + Assert.assertFalse(fooTable.getDataSource() instanceof GlobalTableDataSource); + Assert.assertFalse(fooTable.isJoinable()); + Assert.assertFalse(fooTable.isBroadcast()); + } + + @Test + public void testLocalSegmentCacheSetsDataSourceAsBroadcastButNotJoinable() throws InterruptedException + { + DruidTable fooTable = (DruidTable) schema.getTableMap().get("foo"); + Assert.assertNotNull(fooTable); + Assert.assertTrue(fooTable.getDataSource() instanceof TableDataSource); + Assert.assertFalse(fooTable.getDataSource() instanceof GlobalTableDataSource); + Assert.assertFalse(fooTable.isJoinable()); + Assert.assertFalse(fooTable.isBroadcast()); + + // wait for build twice + buildTableLatch.await(1, TimeUnit.SECONDS); + + final DataSegment someNewBrokerSegment = new DataSegment( + "foo", + Intervals.of("2012/2013"), + "version1", + null, + ImmutableList.of("dim1", "dim2"), + ImmutableList.of("met1", "met2"), + new NumberedShardSpec(2, 3), + null, + 1, + 100L, + PruneSpecsHolder.DEFAULT + ); + segmentDataSourceNames.add("foo"); + serverView.addSegment(someNewBrokerSegment, ServerType.BROKER); + + buildTableLatch = new CountDownLatch(2); + buildTableLatch.await(1, TimeUnit.SECONDS); + + // wait for get again, just to make sure table has been updated (latch counts down just before tables are updated) + getDatasourcesLatch = new CountDownLatch(1); + getDatasourcesLatch.await(1, TimeUnit.SECONDS); + + fooTable = (DruidTable) schema.getTableMap().get("foo"); + Assert.assertNotNull(fooTable); + Assert.assertTrue(fooTable.getDataSource() instanceof TableDataSource); + // should not be a GlobalTableDataSource for now, because isGlobal is couple with joinability. idealy this will be + // changed in the future and we should expect + Assert.assertFalse(fooTable.getDataSource() instanceof GlobalTableDataSource); + Assert.assertTrue(fooTable.isBroadcast()); + Assert.assertFalse(fooTable.isJoinable()); + // now remove it - dataSourceNames.remove("foo"); + segmentDataSourceNames.remove("foo"); serverView.removeSegment(someNewBrokerSegment, ServerType.BROKER); // wait for build @@ -515,6 +618,7 @@ public void testLocalSegmentCacheSetsDataSourceAsGlobal() throws InterruptedExce Assert.assertNotNull(fooTable); Assert.assertTrue(fooTable.getDataSource() instanceof TableDataSource); Assert.assertFalse(fooTable.getDataSource() instanceof GlobalTableDataSource); + Assert.assertFalse(fooTable.isBroadcast()); + Assert.assertFalse(fooTable.isJoinable()); } - } diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java index 3e1356af7b98..ace7acaa17f8 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java @@ -67,6 +67,7 @@ import org.apache.druid.segment.column.RowSignature; import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.incremental.IncrementalIndexSchema; +import org.apache.druid.segment.join.MapJoinableFactory; import org.apache.druid.segment.loading.SegmentLoader; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; import org.apache.druid.server.DruidNode; @@ -242,6 +243,7 @@ public Authorizer getAuthorizer(String name) CalciteTests.createMockQueryLifecycleFactory(walker, conglomerate), new TestServerInventoryView(walker.getSegments(), realtimeSegments), new SegmentManager(EasyMock.createMock(SegmentLoader.class)), + new MapJoinableFactory(ImmutableMap.of()), PLANNER_CONFIG_DEFAULT, new NoopViewManager(), new NoopEscalator() diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java index b7d56f5632d2..ca683a243aea 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java @@ -73,6 +73,7 @@ import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.TestHelper; import org.apache.druid.segment.incremental.IncrementalIndexSchema; +import org.apache.druid.segment.join.MapJoinableFactory; import org.apache.druid.segment.loading.SegmentLoader; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; import org.apache.druid.server.DruidNode; @@ -979,6 +980,7 @@ private static DruidSchema createMockSchema( CalciteTests.createMockQueryLifecycleFactory(walker, conglomerate), new TestServerInventoryView(walker.getSegments()), new SegmentManager(EasyMock.createMock(SegmentLoader.class)), + new MapJoinableFactory(ImmutableMap.of()), plannerConfig, viewManager, TEST_AUTHENTICATOR_ESCALATOR diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/util/SpecificSegmentsQuerySegmentWalker.java b/sql/src/test/java/org/apache/druid/sql/calcite/util/SpecificSegmentsQuerySegmentWalker.java index 1da0fbabf9c9..0490420cdd73 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/util/SpecificSegmentsQuerySegmentWalker.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/util/SpecificSegmentsQuerySegmentWalker.java @@ -120,6 +120,7 @@ public SpecificSegmentsQuerySegmentWalker( scheduler ), conglomerate, + joinableFactoryToUse, new ServerConfig() ); } From 8ccdf71e0f95c520fc57e96faf31e9cda3fd1f29 Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn Date: Thu, 18 Jun 2020 14:52:33 -1000 Subject: [PATCH 094/107] Coordinator loadstatus API full format does not consider Broadcast rules (#10048) * Coordinator loadstatus API full format does not consider Broadcast rules * address comments * fix checkstyle * minor optimization * address comments --- .../server/coordinator/DruidCoordinator.java | 51 +++- .../coordinator/DruidCoordinatorTest.java | 244 ++++++++++++++---- 2 files changed, 236 insertions(+), 59 deletions(-) diff --git a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java index c4de3644c64c..0dcf636b7442 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java @@ -69,6 +69,7 @@ import org.apache.druid.server.coordinator.duty.MarkAsUnusedOvershadowedSegments; import org.apache.druid.server.coordinator.duty.RunRules; import org.apache.druid.server.coordinator.duty.UnloadUnusedSegments; +import org.apache.druid.server.coordinator.rules.BroadcastDistributionRule; import org.apache.druid.server.coordinator.rules.LoadRule; import org.apache.druid.server.coordinator.rules.Rule; import org.apache.druid.server.initialization.ZkPathsConfig; @@ -262,6 +263,11 @@ public Map> computeUnderReplicationCountsPerDataS } /** + * segmentReplicantLookup use in this method could potentially be stale since it is only updated on coordinator runs. + * However, this is ok as long as the {@param dataSegments} is refreshed/latest as this would at least still ensure + * that the stale data in segmentReplicantLookup would be under counting replication levels, + * rather than potentially falsely reporting that everything is available. + * * @return tier -> { dataSource -> underReplicationCount } map */ public Map> computeUnderReplicationCountsPerDataSourcePerTierForSegments( @@ -269,6 +275,13 @@ public Map> computeUnderReplicationCountsPerDataS ) { final Map> underReplicationCountsPerDataSourcePerTier = new HashMap<>(); + final Set decommissioningServers = getDynamicConfigs().getDecommissioningNodes(); + final List broadcastTargetServers = serverInventoryView + .getInventory() + .stream() + .filter(druidServer -> druidServer.isSegmentBroadcastTarget() && !decommissioningServers.contains(druidServer.getHost())) + .map(DruidServer::toImmutableDruidServer) + .collect(Collectors.toList()); if (segmentReplicantLookup == null) { return underReplicationCountsPerDataSourcePerTier; @@ -280,20 +293,38 @@ public Map> computeUnderReplicationCountsPerDataS final List rules = metadataRuleManager.getRulesWithDefault(segment.getDataSource()); for (final Rule rule : rules) { - if (!(rule instanceof LoadRule && rule.appliesTo(segment, now))) { + if (!rule.appliesTo(segment, now)) { continue; } - ((LoadRule) rule) - .getTieredReplicants() - .forEach((final String tier, final Integer ruleReplicants) -> { - int currentReplicants = segmentReplicantLookup.getLoadedReplicants(segment.getId(), tier); - Object2LongMap underReplicationPerDataSource = underReplicationCountsPerDataSourcePerTier - .computeIfAbsent(tier, ignored -> new Object2LongOpenHashMap<>()); + if (rule instanceof LoadRule) { + ((LoadRule) rule) + .getTieredReplicants() + .forEach((final String tier, final Integer ruleReplicants) -> { + int currentReplicants = segmentReplicantLookup.getLoadedReplicants(segment.getId(), tier); + Object2LongMap underReplicationPerDataSource = underReplicationCountsPerDataSourcePerTier + .computeIfAbsent(tier, ignored -> new Object2LongOpenHashMap<>()); + ((Object2LongOpenHashMap) underReplicationPerDataSource) + .addTo(segment.getDataSource(), Math.max(ruleReplicants - currentReplicants, 0)); + }); + } + + if (rule instanceof BroadcastDistributionRule) { + for (ImmutableDruidServer server : broadcastTargetServers) { + Object2LongMap underReplicationPerDataSource = underReplicationCountsPerDataSourcePerTier + .computeIfAbsent(server.getTier(), ignored -> new Object2LongOpenHashMap<>()); + if (server.getSegment(segment.getId()) == null) { ((Object2LongOpenHashMap) underReplicationPerDataSource) - .addTo(segment.getDataSource(), Math.max(ruleReplicants - currentReplicants, 0)); - }); - break; // only the first matching rule applies + .addTo(segment.getDataSource(), 1); + } else { + // This make sure that every datasource has a entry even if the all segments are loaded + underReplicationPerDataSource.putIfAbsent(segment.getDataSource(), 0); + } + } + } + + // only the first matching rule applies + break; } } diff --git a/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorTest.java b/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorTest.java index e48bf9430211..0077fc1123a4 100644 --- a/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorTest.java +++ b/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorTest.java @@ -28,8 +28,6 @@ import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; -import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; -import org.apache.curator.utils.ZKPaths; import org.apache.druid.client.DataSourcesSnapshot; import org.apache.druid.client.DruidDataSource; import org.apache.druid.client.DruidServer; @@ -52,6 +50,7 @@ import org.apache.druid.server.DruidNode; import org.apache.druid.server.coordination.DruidServerMetadata; import org.apache.druid.server.coordination.ServerType; +import org.apache.druid.server.coordinator.rules.ForeverBroadcastDistributionRule; import org.apache.druid.server.coordinator.rules.ForeverLoadRule; import org.apache.druid.server.coordinator.rules.IntervalLoadRule; import org.apache.druid.server.coordinator.rules.Rule; @@ -104,10 +103,12 @@ public class DruidCoordinatorTest extends CuratorTestBase private ObjectMapper objectMapper; private DruidNode druidNode; private LatchableServiceEmitter serviceEmitter = new LatchableServiceEmitter(); + private boolean serverAddedCountExpected = true; @Before public void setUp() throws Exception { + serverAddedCountExpected = true; druidServer = EasyMock.createMock(DruidServer.class); serverInventoryView = EasyMock.createMock(SingleServerInventoryView.class); segmentsMetadataManager = EasyMock.createNiceMock(SegmentsMetadataManager.class); @@ -375,37 +376,22 @@ public void testCoordinatorRun() throws Exception // This coordinator should be leader by now Assert.assertTrue(coordinator.isLeader()); Assert.assertEquals(druidNode.getHostAndPort(), coordinator.getCurrentLeader()); - - final CountDownLatch assignSegmentLatch = new CountDownLatch(1); - pathChildrenCache.getListenable().addListener( - new PathChildrenCacheListener() - { - @Override - public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) - { - if (CuratorUtils.isChildAdded(event)) { - if (assignSegmentLatch.getCount() > 0) { - //Coordinator should try to assign segment to druidServer historical - //Simulate historical loading segment - druidServer.addDataSegment(dataSegment); - assignSegmentLatch.countDown(); - } else { - Assert.fail("The same segment is assigned to the same server multiple times"); - } - } - } - } - ); pathChildrenCache.start(); + final CountDownLatch assignSegmentLatch = createCountDownLatchAndSetPathChildrenCacheListenerWithLatch( + 1, + pathChildrenCache, + ImmutableMap.of("2010-01-01T00:00:00.000Z_2010-01-02T00:00:00.000Z", dataSegment), + druidServer + ); assignSegmentLatch.await(); + Assert.assertTrue(serverAddedCountExpected); final CountDownLatch coordinatorRunLatch = new CountDownLatch(2); serviceEmitter.latch = coordinatorRunLatch; coordinatorRunLatch.await(); Assert.assertEquals(ImmutableMap.of(dataSource, 100.0), coordinator.getLoadStatus()); - curator.delete().guaranteed().forPath(ZKPaths.makePath(LOADPATH, dataSegment.getId().toString())); Object2IntMap numsUnavailableUsedSegmentsPerDataSource = coordinator.computeNumsUnavailableUsedSegmentsPerDataSource(); @@ -496,39 +482,171 @@ public void testCoordinatorTieredRun() throws Exception coordinator.start(); leaderAnnouncerLatch.await(); // Wait for this coordinator to become leader - final CountDownLatch assignSegmentLatchHot = new CountDownLatch(2); - pathChildrenCache.getListenable().addListener( - (client, event) -> { - if (CuratorUtils.isChildAdded(event)) { - DataSegment segment = findSegmentRelatedToCuratorEvent(dataSegments, event); - if (segment != null) { - hotServer.addDataSegment(segment); - curator.delete().guaranteed().forPath(event.getData().getPath()); - } + final CountDownLatch assignSegmentLatchHot = createCountDownLatchAndSetPathChildrenCacheListenerWithLatch(2, pathChildrenCache, dataSegments, hotServer); + final CountDownLatch assignSegmentLatchCold = createCountDownLatchAndSetPathChildrenCacheListenerWithLatch(1, pathChildrenCacheCold, dataSegments, coldServer); + assignSegmentLatchHot.await(); + assignSegmentLatchCold.await(); + Assert.assertTrue(serverAddedCountExpected); - assignSegmentLatchHot.countDown(); - } - } + final CountDownLatch coordinatorRunLatch = new CountDownLatch(2); + serviceEmitter.latch = coordinatorRunLatch; + coordinatorRunLatch.await(); + + Assert.assertEquals(ImmutableMap.of(dataSource, 100.0), coordinator.getLoadStatus()); + + Map> underReplicationCountsPerDataSourcePerTier = + coordinator.computeUnderReplicationCountsPerDataSourcePerTier(); + Assert.assertEquals(2, underReplicationCountsPerDataSourcePerTier.size()); + Assert.assertEquals(0L, underReplicationCountsPerDataSourcePerTier.get(hotTierName).getLong(dataSource)); + Assert.assertEquals(0L, underReplicationCountsPerDataSourcePerTier.get(coldTierName).getLong(dataSource)); + + coordinator.stop(); + leaderUnannouncerLatch.await(); + + EasyMock.verify(serverInventoryView); + EasyMock.verify(segmentsMetadataManager); + EasyMock.verify(metadataRuleManager); + } + + @Test(timeout = 60_000L) + public void testComputeUnderReplicationCountsPerDataSourcePerTierForSegmentsWithBroadcastRule() throws Exception + { + final String dataSource = "dataSource"; + final String hotTierName = "hot"; + final String coldTierName = "cold"; + final String tierName1 = "tier1"; + final String tierName2 = "tier2"; + final Rule broadcastDistributionRule = new ForeverBroadcastDistributionRule(); + final String loadPathCold = "/druid/loadqueue/cold:1234"; + final String loadPathBroker1 = "/druid/loadqueue/broker1:1234"; + final String loadPathBroker2 = "/druid/loadqueue/broker2:1234"; + final String loadPathPeon = "/druid/loadqueue/peon:1234"; + final DruidServer hotServer = new DruidServer("hot", "hot", null, 5L, ServerType.HISTORICAL, hotTierName, 0); + final DruidServer coldServer = new DruidServer("cold", "cold", null, 5L, ServerType.HISTORICAL, coldTierName, 0); + final DruidServer brokerServer1 = new DruidServer("broker1", "broker1", null, 5L, ServerType.BROKER, tierName1, 0); + final DruidServer brokerServer2 = new DruidServer("broker2", "broker2", null, 5L, ServerType.BROKER, tierName2, 0); + final DruidServer peonServer = new DruidServer("peon", "peon", null, 5L, ServerType.INDEXER_EXECUTOR, tierName2, 0); + + final Map dataSegments = ImmutableMap.of( + "2018-01-02T00:00:00.000Z_2018-01-03T00:00:00.000Z", + new DataSegment(dataSource, Intervals.of("2018-01-02/P1D"), "v1", null, null, null, null, 0x9, 0), + "2018-01-03T00:00:00.000Z_2018-01-04T00:00:00.000Z", + new DataSegment(dataSource, Intervals.of("2018-01-03/P1D"), "v1", null, null, null, null, 0x9, 0), + "2017-01-01T00:00:00.000Z_2017-01-02T00:00:00.000Z", + new DataSegment(dataSource, Intervals.of("2017-01-01/P1D"), "v1", null, null, null, null, 0x9, 0) ); - final CountDownLatch assignSegmentLatchCold = new CountDownLatch(1); - pathChildrenCacheCold.getListenable().addListener( - (CuratorFramework client, PathChildrenCacheEvent event) -> { - if (CuratorUtils.isChildAdded(event)) { - DataSegment segment = findSegmentRelatedToCuratorEvent(dataSegments, event); + final LoadQueuePeon loadQueuePeonCold = new CuratorLoadQueuePeon( + curator, + loadPathCold, + objectMapper, + Execs.scheduledSingleThreaded("coordinator_test_load_queue_peon_cold_scheduled-%d"), + Execs.singleThreaded("coordinator_test_load_queue_peon_cold-%d"), + druidCoordinatorConfig + ); - if (segment != null) { - coldServer.addDataSegment(segment); - curator.delete().guaranteed().forPath(event.getData().getPath()); - } + final LoadQueuePeon loadQueuePeonBroker1 = new CuratorLoadQueuePeon( + curator, + loadPathBroker1, + objectMapper, + Execs.scheduledSingleThreaded("coordinator_test_load_queue_peon_broker1_scheduled-%d"), + Execs.singleThreaded("coordinator_test_load_queue_peon_broker1-%d"), + druidCoordinatorConfig + ); - assignSegmentLatchCold.countDown(); - } - } + final LoadQueuePeon loadQueuePeonBroker2 = new CuratorLoadQueuePeon( + curator, + loadPathBroker2, + objectMapper, + Execs.scheduledSingleThreaded("coordinator_test_load_queue_peon_broker2_scheduled-%d"), + Execs.singleThreaded("coordinator_test_load_queue_peon_broker2-%d"), + druidCoordinatorConfig + ); + + final LoadQueuePeon loadQueuePeonPoenServer = new CuratorLoadQueuePeon( + curator, + loadPathPeon, + objectMapper, + Execs.scheduledSingleThreaded("coordinator_test_load_queue_peon_peon_scheduled-%d"), + Execs.singleThreaded("coordinator_test_load_queue_peon_peon-%d"), + druidCoordinatorConfig + ); + final PathChildrenCache pathChildrenCacheCold = new PathChildrenCache( + curator, + loadPathCold, + true, + true, + Execs.singleThreaded("coordinator_test_path_children_cache_cold-%d") + ); + final PathChildrenCache pathChildrenCacheBroker1 = new PathChildrenCache( + curator, + loadPathBroker1, + true, + true, + Execs.singleThreaded("coordinator_test_path_children_cache_broker1-%d") + ); + final PathChildrenCache pathChildrenCacheBroker2 = new PathChildrenCache( + curator, + loadPathBroker2, + true, + true, + Execs.singleThreaded("coordinator_test_path_children_cache_broker2-%d") + ); + final PathChildrenCache pathChildrenCachePeon = new PathChildrenCache( + curator, + loadPathPeon, + true, + true, + Execs.singleThreaded("coordinator_test_path_children_cache_peon-%d") ); + loadManagementPeons.putAll(ImmutableMap.of("hot", loadQueuePeon, + "cold", loadQueuePeonCold, + "broker1", loadQueuePeonBroker1, + "broker2", loadQueuePeonBroker2, + "peon", loadQueuePeonPoenServer)); + + loadQueuePeonCold.start(); + loadQueuePeonBroker1.start(); + loadQueuePeonBroker2.start(); + loadQueuePeonPoenServer.start(); + pathChildrenCache.start(); + pathChildrenCacheCold.start(); + pathChildrenCacheBroker1.start(); + pathChildrenCacheBroker2.start(); + pathChildrenCachePeon.start(); + + DruidDataSource[] druidDataSources = {new DruidDataSource(dataSource, Collections.emptyMap())}; + dataSegments.values().forEach(druidDataSources[0]::addSegment); + + setupSegmentsMetadataMock(druidDataSources[0]); + + EasyMock.expect(metadataRuleManager.getRulesWithDefault(EasyMock.anyString())) + .andReturn(ImmutableList.of(broadcastDistributionRule)).atLeastOnce(); + EasyMock.expect(metadataRuleManager.getAllRules()) + .andReturn(ImmutableMap.of(dataSource, ImmutableList.of(broadcastDistributionRule))).atLeastOnce(); + + EasyMock.expect(serverInventoryView.getInventory()) + .andReturn(ImmutableList.of(hotServer, coldServer, brokerServer1, brokerServer2, peonServer)) + .atLeastOnce(); + EasyMock.expect(serverInventoryView.isStarted()).andReturn(true).anyTimes(); + + EasyMock.replay(metadataRuleManager, serverInventoryView); + + coordinator.start(); + leaderAnnouncerLatch.await(); // Wait for this coordinator to become leader + + final CountDownLatch assignSegmentLatchHot = createCountDownLatchAndSetPathChildrenCacheListenerWithLatch(3, pathChildrenCache, dataSegments, hotServer); + final CountDownLatch assignSegmentLatchCold = createCountDownLatchAndSetPathChildrenCacheListenerWithLatch(3, pathChildrenCacheCold, dataSegments, coldServer); + final CountDownLatch assignSegmentLatchBroker1 = createCountDownLatchAndSetPathChildrenCacheListenerWithLatch(3, pathChildrenCacheBroker1, dataSegments, brokerServer1); + final CountDownLatch assignSegmentLatchBroker2 = createCountDownLatchAndSetPathChildrenCacheListenerWithLatch(3, pathChildrenCacheBroker2, dataSegments, brokerServer2); + final CountDownLatch assignSegmentLatchPeon = createCountDownLatchAndSetPathChildrenCacheListenerWithLatch(3, pathChildrenCachePeon, dataSegments, peonServer); assignSegmentLatchHot.await(); assignSegmentLatchCold.await(); + assignSegmentLatchBroker1.await(); + assignSegmentLatchBroker2.await(); + assignSegmentLatchPeon.await(); + Assert.assertTrue(serverAddedCountExpected); final CountDownLatch coordinatorRunLatch = new CountDownLatch(2); serviceEmitter.latch = coordinatorRunLatch; @@ -538,9 +656,11 @@ public void testCoordinatorTieredRun() throws Exception Map> underReplicationCountsPerDataSourcePerTier = coordinator.computeUnderReplicationCountsPerDataSourcePerTier(); - Assert.assertEquals(2, underReplicationCountsPerDataSourcePerTier.size()); + Assert.assertEquals(4, underReplicationCountsPerDataSourcePerTier.size()); Assert.assertEquals(0L, underReplicationCountsPerDataSourcePerTier.get(hotTierName).getLong(dataSource)); Assert.assertEquals(0L, underReplicationCountsPerDataSourcePerTier.get(coldTierName).getLong(dataSource)); + Assert.assertEquals(0L, underReplicationCountsPerDataSourcePerTier.get(tierName1).getLong(dataSource)); + Assert.assertEquals(0L, underReplicationCountsPerDataSourcePerTier.get(tierName2).getLong(dataSource)); coordinator.stop(); leaderUnannouncerLatch.await(); @@ -550,6 +670,32 @@ public void testCoordinatorTieredRun() throws Exception EasyMock.verify(metadataRuleManager); } + private CountDownLatch createCountDownLatchAndSetPathChildrenCacheListenerWithLatch(int latchCount, + PathChildrenCache pathChildrenCache, + Map segments, + DruidServer server) + { + final CountDownLatch countDownLatch = new CountDownLatch(latchCount); + pathChildrenCache.getListenable().addListener( + (CuratorFramework client, PathChildrenCacheEvent event) -> { + if (CuratorUtils.isChildAdded(event)) { + if (countDownLatch.getCount() > 0) { + DataSegment segment = findSegmentRelatedToCuratorEvent(segments, event); + if (segment != null) { + server.addDataSegment(segment); + curator.delete().guaranteed().forPath(event.getData().getPath()); + } + countDownLatch.countDown(); + } else { + // The segment is assigned to the server more times than expected + serverAddedCountExpected = false; + } + } + } + ); + return countDownLatch; + } + private void setupSegmentsMetadataMock(DruidDataSource dataSource) { EasyMock.expect(segmentsMetadataManager.isPollingDatabasePeriodically()).andReturn(true).anyTimes(); From 87fb04837203cfa1359fc1cb4bebdbd3b676ccd7 Mon Sep 17 00:00:00 2001 From: Suneet Saldanha Date: Thu, 18 Jun 2020 18:18:12 -0700 Subject: [PATCH 095/107] Remove changes from #9114 (#10050) --- docs/configuration/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/configuration/index.md b/docs/configuration/index.md index 4632554e4e5d..34b458a49b2b 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -353,7 +353,6 @@ The following monitors are available: |`org.apache.druid.segment.realtime.RealtimeMetricsMonitor`|Reports statistics on Realtime processes.| |`org.apache.druid.server.metrics.EventReceiverFirehoseMonitor`|Reports how many events have been queued in the EventReceiverFirehose.| |`org.apache.druid.server.metrics.QueryCountStatsMonitor`|Reports how many queries have been successful/failed/interrupted.| -|`org.apache.druid.server.metrics.TaskCountStatsMonitor`|Reports how many tasks are success/failed/running/pending/waiting.| |`org.apache.druid.server.emitter.HttpEmittingMonitor`|Reports internal metrics of `http` or `parametrized` emitter (see below). Must not be used with another emitter type. See the description of the metrics here: https://github.com/apache/druid/pull/4973.| |`org.apache.druid.server.metrics.TaskCountStatsMonitor`|Reports how many ingestion tasks are currently running/pending/waiting and also the number of successful/failed tasks per emission period.| From 10cea13b764bd47011f182dd4f50f1fdaf3c8e64 Mon Sep 17 00:00:00 2001 From: Jihoon Son Date: Thu, 18 Jun 2020 18:40:43 -0700 Subject: [PATCH 096/107] Create packed core partitions for hash/range-partitioned segments in native batch ingestion (#10025) * Fill in the core partition set size properly for batch ingestion with dynamic partitioning * incomplete javadoc * Address comments * fix tests * fix json serde, add tests * checkstyle * Set core partition set size for hash-partitioned segments properly in batch ingestion * test for both parallel and single-threaded task * unused variables * fix test * unused imports * add hash/range buckets * some test adjustment and missing json serde * centralized partition id allocation in parallel and simple tasks * remove string partition chunk * revive string partition chunk * fill numCorePartitions for hadoop * clean up hash stuffs * resolved todos * javadocs * Fix tests * add more tests * doc * unused imports --- .../segment/loading/DataSegmentPusher.java | 9 + .../partition/BucketNumberedShardSpec.java | 106 +++++++++++ .../BuildingHashBasedNumberedShardSpec.java | 144 +++++++++++++++ .../partition/BuildingNumberedShardSpec.java | 74 ++------ .../timeline/partition/BuildingShardSpec.java | 103 +++++++++++ .../BuildingSingleDimensionShardSpec.java | 141 ++++++++++++++ .../HashBasedNumberedPartialShardSpec.java | 27 ++- .../partition/HashBasedNumberedShardSpec.java | 97 ++++++++-- .../partition/HashBucketShardSpec.java | 129 +++++++++++++ .../timeline/partition/LinearShardSpec.java | 8 +- .../timeline/partition/NoneShardSpec.java | 8 +- .../partition/NumberedOverwriteShardSpec.java | 2 +- .../partition/NumberedPartialShardSpec.java | 2 +- .../timeline/partition/NumberedShardSpec.java | 24 +-- .../partition/OverwriteShardSpec.java | 11 ++ .../partition/RangeBucketShardSpec.java | 137 ++++++++++++++ .../druid/timeline/partition/ShardSpec.java | 15 +- .../SingleDimensionPartialShardSpec.java | 25 ++- .../partition/SingleDimensionShardSpec.java | 94 +++++++--- .../druid/timeline/DataSegmentTest.java | 8 +- ...uildingHashBasedNumberedShardSpecTest.java | 85 +++++++++ .../BuildingNumberedShardSpecTest.java | 35 +--- .../BuildingSingleDimensionShardSpecTest.java | 72 ++++++++ ...HashBasedNumberedPartialShardSpecTest.java | 5 +- .../partition/HashBucketShardSpecTest.java | 121 ++++++++++++ .../NumberedOverwriteShardSpecTest.java | 2 +- .../PartitionHolderCompletenessTest.java | 103 +++++++++++ .../partition/RangeBucketShardSpecTest.java | 115 ++++++++++++ .../partition/ShardSpecTestUtils.java | 49 +++++ docs/ingestion/native-batch.md | 2 +- .../MaterializedViewSupervisorTest.java | 16 +- .../indexer/DetermineHashedPartitionsJob.java | 2 + .../druid/indexer/DeterminePartitionsJob.java | 15 +- .../HadoopDruidDetermineConfigurationJob.java | 2 + .../indexer/BatchDeltaIngestionTest.java | 2 +- .../indexer/HadoopDruidIndexerConfigTest.java | 2 +- .../druid/indexer/IndexGeneratorJobTest.java | 9 +- .../task/CachingLocalSegmentAllocator.java | 138 +++++++------- .../druid/indexing/common/task/IndexTask.java | 15 +- .../common/task/LocalSegmentAllocator.java | 12 +- ...nearlyPartitionedSequenceNameFunction.java | 7 +- .../OverlordCoordinatingSegmentAllocator.java | 12 +- ...tor.java => SegmentAllocatorForBatch.java} | 12 +- .../common/task/SegmentAllocators.java | 16 +- .../indexing/common/task/ShardSpecs.java | 14 +- ...visorTaskCoordinatingSegmentAllocator.java | 12 +- .../druid/indexing/common/task/Task.java | 2 - .../GeneratedHashPartitionsReport.java | 44 ----- .../parallel/GenericPartitionLocation.java | 12 +- .../batch/parallel/GenericPartitionStat.java | 13 +- .../batch/parallel/HashPartitionLocation.java | 51 ------ .../batch/parallel/HashPartitionStat.java | 90 --------- .../parallel/ParallelIndexSupervisorTask.java | 104 ++++------- .../parallel/ParallelIndexTaskRunner.java | 5 +- .../parallel/ParallelIndexTuningConfig.java | 4 +- .../PartialGenericSegmentMergeTask.java | 36 ++-- ...egmentGenerateParallelIndexTaskRunner.java | 4 +- .../PartialHashSegmentGenerateTask.java | 25 +-- .../PartialHashSegmentMergeIOConfig.java | 40 ---- .../PartialHashSegmentMergeIngestionSpec.java | 37 ---- ...shSegmentMergeParallelIndexTaskRunner.java | 115 ------------ .../parallel/PartialHashSegmentMergeTask.java | 112 ------------ ...egmentGenerateParallelIndexTaskRunner.java | 2 - .../PartialRangeSegmentGenerateTask.java | 9 +- .../parallel/PartialSegmentGenerateTask.java | 12 +- .../parallel/PartialSegmentMergeTask.java | 32 ++-- .../batch/parallel/PartitionLocation.java | 8 +- .../task/batch/parallel/PartitionStat.java | 2 +- .../batch/parallel/SinglePhaseSubTask.java | 1 + .../task/batch/parallel/SubTaskReport.java | 1 - .../partition/CompletePartitionAnalysis.java | 9 +- .../partition/HashPartitionAnalysis.java | 49 ++--- .../partition/RangePartitionAnalysis.java | 85 +++------ .../worker/IntermediaryDataManager.java | 14 +- .../indexing/worker/http/ShuffleResource.java | 8 +- .../actions/SegmentAllocateActionTest.java | 17 +- .../indexing/common/task/IndexTaskTest.java | 4 +- ...itionCachingLocalSegmentAllocatorTest.java | 45 ++--- .../indexing/common/task/ShardSpecsTest.java | 12 +- ...bstractMultiPhaseParallelIndexingTest.java | 47 +++-- ...stractParallelIndexSupervisorTaskTest.java | 3 +- .../GeneratedHashPartitionsReportTest.java | 59 ------ .../GenericPartitionLocationTest.java | 2 +- .../parallel/GenericPartitionStatTest.java | 12 +- ...rtitionAdjustingCorePartitionSizeTest.java | 165 +++++++++++++++++ ...itionCachingLocalSegmentAllocatorTest.java | 21 ++- ...rtitionMultiPhaseParallelIndexingTest.java | 21 ++- .../batch/parallel/HashPartitionStatTest.java | 59 ------ .../batch/parallel/HttpShuffleClientTest.java | 2 +- ...rallelIndexSupervisorTaskResourceTest.java | 1 + .../ParallelIndexSupervisorTaskTest.java | 24 +-- .../parallel/ParallelIndexTestingFactory.java | 5 +- .../PartialHashSegmentMergeIOConfigTest.java | 54 ------ ...tialHashSegmentMergeIngestionSpecTest.java | 68 ------- .../PartialHashSegmentMergeTaskTest.java | 88 --------- ...rtitionAdjustingCorePartitionSizeTest.java | 167 +++++++++++++++++ .../SinglePhaseParallelIndexingTest.java | 6 +- .../indexing/overlord/TaskLockboxTest.java | 4 +- .../indexer/AbstractITBatchIndexTest.java | 2 - .../appenderator/SegmentPublisherHelper.java | 17 +- .../client/CachingClusteredClientTest.java | 48 ++--- ...exerSQLMetadataStorageCoordinatorTest.java | 13 +- .../SegmentIdWithShardSpecTest.java | 2 +- .../SegmentPublisherHelperTest.java | 173 ++++++++++++++++++ .../server/shard/NumberedShardSpecTest.java | 11 +- .../shard/SingleDimensionShardSpecTest.java | 2 +- .../HashBasedNumberedShardSpecTest.java | 69 ++++--- 107 files changed, 2649 insertions(+), 1539 deletions(-) create mode 100644 core/src/main/java/org/apache/druid/timeline/partition/BucketNumberedShardSpec.java create mode 100644 core/src/main/java/org/apache/druid/timeline/partition/BuildingHashBasedNumberedShardSpec.java create mode 100644 core/src/main/java/org/apache/druid/timeline/partition/BuildingShardSpec.java create mode 100644 core/src/main/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpec.java create mode 100644 core/src/main/java/org/apache/druid/timeline/partition/HashBucketShardSpec.java create mode 100644 core/src/main/java/org/apache/druid/timeline/partition/RangeBucketShardSpec.java create mode 100644 core/src/test/java/org/apache/druid/timeline/partition/BuildingHashBasedNumberedShardSpecTest.java create mode 100644 core/src/test/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpecTest.java create mode 100644 core/src/test/java/org/apache/druid/timeline/partition/HashBucketShardSpecTest.java create mode 100644 core/src/test/java/org/apache/druid/timeline/partition/PartitionHolderCompletenessTest.java create mode 100644 core/src/test/java/org/apache/druid/timeline/partition/RangeBucketShardSpecTest.java create mode 100644 core/src/test/java/org/apache/druid/timeline/partition/ShardSpecTestUtils.java rename indexing-service/src/main/java/org/apache/druid/indexing/common/task/{CachingSegmentAllocator.java => SegmentAllocatorForBatch.java} (57%) delete mode 100644 indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GeneratedHashPartitionsReport.java delete mode 100644 indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionLocation.java delete mode 100644 indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionStat.java delete mode 100644 indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIOConfig.java delete mode 100644 indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIngestionSpec.java delete mode 100644 indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeParallelIndexTaskRunner.java delete mode 100644 indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeTask.java delete mode 100644 indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GeneratedHashPartitionsReportTest.java create mode 100644 indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionAdjustingCorePartitionSizeTest.java delete mode 100644 indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionStatTest.java delete mode 100644 indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIOConfigTest.java delete mode 100644 indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIngestionSpecTest.java delete mode 100644 indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeTaskTest.java create mode 100644 indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/RangePartitionAdjustingCorePartitionSizeTest.java create mode 100644 server/src/test/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelperTest.java diff --git a/core/src/main/java/org/apache/druid/segment/loading/DataSegmentPusher.java b/core/src/main/java/org/apache/druid/segment/loading/DataSegmentPusher.java index 7e9eb9234b76..98c75505c05f 100644 --- a/core/src/main/java/org/apache/druid/segment/loading/DataSegmentPusher.java +++ b/core/src/main/java/org/apache/druid/segment/loading/DataSegmentPusher.java @@ -20,9 +20,11 @@ package org.apache.druid.segment.loading; import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; import org.apache.druid.guice.annotations.ExtensionPoint; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; import java.io.File; import java.io.IOException; @@ -105,6 +107,13 @@ default List getAllowedPropertyPrefixesForHadoop() // on segment deletion if segment being deleted was the only segment static String getDefaultStorageDir(DataSegment segment, boolean useUniquePath) { + // Sanity check for shardSpec type. + // BucketNumberedShardSpec should never be used in segment push. + Preconditions.checkArgument( + !(segment.getShardSpec() instanceof BucketNumberedShardSpec), + "Illegal shardSpec type[%s]", + segment.getShardSpec() + ); return JOINER.join( segment.getDataSource(), StringUtils.format("%s_%s", segment.getInterval().getStart(), segment.getInterval().getEnd()), diff --git a/core/src/main/java/org/apache/druid/timeline/partition/BucketNumberedShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/BucketNumberedShardSpec.java new file mode 100644 index 000000000000..d692dad113da --- /dev/null +++ b/core/src/main/java/org/apache/druid/timeline/partition/BucketNumberedShardSpec.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.google.common.collect.RangeSet; + +import java.util.List; +import java.util.Map; + +/** + * This is one of the special shardSpecs which are temporarily used during batch ingestion. In Druid, there is a + * concept of core partition set which is a set of segments atomically becoming queryable together in Brokers. The core + * partition set is represented as a range of partitionIds, i.e., [0, {@link ShardSpec#getNumCorePartitions()}). + * + * When you run a batch ingestion task with a non-linear partitioning scheme, the task populates all possible buckets + * upfront at the beginning (see {@code CachingLocalSegmentAllocator}) and uses them to partition input rows. However, + * some of the buckets can be empty even after the task consumes all inputs if the data is highly skewed. Since Druid + * doesn't create empty segments, the partitionId should be dynamically allocated when a bucket is actually in use, + * so that we can always create the packed core partition set without missing partitionIds. + * + * This BucketNumberedShardSpec is used for such use case. The task with a non-linear partitioning scheme uses it + * to postpone the partitionId allocation until all empty buckets are identified. See + * {@code ParallelIndexSupervisorTask.groupGenericPartitionLocationsPerPartition} and + * {@code CachingLocalSegmentAllocator} for parallel and sequential ingestion, respectively. + * + * Note that {@link org.apache.druid.timeline.SegmentId} requires the partitionId. Since the segmentId is used + * everwhere during ingestion, this class should implement {@link #getPartitionNum()} which returns the bucketId. + * This should be fine because the segmentId is only used to identify each segment until pushing them to deep storage. + * The bucketId should be enough to uniquely identify each segment. However, when pushing segments to deep storage, + * the partitionId is used to create the path to store the segment on deep storage + * ({@link org.apache.druid.segment.loading.DataSegmentPusher#getDefaultStorageDir} which should be correct. + * As a result, this shardSpec should not be used in pushing segments. + * + * This class should be Jackson-serializable as the subtasks can send it to the parallel task in parallel ingestion. + * + * This interface doesn't really have to extend {@link ShardSpec}. The only reason is the ShardSpec is used in many + * places such as {@link org.apache.druid.timeline.DataSegment}, and we have to modify those places to allow other + * types than ShardSpec which seems pretty invasive. Maybe we could clean up this mess someday in the future. + * + * @see BuildingShardSpec + */ +public interface BucketNumberedShardSpec extends ShardSpec +{ + int getBucketId(); + + T convert(int partitionId); + + @Override + default PartitionChunk createChunk(O obj) + { + // The partitionId (or partitionNum, chunkNumber) is not determined yet. Use bucketId for now. + return new NumberedPartitionChunk<>(getBucketId(), 0, obj); + } + + @Override + default int getPartitionNum() + { + // See the class-level Javadoc for returning bucketId here. + return getBucketId(); + } + + @Override + default int getNumCorePartitions() + { + throw new UnsupportedOperationException(); + } + + // The below methods are used on the query side, and so must not be called for this shardSpec. + + @JsonIgnore + @Override + default List getDomainDimensions() + { + throw new UnsupportedOperationException(); + } + + @Override + default boolean possibleInDomain(Map> domain) + { + throw new UnsupportedOperationException(); + } + + @Override + default boolean isCompatible(Class other) + { + throw new UnsupportedOperationException(); + } +} diff --git a/core/src/main/java/org/apache/druid/timeline/partition/BuildingHashBasedNumberedShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/BuildingHashBasedNumberedShardSpec.java new file mode 100644 index 000000000000..fb896fc2ac84 --- /dev/null +++ b/core/src/main/java/org/apache/druid/timeline/partition/BuildingHashBasedNumberedShardSpec.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.annotation.JacksonInject; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; + +/** + * See {@link BuildingShardSpec} for how this class is used. + * + * @see HashBasedNumberedShardSpec + */ +public class BuildingHashBasedNumberedShardSpec implements BuildingShardSpec +{ + public static final String TYPE = "building_hashed"; + + private final int partitionId; + private final int bucketId; + private final int numBuckets; + private final List partitionDimensions; + private final ObjectMapper jsonMapper; + + @JsonCreator + public BuildingHashBasedNumberedShardSpec( + @JsonProperty("partitionId") int partitionId, + @JsonProperty("bucketId") int bucketId, + @JsonProperty("numBuckets") int numBuckets, + @JsonProperty("partitionDimensions") @Nullable List partitionDimensions, + @JacksonInject ObjectMapper jsonMapper + ) + { + this.partitionId = partitionId; + this.bucketId = bucketId; + this.numBuckets = numBuckets; + this.partitionDimensions = partitionDimensions == null + ? HashBasedNumberedShardSpec.DEFAULT_PARTITION_DIMENSIONS + : partitionDimensions; + this.jsonMapper = jsonMapper; + } + + @JsonProperty("partitionId") + @Override + public int getPartitionNum() + { + return partitionId; + } + + @Override + @JsonProperty + public int getBucketId() + { + return bucketId; + } + + @JsonProperty + public int getNumBuckets() + { + return numBuckets; + } + + @JsonProperty + public List getPartitionDimensions() + { + return partitionDimensions; + } + + @Override + public PartitionChunk createChunk(T obj) + { + // This method can be called in AppenderatorImpl to create a sinkTimeline. + // The sinkTimeline doesn't seem in use in batch ingestion, let's set 'chunks' to 0 for now. + // HashBasedNumberedShardSpec is using NumberedPartitionChunk, so we use it here too. + return new NumberedPartitionChunk<>(partitionId, 0, obj); + } + + @Override + public HashBasedNumberedShardSpec convert(int numCorePartitions) + { + return new HashBasedNumberedShardSpec( + partitionId, + numCorePartitions, + bucketId, + numBuckets, + partitionDimensions, + jsonMapper + ); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BuildingHashBasedNumberedShardSpec that = (BuildingHashBasedNumberedShardSpec) o; + return partitionId == that.partitionId && + bucketId == that.bucketId && + numBuckets == that.numBuckets && + Objects.equals(partitionDimensions, that.partitionDimensions); + } + + @Override + public int hashCode() + { + return Objects.hash(partitionId, bucketId, numBuckets, partitionDimensions); + } + + @Override + public String toString() + { + return "BuildingHashBasedNumberedShardSpec{" + + "partitionId=" + partitionId + + ", bucketId=" + bucketId + + ", numBuckets=" + numBuckets + + ", partitionDimensions=" + partitionDimensions + + '}'; + } +} diff --git a/core/src/main/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpec.java index 4604f23a982c..a179d3ca7bac 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpec.java @@ -20,45 +20,20 @@ package org.apache.druid.timeline.partition; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Preconditions; -import com.google.common.collect.RangeSet; -import org.apache.druid.data.input.InputRow; -import java.util.List; -import java.util.Map; import java.util.Objects; /** - * This is a special shardSpec which is temporarily used during batch ingestion. In Druid, there is a concept - * of core partition set which is a set of segments atomically becoming queryable together in Brokers. The core - * partition set is represented as a range of partitionIds. For {@link NumberedShardSpec}, the core partition set - * is [0, {@link NumberedShardSpec#partitions}). + * See {@link BuildingShardSpec} for how this class is used. * - * The NumberedShardSpec is used for dynamic partitioning which is based on the number of rows in each segment. - * In streaming ingestion, the core partition set size cannot be determined since it's impossible to know how many - * segments will be created per time chunk. However, in batch ingestion with time chunk locking, the core partition - * set is the set of segments created by an initial task or an overwriting task. Since the core partition set is - * determined when the task publishes segments at the end, the task postpones creating proper NumberedShardSpec - * until the end. - * - * This shardSpec is used for such use case. A non-appending batch task can use this shardSpec until it publishes - * segments at last. When it publishes segments, it should convert the shardSpec of those segments to NumberedShardSpec. - * See {@code SegmentPublisherHelper#annotateShardSpec} for converting to NumberedShardSpec. Note that, when - * the segment lock is used, the Overlord coordinates the segment allocation and this class is never used. Instead, - * the task sends {@link PartialShardSpec} to the Overlord to allocate a new segment. The result segment could have - * either a {@link ShardSpec} (for root generation segments) or an {@link OverwriteShardSpec} (for non-root - * generation segments). - * - * This class should be Jackson-serializable as the subtasks can send it to the parallel task in parallel ingestion. - * - * Finally, this shardSpec has only partitionId which is same as {@link LinearShardSpec}. The difference between + * This shardSpec has only partitionId which is same as {@link LinearShardSpec}. The difference between * them is this shardSpec should never be published and so never be used in other places such as Broker timeline. * * @see NumberedShardSpec */ -public class BuildingNumberedShardSpec implements ShardSpec +public class BuildingNumberedShardSpec implements BuildingShardSpec { public static final String TYPE = "building_numbered"; @@ -71,7 +46,15 @@ public BuildingNumberedShardSpec(@JsonProperty("partitionId") int partitionId) this.partitionId = partitionId; } - public NumberedShardSpec toNumberedShardSpec(int numTotalPartitions) + @Override + public int getBucketId() + { + // This method is currently not called when the shardSpec type is this class. + throw new UnsupportedOperationException(); + } + + @Override + public NumberedShardSpec convert(int numTotalPartitions) { return new NumberedShardSpec(partitionId, numTotalPartitions); } @@ -91,39 +74,6 @@ public int getPartitionNum() return partitionId; } - @Override - public ShardSpecLookup getLookup(List shardSpecs) - { - return NumberedShardSpec.createLookup(shardSpecs); - } - - // The below methods are used on the query side, and so must not be called for this shardSpec. - - @Override - public boolean isInChunk(long timestamp, InputRow inputRow) - { - throw new UnsupportedOperationException(); - } - - @JsonIgnore - @Override - public List getDomainDimensions() - { - throw new UnsupportedOperationException(); - } - - @Override - public boolean possibleInDomain(Map> domain) - { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isCompatible(Class other) - { - throw new UnsupportedOperationException(); - } - @Override public boolean equals(Object o) { diff --git a/core/src/main/java/org/apache/druid/timeline/partition/BuildingShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/BuildingShardSpec.java new file mode 100644 index 000000000000..973fdf4d2a7d --- /dev/null +++ b/core/src/main/java/org/apache/druid/timeline/partition/BuildingShardSpec.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.google.common.collect.RangeSet; +import org.apache.druid.data.input.InputRow; + +import java.util.List; +import java.util.Map; + +/** + * This is one of the special shardSpecs which are temporarily used during batch ingestion. In Druid, there is a + * concept of core partition set which is a set of segments atomically becoming queryable together in Brokers. The core + * partition set is represented as a range of partitionIds, i.e., [0, {@link ShardSpec#getNumCorePartitions()}). + * + * In streaming ingestion, the core partition set size cannot be determined since it's impossible to know how many + * segments will be created per time chunk upfront. However, in batch ingestion with time chunk locking, the core + * partition set is the set of segments created by an initial task or an overwriting task. Since the core partition + * set is determined when the task publishes segments at the end, the task postpones creating proper {@link ShardSpec} + * until the end. + * + * This BuildingShardSpec is used for such use case. A non-appending batch task can use this shardSpec until it + * publishes segments at last. When it publishes segments, it should convert the buildingShardSpec of those segments + * to a proper shardSpec type {@link T}. See {@code SegmentPublisherHelper#annotateShardSpec} for converting shardSpec. + * Note that, when the segment lock is used, the Overlord coordinates the segment allocation and this class is never + * used. Instead, the task sends {@link PartialShardSpec} to the Overlord to allocate a new segment. The result segment + * could have either a {@link ShardSpec} (for root generation segments) or an {@link OverwriteShardSpec} (for non-root + * generation segments). + * + * This class should be Jackson-serializable as the subtasks can send it to the parallel task in parallel ingestion. + * + * This interface doesn't really have to extend {@link ShardSpec}. The only reason is the ShardSpec is used in many + * places such as {@link org.apache.druid.timeline.DataSegment}, and we have to modify those places to allow other + * types than ShardSpec which seems pretty invasive. Maybe we could clean up this mess someday in the future. + * + * @see BucketNumberedShardSpec + */ +public interface BuildingShardSpec extends ShardSpec +{ + int getBucketId(); + + T convert(int numCorePartitions); + + @Override + default int getNumCorePartitions() + { + throw new UnsupportedOperationException(); + } + + /** + * {@link BucketNumberedShardSpec} should be used for shard spec lookup. + */ + @Override + default ShardSpecLookup getLookup(List shardSpecs) + { + throw new UnsupportedOperationException(); + } + + // The below methods are used on the query side, and so must not be called for this shardSpec. + + @Override + default boolean isInChunk(long timestamp, InputRow inputRow) + { + throw new UnsupportedOperationException(); + } + + @JsonIgnore + @Override + default List getDomainDimensions() + { + throw new UnsupportedOperationException(); + } + + @Override + default boolean possibleInDomain(Map> domain) + { + throw new UnsupportedOperationException(); + } + + @Override + default boolean isCompatible(Class other) + { + throw new UnsupportedOperationException(); + } +} diff --git a/core/src/main/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpec.java new file mode 100644 index 000000000000..6dd099205447 --- /dev/null +++ b/core/src/main/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpec.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.annotation.Nullable; +import java.util.Objects; + +/** + * See {@link BuildingShardSpec} for how this class is used. + * + * @see SingleDimensionShardSpec + */ +public class BuildingSingleDimensionShardSpec implements BuildingShardSpec +{ + public static final String TYPE = "building_single_dim"; + + private final int bucketId; + private final String dimension; + @Nullable + private final String start; + @Nullable + private final String end; + private final int partitionId; + + @JsonCreator + public BuildingSingleDimensionShardSpec( + @JsonProperty("bucketId") int bucketId, + @JsonProperty("dimension") String dimension, + @JsonProperty("start") @Nullable String start, + @JsonProperty("end") @Nullable String end, + @JsonProperty("partitionNum") int partitionNum + ) + { + this.bucketId = bucketId; + this.dimension = dimension; + this.start = start; + this.end = end; + this.partitionId = partitionNum; + } + + @JsonProperty("dimension") + public String getDimension() + { + return dimension; + } + + @Nullable + @JsonProperty("start") + public String getStart() + { + return start; + } + + @Nullable + @JsonProperty("end") + public String getEnd() + { + return end; + } + + @Override + @JsonProperty("partitionNum") + public int getPartitionNum() + { + return partitionId; + } + + @Override + @JsonProperty("bucketId") + public int getBucketId() + { + return bucketId; + } + + @Override + public SingleDimensionShardSpec convert(int numCorePartitions) + { + return new SingleDimensionShardSpec(dimension, start, end, partitionId, numCorePartitions); + } + + @Override + public PartitionChunk createChunk(T obj) + { + return new NumberedPartitionChunk<>(partitionId, 0, obj); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BuildingSingleDimensionShardSpec that = (BuildingSingleDimensionShardSpec) o; + return bucketId == that.bucketId && + partitionId == that.partitionId && + Objects.equals(dimension, that.dimension) && + Objects.equals(start, that.start) && + Objects.equals(end, that.end); + } + + @Override + public int hashCode() + { + return Objects.hash(bucketId, dimension, start, end, partitionId); + } + + @Override + public String toString() + { + return "BuildingSingleDimensionShardSpec{" + + "bucketId=" + bucketId + + ", dimension='" + dimension + '\'' + + ", start='" + start + '\'' + + ", end='" + end + '\'' + + ", partitionNum=" + partitionId + + '}'; + } +} diff --git a/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedPartialShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedPartialShardSpec.java index 495a85284bb3..0e32ee04bcf1 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedPartialShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedPartialShardSpec.java @@ -33,15 +33,18 @@ public class HashBasedNumberedPartialShardSpec implements PartialShardSpec @Nullable private final List partitionDimensions; + private final int bucketId; private final int numBuckets; @JsonCreator public HashBasedNumberedPartialShardSpec( @JsonProperty("partitionDimensions") @Nullable List partitionDimensions, + @JsonProperty("bucketId") int bucketId, @JsonProperty("numPartitions") int numBuckets ) { this.partitionDimensions = partitionDimensions; + this.bucketId = bucketId; this.numBuckets = numBuckets; } @@ -52,6 +55,12 @@ public List getPartitionDimensions() return partitionDimensions; } + @JsonProperty + public int getBucketId() + { + return bucketId; + } + @JsonProperty("numPartitions") public int getNumBuckets() { @@ -61,9 +70,16 @@ public int getNumBuckets() @Override public ShardSpec complete(ObjectMapper objectMapper, @Nullable ShardSpec specOfPreviousMaxPartitionId) { - final HashBasedNumberedShardSpec prevSpec = (HashBasedNumberedShardSpec) specOfPreviousMaxPartitionId; + // The shardSpec is created by the Overlord. + // For batch tasks, this code can be executed only with segment locking (forceTimeChunkLock = false). + // In this mode, you can have 2 or more tasks concurrently ingesting into the same time chunk of + // the same datasource. Since there is no restriction for those tasks in segment allocation, the + // allocated IDs for each task can interleave. As a result, the core partition set cannot be + // represented as a range. We always set 0 for the core partition set size if this is an initial segment. return new HashBasedNumberedShardSpec( - prevSpec == null ? 0 : prevSpec.getPartitionNum() + 1, + specOfPreviousMaxPartitionId == null ? 0 : specOfPreviousMaxPartitionId.getPartitionNum() + 1, + specOfPreviousMaxPartitionId == null ? 0 : specOfPreviousMaxPartitionId.getNumCorePartitions(), + bucketId, numBuckets, partitionDimensions, objectMapper @@ -73,7 +89,7 @@ public ShardSpec complete(ObjectMapper objectMapper, @Nullable ShardSpec specOfP @Override public ShardSpec complete(ObjectMapper objectMapper, int partitionId) { - return new HashBasedNumberedShardSpec(partitionId, numBuckets, partitionDimensions, objectMapper); + return new HashBasedNumberedShardSpec(partitionId, 0, bucketId, numBuckets, partitionDimensions, objectMapper); } @Override @@ -92,13 +108,14 @@ public boolean equals(Object o) return false; } HashBasedNumberedPartialShardSpec that = (HashBasedNumberedPartialShardSpec) o; - return numBuckets == that.numBuckets && + return bucketId == that.bucketId && + numBuckets == that.numBuckets && Objects.equals(partitionDimensions, that.partitionDimensions); } @Override public int hashCode() { - return Objects.hash(partitionDimensions, numBuckets); + return Objects.hash(partitionDimensions, bucketId, numBuckets); } } diff --git a/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpec.java index 2d8f525d6fc9..23cdb4ef3868 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpec.java @@ -35,29 +35,55 @@ import javax.annotation.Nullable; import java.util.List; +import java.util.Objects; public class HashBasedNumberedShardSpec extends NumberedShardSpec { + static final List DEFAULT_PARTITION_DIMENSIONS = ImmutableList.of(); + private static final HashFunction HASH_FUNCTION = Hashing.murmur3_32(); - private static final List DEFAULT_PARTITION_DIMENSIONS = ImmutableList.of(); + private final int bucketId; + /** + * Number of hash buckets + */ + private final int numBuckets; private final ObjectMapper jsonMapper; @JsonIgnore private final List partitionDimensions; @JsonCreator public HashBasedNumberedShardSpec( - @JsonProperty("partitionNum") int partitionNum, // partitionId - @JsonProperty("partitions") int partitions, // # of partitions + @JsonProperty("partitionNum") int partitionNum, // partitionId, hash bucketId + @JsonProperty("partitions") int partitions, // core partition set size + @JsonProperty("bucketId") @Nullable Integer bucketId, // nullable for backward compatibility + @JsonProperty("numBuckets") @Nullable Integer numBuckets, // nullable for backward compatibility @JsonProperty("partitionDimensions") @Nullable List partitionDimensions, @JacksonInject ObjectMapper jsonMapper ) { super(partitionNum, partitions); + // Use partitionId as bucketId if it's missing. + this.bucketId = bucketId == null ? partitionNum : bucketId; + // If numBuckets is missing, assume that any hash bucket is not empty. + // Use the core partition set size as the number of buckets. + this.numBuckets = numBuckets == null ? partitions : numBuckets; this.jsonMapper = jsonMapper; this.partitionDimensions = partitionDimensions == null ? DEFAULT_PARTITION_DIMENSIONS : partitionDimensions; } + @JsonProperty + public int getBucketId() + { + return bucketId; + } + + @JsonProperty + public int getNumBuckets() + { + return numBuckets; + } + @JsonProperty("partitionDimensions") public List getPartitionDimensions() { @@ -73,7 +99,7 @@ public boolean isCompatible(Class other) @Override public boolean isInChunk(long timestamp, InputRow inputRow) { - return (((long) hash(timestamp, inputRow)) - getPartitionNum()) % getPartitions() == 0; + return (((long) hash(timestamp, inputRow)) - bucketId) % numBuckets == 0; } /** @@ -88,7 +114,12 @@ public boolean isInChunk(long timestamp, InputRow inputRow) */ protected int hash(long timestamp, InputRow inputRow) { - final List groupKey = getGroupKey(timestamp, inputRow); + return hash(jsonMapper, partitionDimensions, timestamp, inputRow); + } + + public static int hash(ObjectMapper jsonMapper, List partitionDimensions, long timestamp, InputRow inputRow) + { + final List groupKey = getGroupKey(partitionDimensions, timestamp, inputRow); try { return hash(jsonMapper, groupKey); } @@ -98,7 +129,7 @@ protected int hash(long timestamp, InputRow inputRow) } @VisibleForTesting - List getGroupKey(final long timestamp, final InputRow inputRow) + static List getGroupKey(final List partitionDimensions, final long timestamp, final InputRow inputRow) { if (partitionDimensions.isEmpty()) { return Rows.toGroupKey(timestamp, inputRow); @@ -114,21 +145,57 @@ public static int hash(ObjectMapper jsonMapper, List objects) throws Jso } @Override - public String toString() + public ShardSpecLookup getLookup(final List shardSpecs) { - return "HashBasedNumberedShardSpec{" + - "partitionNum=" + getPartitionNum() + - ", partitions=" + getPartitions() + - ", partitionDimensions=" + getPartitionDimensions() + - '}'; + return createHashLookup(jsonMapper, partitionDimensions, shardSpecs, numBuckets); } - @Override - public ShardSpecLookup getLookup(final List shardSpecs) + static ShardSpecLookup createHashLookup( + ObjectMapper jsonMapper, + List partitionDimensions, + List shardSpecs, + int numBuckets + ) { return (long timestamp, InputRow row) -> { - int index = Math.abs(hash(timestamp, row) % getPartitions()); + int index = Math.abs(hash(jsonMapper, partitionDimensions, timestamp, row) % numBuckets); return shardSpecs.get(index); }; } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + HashBasedNumberedShardSpec that = (HashBasedNumberedShardSpec) o; + return bucketId == that.bucketId && + numBuckets == that.numBuckets && + Objects.equals(partitionDimensions, that.partitionDimensions); + } + + @Override + public int hashCode() + { + return Objects.hash(super.hashCode(), bucketId, numBuckets, partitionDimensions); + } + + @Override + public String toString() + { + return "HashBasedNumberedShardSpec{" + + "partitionNum=" + getPartitionNum() + + ", partitions=" + getNumCorePartitions() + + ", bucketId=" + bucketId + + ", numBuckets=" + numBuckets + + ", partitionDimensions=" + partitionDimensions + + '}'; + } } diff --git a/core/src/main/java/org/apache/druid/timeline/partition/HashBucketShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/HashBucketShardSpec.java new file mode 100644 index 000000000000..324c02044985 --- /dev/null +++ b/core/src/main/java/org/apache/druid/timeline/partition/HashBucketShardSpec.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.annotation.JacksonInject; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.druid.data.input.InputRow; + +import java.util.List; +import java.util.Objects; + +/** + * See {@link BucketNumberedShardSpec} for how this class is used. + * + * @see BuildingHashBasedNumberedShardSpec + */ +public class HashBucketShardSpec implements BucketNumberedShardSpec +{ + public static final String TYPE = "bucket_hash"; + + private final int bucketId; + private final int numBuckets; + private final List partitionDimensions; + private final ObjectMapper jsonMapper; + + @JsonCreator + public HashBucketShardSpec( + @JsonProperty("bucketId") int bucketId, + @JsonProperty("numBuckets") int numBuckets, + @JsonProperty("partitionDimensions") List partitionDimensions, + @JacksonInject ObjectMapper jsonMapper + ) + { + this.bucketId = bucketId; + this.numBuckets = numBuckets; + this.partitionDimensions = partitionDimensions == null + ? HashBasedNumberedShardSpec.DEFAULT_PARTITION_DIMENSIONS + : partitionDimensions; + this.jsonMapper = jsonMapper; + } + + @Override + @JsonProperty + public int getBucketId() + { + return bucketId; + } + + @JsonProperty + public int getNumBuckets() + { + return numBuckets; + } + + @JsonProperty + public List getPartitionDimensions() + { + return partitionDimensions; + } + + @Override + public BuildingHashBasedNumberedShardSpec convert(int partitionId) + { + return new BuildingHashBasedNumberedShardSpec(partitionId, bucketId, numBuckets, partitionDimensions, jsonMapper); + } + + @Override + public boolean isInChunk(long timestamp, InputRow inputRow) + { + // not in use + throw new UnsupportedOperationException(); + } + + @Override + public ShardSpecLookup getLookup(List shardSpecs) + { + return HashBasedNumberedShardSpec.createHashLookup(jsonMapper, partitionDimensions, shardSpecs, numBuckets); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HashBucketShardSpec that = (HashBucketShardSpec) o; + return bucketId == that.bucketId && + numBuckets == that.numBuckets && + Objects.equals(partitionDimensions, that.partitionDimensions); + } + + @Override + public int hashCode() + { + return Objects.hash(bucketId, numBuckets, partitionDimensions); + } + + @Override + public String toString() + { + return "HashBucket{" + + ", bucketId=" + bucketId + + ", numBuckets=" + numBuckets + + ", partitionDimensions=" + partitionDimensions + + '}'; + } +} diff --git a/core/src/main/java/org/apache/druid/timeline/partition/LinearShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/LinearShardSpec.java index 1ebb24e16038..95b0bd832b9b 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/LinearShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/LinearShardSpec.java @@ -50,7 +50,13 @@ public int getPartitionNum() } @Override - public ShardSpecLookup getLookup(final List shardSpecs) + public int getNumCorePartitions() + { + return 0; + } + + @Override + public ShardSpecLookup getLookup(final List shardSpecs) { return (long timestamp, InputRow row) -> shardSpecs.get(0); } diff --git a/core/src/main/java/org/apache/druid/timeline/partition/NoneShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/NoneShardSpec.java index dde92167362a..d53011819413 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/NoneShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/NoneShardSpec.java @@ -66,7 +66,13 @@ public int getPartitionNum() } @Override - public ShardSpecLookup getLookup(final List shardSpecs) + public int getNumCorePartitions() + { + return 0; + } + + @Override + public ShardSpecLookup getLookup(final List shardSpecs) { return (long timestamp, InputRow row) -> shardSpecs.get(0); } diff --git a/core/src/main/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpec.java index dbbb8f66e936..adb0d288d6a6 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpec.java @@ -188,7 +188,7 @@ public short getAtomicUpdateGroupSize() } @Override - public ShardSpecLookup getLookup(List shardSpecs) + public ShardSpecLookup getLookup(List shardSpecs) { return (long timestamp, InputRow row) -> shardSpecs.get(0); } diff --git a/core/src/main/java/org/apache/druid/timeline/partition/NumberedPartialShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/NumberedPartialShardSpec.java index 7c7b9753aaa8..730502845f30 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/NumberedPartialShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/NumberedPartialShardSpec.java @@ -50,7 +50,7 @@ public ShardSpec complete(ObjectMapper objectMapper, @Nullable ShardSpec specOfP return new NumberedShardSpec(0, 0); } else { final NumberedShardSpec prevSpec = (NumberedShardSpec) specOfPreviousMaxPartitionId; - return new NumberedShardSpec(prevSpec.getPartitionNum() + 1, prevSpec.getPartitions()); + return new NumberedShardSpec(prevSpec.getPartitionNum() + 1, prevSpec.getNumCorePartitions()); } } diff --git a/core/src/main/java/org/apache/druid/timeline/partition/NumberedShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/NumberedShardSpec.java index 6f8898e298f1..5db6bf6000ac 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/NumberedShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/NumberedShardSpec.java @@ -66,12 +66,17 @@ public int getPartitionNum() } @Override - public ShardSpecLookup getLookup(final List shardSpecs) + public ShardSpecLookup getLookup(final List shardSpecs) + { + return createNumberedLookup(shardSpecs); + } + + static ShardSpecLookup createNumberedLookup(List shardSpecs) { return createLookup(shardSpecs); } - static ShardSpecLookup createLookup(List shardSpecs) + static ShardSpecLookup createLookup(List shardSpecs) { return (long timestamp, InputRow row) -> shardSpecs.get(0); } @@ -94,8 +99,9 @@ public boolean isCompatible(Class other) return other == NumberedShardSpec.class || other == NumberedOverwriteShardSpec.class; } + @Override @JsonProperty("partitions") - public int getPartitions() + public int getNumCorePartitions() { return partitions; } @@ -127,16 +133,12 @@ public boolean equals(Object o) if (this == o) { return true; } - - if (!(o instanceof NumberedShardSpec)) { - return false; - } - - final NumberedShardSpec that = (NumberedShardSpec) o; - if (partitionNum != that.partitionNum) { + if (o == null || getClass() != o.getClass()) { return false; } - return partitions == that.partitions; + NumberedShardSpec that = (NumberedShardSpec) o; + return partitionNum == that.partitionNum && + partitions == that.partitions; } @Override diff --git a/core/src/main/java/org/apache/druid/timeline/partition/OverwriteShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/OverwriteShardSpec.java index 0fea5646e311..6a77ea55286b 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/OverwriteShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/OverwriteShardSpec.java @@ -28,6 +28,17 @@ */ public interface OverwriteShardSpec extends ShardSpec { + /** + * The core partition concept is not used with segment locking. Instead, the {@link AtomicUpdateGroup} is used + * to atomically overshadow segments. Here, we always returns 0 so that the {@link PartitionHolder} skips checking + * the completeness of the core partitions. + */ + @Override + default int getNumCorePartitions() + { + return 0; + } + default OverwriteShardSpec withAtomicUpdateGroupSize(int atomicUpdateGroupSize) { return withAtomicUpdateGroupSize((short) atomicUpdateGroupSize); diff --git a/core/src/main/java/org/apache/druid/timeline/partition/RangeBucketShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/RangeBucketShardSpec.java new file mode 100644 index 000000000000..a329131e7487 --- /dev/null +++ b/core/src/main/java/org/apache/druid/timeline/partition/RangeBucketShardSpec.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.druid.data.input.InputRow; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; + +/** + * See {@link BucketNumberedShardSpec} for how this class is used. + * + * @see BuildingSingleDimensionShardSpec + */ +public class RangeBucketShardSpec implements BucketNumberedShardSpec +{ + public static final String TYPE = "bucket_single_dim"; + + private final int bucketId; + private final String dimension; + @Nullable + private final String start; + @Nullable + private final String end; + + @JsonCreator + public RangeBucketShardSpec( + @JsonProperty("bucketId") int bucketId, + @JsonProperty("dimension") String dimension, + @JsonProperty("start") @Nullable String start, + @JsonProperty("end") @Nullable String end + ) + { + this.bucketId = bucketId; + this.dimension = dimension; + this.start = start; + this.end = end; + } + + @Override + @JsonProperty + public int getBucketId() + { + return bucketId; + } + + @JsonProperty + public String getDimension() + { + return dimension; + } + + @Nullable + @JsonProperty + public String getStart() + { + return start; + } + + @Nullable + @JsonProperty + public String getEnd() + { + return end; + } + + @Override + public BuildingSingleDimensionShardSpec convert(int partitionId) + { + return new BuildingSingleDimensionShardSpec(bucketId, dimension, start, end, partitionId); + } + + @Override + public boolean isInChunk(long timestamp, InputRow inputRow) + { + return SingleDimensionShardSpec.isInChunk(dimension, start, end, inputRow); + } + + @Override + public ShardSpecLookup getLookup(List shardSpecs) + { + return SingleDimensionShardSpec.createLookup(shardSpecs); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RangeBucketShardSpec bucket = (RangeBucketShardSpec) o; + return bucketId == bucket.bucketId && + Objects.equals(dimension, bucket.dimension) && + Objects.equals(start, bucket.start) && + Objects.equals(end, bucket.end); + } + + @Override + public int hashCode() + { + return Objects.hash(bucketId, dimension, start, end); + } + + @Override + public String toString() + { + return "RangeBucket{" + + ", bucketId=" + bucketId + + ", dimension='" + dimension + '\'' + + ", start='" + start + '\'' + + ", end='" + end + '\'' + + '}'; + } +} diff --git a/core/src/main/java/org/apache/druid/timeline/partition/ShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/ShardSpec.java index 06ff8dd6720d..8f59d3959e72 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/ShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/ShardSpec.java @@ -40,7 +40,16 @@ @JsonSubTypes.Type(name = "numbered", value = NumberedShardSpec.class), @JsonSubTypes.Type(name = "hashed", value = HashBasedNumberedShardSpec.class), @JsonSubTypes.Type(name = NumberedOverwriteShardSpec.TYPE, value = NumberedOverwriteShardSpec.class), - @JsonSubTypes.Type(name = BuildingNumberedShardSpec.TYPE, value = BuildingNumberedShardSpec.class) + // BuildingShardSpecs are the shardSpec with missing numCorePartitions, and thus must not be published. + // See BuildingShardSpec for more details. + @JsonSubTypes.Type(name = BuildingNumberedShardSpec.TYPE, value = BuildingNumberedShardSpec.class), + @JsonSubTypes.Type(name = BuildingHashBasedNumberedShardSpec.TYPE, value = BuildingHashBasedNumberedShardSpec.class), + @JsonSubTypes.Type(name = BuildingSingleDimensionShardSpec.TYPE, value = BuildingSingleDimensionShardSpec.class), + // BucketShardSpecs are the shardSpec with missing partitionId and numCorePartitions. + // These shardSpecs must not be used in segment push. + // See BucketShardSpec for more details. + @JsonSubTypes.Type(name = HashBucketShardSpec.TYPE, value = HashBucketShardSpec.class), + @JsonSubTypes.Type(name = RangeBucketShardSpec.TYPE, value = RangeBucketShardSpec.class) }) public interface ShardSpec { @@ -55,6 +64,8 @@ public interface ShardSpec */ int getPartitionNum(); + int getNumCorePartitions(); + /** * Returns the start root partition ID of the atomic update group which this segment belongs to. * @@ -96,7 +107,7 @@ default short getAtomicUpdateGroupSize() } @JsonIgnore - ShardSpecLookup getLookup(List shardSpecs); + ShardSpecLookup getLookup(List shardSpecs); /** * Get dimensions who have possible range for the rows this shard contains. diff --git a/core/src/main/java/org/apache/druid/timeline/partition/SingleDimensionPartialShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/SingleDimensionPartialShardSpec.java index 22ca97d3fe19..e2ebd295c356 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/SingleDimensionPartialShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/SingleDimensionPartialShardSpec.java @@ -87,15 +87,19 @@ public int getNumBuckets() @Override public ShardSpec complete(ObjectMapper objectMapper, @Nullable ShardSpec specOfPreviousMaxPartitionId) { - final int partitionId; - if (specOfPreviousMaxPartitionId != null) { - assert specOfPreviousMaxPartitionId instanceof SingleDimensionShardSpec; - final SingleDimensionShardSpec prevSpec = (SingleDimensionShardSpec) specOfPreviousMaxPartitionId; - partitionId = prevSpec.getPartitionNum() + 1; - } else { - partitionId = 0; - } - return complete(objectMapper, partitionId); + // The shardSpec is created by the Overlord. + // For batch tasks, this code can be executed only with segment locking (forceTimeChunkLock = false). + // In this mode, you can have 2 or more tasks concurrently ingesting into the same time chunk of + // the same datasource. Since there is no restriction for those tasks in segment allocation, the + // allocated IDs for each task can interleave. As a result, the core partition set cannot be + // represented as a range. We always set 0 for the core partition set size if this is an initial segment. + return new SingleDimensionShardSpec( + partitionDimension, + start, + end, + specOfPreviousMaxPartitionId == null ? 0 : specOfPreviousMaxPartitionId.getPartitionNum() + 1, + specOfPreviousMaxPartitionId == null ? 0 : specOfPreviousMaxPartitionId.getNumCorePartitions() + ); } @Override @@ -106,7 +110,8 @@ public ShardSpec complete(ObjectMapper objectMapper, int partitionId) partitionDimension, start, end, - partitionId + partitionId, + 0 ); } diff --git a/core/src/main/java/org/apache/druid/timeline/partition/SingleDimensionShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/SingleDimensionShardSpec.java index 9db390c462fe..1a00534d6ce8 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/SingleDimensionShardSpec.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/SingleDimensionShardSpec.java @@ -38,12 +38,15 @@ */ public class SingleDimensionShardSpec implements ShardSpec { + public static final int UNKNOWN_NUM_CORE_PARTITIONS = -1; + private final String dimension; @Nullable private final String start; @Nullable private final String end; private final int partitionNum; + private final int numCorePartitions; /** * @param dimension partition dimension @@ -56,7 +59,8 @@ public SingleDimensionShardSpec( @JsonProperty("dimension") String dimension, @JsonProperty("start") @Nullable String start, @JsonProperty("end") @Nullable String end, - @JsonProperty("partitionNum") int partitionNum + @JsonProperty("partitionNum") int partitionNum, + @JsonProperty("numCorePartitions") @Nullable Integer numCorePartitions // nullable for backward compatibility ) { Preconditions.checkArgument(partitionNum >= 0, "partitionNum >= 0"); @@ -64,6 +68,18 @@ public SingleDimensionShardSpec( this.start = start; this.end = end; this.partitionNum = partitionNum; + this.numCorePartitions = numCorePartitions == null ? UNKNOWN_NUM_CORE_PARTITIONS : numCorePartitions; + } + + public SingleDimensionShardSpec withNumCorePartitions(int numCorePartitions) + { + return new SingleDimensionShardSpec( + dimension, + start, + end, + partitionNum, + numCorePartitions + ); } @JsonProperty("dimension") @@ -94,7 +110,19 @@ public int getPartitionNum() } @Override - public ShardSpecLookup getLookup(final List shardSpecs) + @JsonProperty + public int getNumCorePartitions() + { + return numCorePartitions; + } + + @Override + public ShardSpecLookup getLookup(final List shardSpecs) + { + return createLookup(shardSpecs); + } + + static ShardSpecLookup createLookup(List shardSpecs) { return (long timestamp, InputRow row) -> { for (ShardSpec spec : shardSpecs) { @@ -146,22 +174,20 @@ public boolean isCompatible(Class other) @Override public PartitionChunk createChunk(T obj) { - return new StringPartitionChunk(start, end, partitionNum, obj); + if (numCorePartitions == UNKNOWN_NUM_CORE_PARTITIONS) { + return new StringPartitionChunk<>(start, end, partitionNum, obj); + } else { + return new NumberedPartitionChunk<>(partitionNum, numCorePartitions, obj); + } } @Override public boolean isInChunk(long timestamp, InputRow inputRow) { - final List values = inputRow.getDimension(dimension); - - if (values == null || values.size() != 1) { - return checkValue(null); - } else { - return checkValue(values.get(0)); - } + return isInChunk(dimension, start, end, inputRow); } - private boolean checkValue(String value) + private static boolean checkValue(@Nullable String start, @Nullable String end, String value) { if (value == null) { return start == null; @@ -175,15 +201,20 @@ private boolean checkValue(String value) (end == null || value.compareTo(end) < 0); } - @Override - public String toString() + public static boolean isInChunk( + String dimension, + @Nullable String start, + @Nullable String end, + InputRow inputRow + ) { - return "SingleDimensionShardSpec{" + - "dimension='" + dimension + '\'' + - ", start='" + start + '\'' + - ", end='" + end + '\'' + - ", partitionNum=" + partitionNum + - '}'; + final List values = inputRow.getDimension(dimension); + + if (values == null || values.size() != 1) { + return checkValue(start, end, null); + } else { + return checkValue(start, end, values.get(0)); + } } @Override @@ -195,16 +226,29 @@ public boolean equals(Object o) if (o == null || getClass() != o.getClass()) { return false; } - SingleDimensionShardSpec that = (SingleDimensionShardSpec) o; - return partitionNum == that.partitionNum && - Objects.equals(dimension, that.dimension) && - Objects.equals(start, that.start) && - Objects.equals(end, that.end); + SingleDimensionShardSpec shardSpec = (SingleDimensionShardSpec) o; + return partitionNum == shardSpec.partitionNum && + numCorePartitions == shardSpec.numCorePartitions && + Objects.equals(dimension, shardSpec.dimension) && + Objects.equals(start, shardSpec.start) && + Objects.equals(end, shardSpec.end); } @Override public int hashCode() { - return Objects.hash(dimension, start, end, partitionNum); + return Objects.hash(dimension, start, end, partitionNum, numCorePartitions); + } + + @Override + public String toString() + { + return "SingleDimensionShardSpec{" + + "dimension='" + dimension + '\'' + + ", start='" + start + '\'' + + ", end='" + end + '\'' + + ", partitionNum=" + partitionNum + + ", numCorePartitions=" + numCorePartitions + + '}'; } } diff --git a/core/src/test/java/org/apache/druid/timeline/DataSegmentTest.java b/core/src/test/java/org/apache/druid/timeline/DataSegmentTest.java index 6d0af3f2892e..c2b0b76e8881 100644 --- a/core/src/test/java/org/apache/druid/timeline/DataSegmentTest.java +++ b/core/src/test/java/org/apache/druid/timeline/DataSegmentTest.java @@ -75,7 +75,13 @@ public int getPartitionNum() } @Override - public ShardSpecLookup getLookup(List shardSpecs) + public int getNumCorePartitions() + { + return 0; + } + + @Override + public ShardSpecLookup getLookup(List shardSpecs) { return null; } diff --git a/core/src/test/java/org/apache/druid/timeline/partition/BuildingHashBasedNumberedShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/BuildingHashBasedNumberedShardSpecTest.java new file mode 100644 index 000000000000..2c052d51c228 --- /dev/null +++ b/core/src/test/java/org/apache/druid/timeline/partition/BuildingHashBasedNumberedShardSpecTest.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.InjectableValues.Std; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.google.common.collect.ImmutableList; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Assert; +import org.junit.Test; + +public class BuildingHashBasedNumberedShardSpecTest +{ + private final ObjectMapper mapper = ShardSpecTestUtils.initObjectMapper(); + + @Test + public void testConvert() + { + Assert.assertEquals( + new HashBasedNumberedShardSpec(5, 10, 5, 12, ImmutableList.of("dim"), mapper), + new BuildingHashBasedNumberedShardSpec(5, 5, 12, ImmutableList.of("dim"), mapper).convert(10) + ); + } + + @Test + public void testCreateChunk() + { + Assert.assertEquals( + new NumberedPartitionChunk<>(5, 0, "test"), + new BuildingHashBasedNumberedShardSpec(5, 5, 12, ImmutableList.of("dim"), mapper) + .createChunk("test") + ); + } + + @Test + public void testSerde() throws JsonProcessingException + { + mapper.registerSubtypes( + new NamedType(BuildingHashBasedNumberedShardSpec.class, BuildingHashBasedNumberedShardSpec.TYPE) + ); + mapper.setInjectableValues(new Std().addValue(ObjectMapper.class, mapper)); + final BuildingHashBasedNumberedShardSpec original = new BuildingHashBasedNumberedShardSpec( + 3, + 5, + 12, + ImmutableList.of("dim"), + mapper + ); + final String json = mapper.writeValueAsString(original); + final BuildingHashBasedNumberedShardSpec fromJson = (BuildingHashBasedNumberedShardSpec) mapper.readValue( + json, + ShardSpec.class + ); + Assert.assertEquals(original, fromJson); + } + + @Test + public void testEquals() + { + EqualsVerifier.forClass(BuildingHashBasedNumberedShardSpec.class) + .withIgnoredFields("jsonMapper") + .withPrefabValues(ObjectMapper.class, new ObjectMapper(), new ObjectMapper()) + .usingGetClass() + .verify(); + } +} diff --git a/core/src/test/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpecTest.java index 21c5a03644e5..b608d4cda730 100644 --- a/core/src/test/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpecTest.java +++ b/core/src/test/java/org/apache/druid/timeline/partition/BuildingNumberedShardSpecTest.java @@ -23,22 +23,16 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.NamedType; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import nl.jqno.equalsverifier.EqualsVerifier; -import org.apache.druid.data.input.MapBasedInputRow; -import org.apache.druid.java.util.common.DateTimes; import org.junit.Assert; import org.junit.Test; -import java.util.List; - public class BuildingNumberedShardSpecTest { @Test - public void testToNumberedShardSpec() + public void testConvert() { - Assert.assertEquals(new NumberedShardSpec(5, 10), new BuildingNumberedShardSpec(5).toNumberedShardSpec(10)); + Assert.assertEquals(new NumberedShardSpec(5, 10), new BuildingNumberedShardSpec(5).convert(10)); } @Test @@ -50,33 +44,10 @@ public void testCreateChunk() ); } - @Test - public void testShardSpecLookup() - { - final List shardSpecs = ImmutableList.of( - new BuildingNumberedShardSpec(1), - new BuildingNumberedShardSpec(2), - new BuildingNumberedShardSpec(3) - ); - final ShardSpecLookup lookup = shardSpecs.get(0).getLookup(shardSpecs); - // Timestamp doesn't matter. It always returns the first shardSpec. - final long currentTime = DateTimes.nowUtc().getMillis(); - Assert.assertEquals( - shardSpecs.get(0), - lookup.getShardSpec( - currentTime, - new MapBasedInputRow( - currentTime, - ImmutableList.of("dim"), ImmutableMap.of("dim", "val", "time", currentTime) - ) - ) - ); - } - @Test public void testSerde() throws JsonProcessingException { - final ObjectMapper mapper = new ObjectMapper(); + final ObjectMapper mapper = ShardSpecTestUtils.initObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.registerSubtypes(new NamedType(BuildingNumberedShardSpec.class, BuildingNumberedShardSpec.TYPE)); final BuildingNumberedShardSpec original = new BuildingNumberedShardSpec(5); diff --git a/core/src/test/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpecTest.java new file mode 100644 index 000000000000..d70a42ff5bae --- /dev/null +++ b/core/src/test/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpecTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.InjectableValues.Std; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Assert; +import org.junit.Test; + +public class BuildingSingleDimensionShardSpecTest +{ + @Test + public void testConvert() + { + Assert.assertEquals( + new SingleDimensionShardSpec("dim", "start", "end", 5, 10), + new BuildingSingleDimensionShardSpec(1, "dim", "start", "end", 5).convert(10) + ); + } + + @Test + public void testCreateChunk() + { + Assert.assertEquals( + new NumberedPartitionChunk<>(5, 0, "test"), + new BuildingSingleDimensionShardSpec(1, "dim", "start", "end", 5).createChunk("test") + ); + } + + @Test + public void testSerde() throws JsonProcessingException + { + final ObjectMapper mapper = ShardSpecTestUtils.initObjectMapper(); + mapper.registerSubtypes( + new NamedType(BuildingSingleDimensionShardSpec.class, BuildingSingleDimensionShardSpec.TYPE) + ); + mapper.setInjectableValues(new Std().addValue(ObjectMapper.class, mapper)); + final BuildingSingleDimensionShardSpec original = new BuildingSingleDimensionShardSpec(1, "dim", "start", "end", 5); + final String json = mapper.writeValueAsString(original); + final BuildingSingleDimensionShardSpec fromJson = (BuildingSingleDimensionShardSpec) mapper.readValue( + json, + ShardSpec.class + ); + Assert.assertEquals(original, fromJson); + } + + @Test + public void testEquals() + { + EqualsVerifier.forClass(BuildingSingleDimensionShardSpec.class).usingGetClass().verify(); + } +} diff --git a/core/src/test/java/org/apache/druid/timeline/partition/HashBasedNumberedPartialShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/HashBasedNumberedPartialShardSpecTest.java index 9b2c664679f3..551992bdb2d6 100644 --- a/core/src/test/java/org/apache/druid/timeline/partition/HashBasedNumberedPartialShardSpecTest.java +++ b/core/src/test/java/org/apache/druid/timeline/partition/HashBasedNumberedPartialShardSpecTest.java @@ -46,6 +46,7 @@ public void testSerde() throws IOException { final HashBasedNumberedPartialShardSpec expected = new HashBasedNumberedPartialShardSpec( ImmutableList.of("dim1", "dim2"), + 1, 3 ); final byte[] json = MAPPER.writeValueAsBytes(expected); @@ -61,14 +62,16 @@ public void testJsonPropertyNames() throws IOException { final HashBasedNumberedPartialShardSpec expected = new HashBasedNumberedPartialShardSpec( ImmutableList.of("dim1", "dim2"), + 1, 3 ); final byte[] json = MAPPER.writeValueAsBytes(expected); //noinspection unchecked final Map map = MAPPER.readValue(json, Map.class); - Assert.assertEquals(3, map.size()); + Assert.assertEquals(4, map.size()); Assert.assertEquals(HashBasedNumberedPartialShardSpec.TYPE, map.get("type")); Assert.assertEquals(expected.getPartitionDimensions(), map.get("partitionDimensions")); + Assert.assertEquals(expected.getBucketId(), map.get("bucketId")); Assert.assertEquals(expected.getNumBuckets(), map.get("numPartitions")); } } diff --git a/core/src/test/java/org/apache/druid/timeline/partition/HashBucketShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/HashBucketShardSpecTest.java new file mode 100644 index 000000000000..df2207b798f4 --- /dev/null +++ b/core/src/test/java/org/apache/druid/timeline/partition/HashBucketShardSpecTest.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.InjectableValues.Std; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.apache.druid.data.input.MapBasedInputRow; +import org.apache.druid.java.util.common.DateTimes; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class HashBucketShardSpecTest +{ + private final ObjectMapper mapper = ShardSpecTestUtils.initObjectMapper(); + + @Test + public void testConvert() + { + Assert.assertEquals( + new BuildingHashBasedNumberedShardSpec(3, 5, 12, ImmutableList.of("dim"), mapper), + new HashBucketShardSpec(5, 12, ImmutableList.of("dim"), mapper).convert(3) + ); + } + + @Test + public void testCreateChunk() + { + Assert.assertEquals( + new NumberedPartitionChunk<>(5, 0, "test"), + new HashBucketShardSpec(5, 12, ImmutableList.of("dim"), mapper).createChunk("test") + ); + } + + @Test + public void testShardSpecLookup() + { + final List shardSpecs = ImmutableList.of( + new HashBucketShardSpec(0, 3, ImmutableList.of("dim"), mapper), + new HashBucketShardSpec(1, 3, ImmutableList.of("dim"), mapper), + new HashBucketShardSpec(2, 3, ImmutableList.of("dim"), mapper) + ); + final ShardSpecLookup lookup = shardSpecs.get(0).getLookup(shardSpecs); + final long currentTime = DateTimes.nowUtc().getMillis(); + Assert.assertEquals( + shardSpecs.get(1), + lookup.getShardSpec( + currentTime, + new MapBasedInputRow( + currentTime, + ImmutableList.of("dim"), ImmutableMap.of("dim", "1", "time", currentTime) + ) + ) + ); + Assert.assertEquals( + shardSpecs.get(2), + lookup.getShardSpec( + currentTime, + new MapBasedInputRow( + currentTime, + ImmutableList.of("dim"), ImmutableMap.of("dim", "2", "time", currentTime) + ) + ) + ); + Assert.assertEquals( + shardSpecs.get(0), + lookup.getShardSpec( + currentTime, + new MapBasedInputRow( + currentTime, + ImmutableList.of("dim"), ImmutableMap.of("dim", "3", "time", currentTime) + ) + ) + ); + } + + @Test + public void testSerde() throws JsonProcessingException + { + mapper.registerSubtypes(new NamedType(HashBucketShardSpec.class, HashBucketShardSpec.TYPE)); + mapper.setInjectableValues(new Std().addValue(ObjectMapper.class, mapper)); + + final HashBucketShardSpec original = new HashBucketShardSpec(5, 12, ImmutableList.of("dim"), mapper); + final String json = mapper.writeValueAsString(original); + final HashBucketShardSpec fromJson = (HashBucketShardSpec) mapper.readValue(json, ShardSpec.class); + Assert.assertEquals(original, fromJson); + } + + @Test + public void testEquals() + { + EqualsVerifier.forClass(HashBucketShardSpec.class) + .withIgnoredFields("jsonMapper") + .withPrefabValues(ObjectMapper.class, new ObjectMapper(), new ObjectMapper()) + .usingGetClass() + .verify(); + } +} diff --git a/core/src/test/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpecTest.java index d605105c48d5..c6d7935b5699 100644 --- a/core/src/test/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpecTest.java +++ b/core/src/test/java/org/apache/druid/timeline/partition/NumberedOverwriteShardSpecTest.java @@ -37,7 +37,7 @@ public void testEquals() @Test public void testSerde() throws JsonProcessingException { - final ObjectMapper mapper = new ObjectMapper(); + final ObjectMapper mapper = ShardSpecTestUtils.initObjectMapper(); mapper.registerSubtypes(new NamedType(NumberedOverwriteShardSpec.class, NumberedOverwriteShardSpec.TYPE)); final NumberedOverwriteShardSpec original = new NumberedOverwriteShardSpec( PartitionIds.NON_ROOT_GEN_START_PARTITION_ID + 2, diff --git a/core/src/test/java/org/apache/druid/timeline/partition/PartitionHolderCompletenessTest.java b/core/src/test/java/org/apache/druid/timeline/partition/PartitionHolderCompletenessTest.java new file mode 100644 index 000000000000..38b9a47854f2 --- /dev/null +++ b/core/src/test/java/org/apache/druid/timeline/partition/PartitionHolderCompletenessTest.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import org.apache.druid.java.util.common.StringUtils; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.List; + +@RunWith(Parameterized.class) +public class PartitionHolderCompletenessTest +{ + @Parameterized.Parameters(name = "{1}") + public static Iterable constructorFeeder() + { + return ImmutableList.of( + new Object[]{ + ImmutableList.of( + new NumberedShardSpec(0, 3), + new NumberedShardSpec(1, 3), + new NumberedShardSpec(2, 3) + ), + NumberedShardSpec.class.getSimpleName() + }, + new Object[]{ + // Simulate empty hash buckets + ImmutableList.of( + new HashBasedNumberedShardSpec(0, 3, 0, 5, null, new ObjectMapper()), + new HashBasedNumberedShardSpec(1, 3, 2, 5, null, new ObjectMapper()), + new HashBasedNumberedShardSpec(2, 3, 3, 5, null, new ObjectMapper()) + ), + HashBasedNumberedShardSpec.class.getSimpleName() + }, + new Object[]{ + // Simulate empty range buckets + ImmutableList.of( + new SingleDimensionShardSpec("dim", null, "aaa", 0, 3), + new SingleDimensionShardSpec("dim", "bbb", "fff", 1, 3), + new SingleDimensionShardSpec("dim", "ttt", "zzz", 2, 3) + ), + StringUtils.format( + "%s with empty buckets", + SingleDimensionShardSpec.class.getSimpleName() + ) + }, + new Object[]{ + // Simulate old format segments with missing numCorePartitions + ImmutableList.of( + new SingleDimensionShardSpec("dim", null, "bbb", 0, null), + new SingleDimensionShardSpec("dim", "bbb", "fff", 1, null), + new SingleDimensionShardSpec("dim", "fff", null, 2, null) + ), + StringUtils.format( + "%s with missing numCorePartitions", + SingleDimensionShardSpec.class.getSimpleName() + ) + } + ); + } + + private final List shardSpecs; + + public PartitionHolderCompletenessTest(List shardSpecs, String paramName) + { + this.shardSpecs = shardSpecs; + } + + @Test + public void testIsComplete() + { + final PartitionHolder holder = new PartitionHolder<>( + shardSpecs.get(0).createChunk(new OvershadowableInteger("version", shardSpecs.get(0).getPartitionNum(), 0)) + ); + for (int i = 0; i < shardSpecs.size() - 1; i++) { + Assert.assertFalse(holder.isComplete()); + final ShardSpec shardSpec = shardSpecs.get(i + 1); + holder.add(shardSpec.createChunk(new OvershadowableInteger("version", shardSpec.getPartitionNum(), 0))); + } + Assert.assertTrue(holder.isComplete()); + } +} diff --git a/core/src/test/java/org/apache/druid/timeline/partition/RangeBucketShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/RangeBucketShardSpecTest.java new file mode 100644 index 000000000000..d2c06e05f3f1 --- /dev/null +++ b/core/src/test/java/org/apache/druid/timeline/partition/RangeBucketShardSpecTest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.InjectableValues.Std; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.NamedType; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.apache.druid.data.input.MapBasedInputRow; +import org.apache.druid.java.util.common.DateTimes; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class RangeBucketShardSpecTest +{ + @Test + public void testConvert() + { + Assert.assertEquals( + new BuildingSingleDimensionShardSpec(1, "dim", "start", "end", 5), + new RangeBucketShardSpec(1, "dim", "start", "end").convert(5) + ); + } + + @Test + public void testCreateChunk() + { + Assert.assertEquals( + new NumberedPartitionChunk<>(1, 0, "test"), + new RangeBucketShardSpec(1, "dim", "start", "end").createChunk("test") + ); + } + + @Test + public void testShardSpecLookup() + { + final List shardSpecs = ImmutableList.of( + new RangeBucketShardSpec(0, "dim", null, "c"), + new RangeBucketShardSpec(1, "dim", "f", "i"), + new RangeBucketShardSpec(2, "dim", "i", null) + ); + final ShardSpecLookup lookup = shardSpecs.get(0).getLookup(shardSpecs); + final long currentTime = DateTimes.nowUtc().getMillis(); + Assert.assertEquals( + shardSpecs.get(0), + lookup.getShardSpec( + currentTime, + new MapBasedInputRow( + currentTime, + ImmutableList.of("dim"), ImmutableMap.of("dim", "a", "time", currentTime) + ) + ) + ); + Assert.assertEquals( + shardSpecs.get(1), + lookup.getShardSpec( + currentTime, + new MapBasedInputRow( + currentTime, + ImmutableList.of("dim"), ImmutableMap.of("dim", "g", "time", currentTime) + ) + ) + ); + Assert.assertEquals( + shardSpecs.get(2), + lookup.getShardSpec( + currentTime, + new MapBasedInputRow( + currentTime, + ImmutableList.of("dim"), ImmutableMap.of("dim", "k", "time", currentTime) + ) + ) + ); + } + + @Test + public void testSerde() throws JsonProcessingException + { + final ObjectMapper mapper = ShardSpecTestUtils.initObjectMapper(); + mapper.registerSubtypes(new NamedType(RangeBucketShardSpec.class, RangeBucketShardSpec.TYPE)); + mapper.setInjectableValues(new Std().addValue(ObjectMapper.class, mapper)); + final RangeBucketShardSpec original = new RangeBucketShardSpec(1, "dim", "start", "end"); + final String json = mapper.writeValueAsString(original); + final RangeBucketShardSpec fromJson = (RangeBucketShardSpec) mapper.readValue(json, ShardSpec.class); + Assert.assertEquals(original, fromJson); + } + + @Test + public void testEquals() + { + EqualsVerifier.forClass(RangeBucketShardSpec.class).usingGetClass().verify(); + } +} diff --git a/core/src/test/java/org/apache/druid/timeline/partition/ShardSpecTestUtils.java b/core/src/test/java/org/apache/druid/timeline/partition/ShardSpecTestUtils.java new file mode 100644 index 000000000000..2f153651513b --- /dev/null +++ b/core/src/test/java/org/apache/druid/timeline/partition/ShardSpecTestUtils.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.timeline.partition; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +public class ShardSpecTestUtils +{ + public static ObjectMapper initObjectMapper() + { + // Copied configurations from org.apache.druid.jackson.DefaultObjectMapper + final ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(MapperFeature.AUTO_DETECT_GETTERS, false); + // See https://github.com/FasterXML/jackson-databind/issues/170 + // configure(MapperFeature.AUTO_DETECT_CREATORS, false); + mapper.configure(MapperFeature.AUTO_DETECT_FIELDS, false); + mapper.configure(MapperFeature.AUTO_DETECT_IS_GETTERS, false); + mapper.configure(MapperFeature.AUTO_DETECT_SETTERS, false); + mapper.configure(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS, false); + mapper.configure(SerializationFeature.INDENT_OUTPUT, false); + mapper.configure(SerializationFeature.FLUSH_AFTER_WRITE_VALUE, false); + return mapper; + } + + private ShardSpecTestUtils() + { + } +} diff --git a/docs/ingestion/native-batch.md b/docs/ingestion/native-batch.md index 981f9f700938..799b32e0fbad 100644 --- a/docs/ingestion/native-batch.md +++ b/docs/ingestion/native-batch.md @@ -302,7 +302,7 @@ and then by the hash value of `partitionDimensions` (secondary partition key) in The partitioned data is stored in local storage of the [middleManager](../design/middlemanager.md) or the [indexer](../design/indexer.md). - The `partial segment merge` phase is similar to the Reduce phase in MapReduce. -The Parallel task spawns a new set of worker tasks (type `partial_index_merge`) to merge the partitioned data +The Parallel task spawns a new set of worker tasks (type `partial_index_generic_merge`) to merge the partitioned data created in the previous phase. Here, the partitioned data is shuffled based on the time chunk and the hash value of `partitionDimensions` to be merged; each worker task reads the data falling in the same time chunk and the same hash value from multiple MiddleManager/Indexer processes and merges diff --git a/extensions-contrib/materialized-view-maintenance/src/test/java/org/apache/druid/indexing/materializedview/MaterializedViewSupervisorTest.java b/extensions-contrib/materialized-view-maintenance/src/test/java/org/apache/druid/indexing/materializedview/MaterializedViewSupervisorTest.java index ec276795ddf7..766f5109269e 100644 --- a/extensions-contrib/materialized-view-maintenance/src/test/java/org/apache/druid/indexing/materializedview/MaterializedViewSupervisorTest.java +++ b/extensions-contrib/materialized-view-maintenance/src/test/java/org/apache/druid/indexing/materializedview/MaterializedViewSupervisorTest.java @@ -140,7 +140,7 @@ public void testCheckSegments() throws IOException ImmutableMap.of(), ImmutableList.of("dim1", "dim2"), ImmutableList.of("m1"), - new HashBasedNumberedShardSpec(0, 1, null, null), + new HashBasedNumberedShardSpec(0, 1, 0, 1, null, null), 9, 1024 ), @@ -151,7 +151,7 @@ public void testCheckSegments() throws IOException ImmutableMap.of(), ImmutableList.of("dim1", "dim2"), ImmutableList.of("m1"), - new HashBasedNumberedShardSpec(0, 1, null, null), + new HashBasedNumberedShardSpec(0, 1, 0, 1, null, null), 9, 1024 ), @@ -162,7 +162,7 @@ public void testCheckSegments() throws IOException ImmutableMap.of(), ImmutableList.of("dim1", "dim2"), ImmutableList.of("m1"), - new HashBasedNumberedShardSpec(0, 1, null, null), + new HashBasedNumberedShardSpec(0, 1, 0, 1, null, null), 9, 1024 ) @@ -175,7 +175,7 @@ public void testCheckSegments() throws IOException ImmutableMap.of(), ImmutableList.of("dim1", "dim2"), ImmutableList.of("m1"), - new HashBasedNumberedShardSpec(0, 1, null, null), + new HashBasedNumberedShardSpec(0, 1, 0, 1, null, null), 9, 1024 ), @@ -186,7 +186,7 @@ public void testCheckSegments() throws IOException ImmutableMap.of(), ImmutableList.of("dim1", "dim2"), ImmutableList.of("m1"), - new HashBasedNumberedShardSpec(0, 1, null, null), + new HashBasedNumberedShardSpec(0, 1, 0, 1, null, null), 9, 1024 ) @@ -209,7 +209,7 @@ public void testCheckSegments() throws IOException ImmutableMap.of(), ImmutableList.of("dim1", "dim2"), ImmutableList.of("m1"), - new HashBasedNumberedShardSpec(0, 1, null, null), + new HashBasedNumberedShardSpec(0, 1, 0, 1, null, null), 9, 1024 ) @@ -225,7 +225,7 @@ public void testCheckSegments() throws IOException ImmutableMap.of(), ImmutableList.of("dim1", "dim2"), ImmutableList.of("m1"), - new HashBasedNumberedShardSpec(0, 1, null, null), + new HashBasedNumberedShardSpec(0, 1, 0, 1, null, null), 9, 1024 ) @@ -246,7 +246,7 @@ public void testCheckSegmentsAndSubmitTasks() throws IOException ImmutableMap.of(), ImmutableList.of("dim1", "dim2"), ImmutableList.of("m1"), - new HashBasedNumberedShardSpec(0, 1, null, null), + new HashBasedNumberedShardSpec(0, 1, 0, 1, null, null), 9, 1024 ) diff --git a/indexing-hadoop/src/main/java/org/apache/druid/indexer/DetermineHashedPartitionsJob.java b/indexing-hadoop/src/main/java/org/apache/druid/indexer/DetermineHashedPartitionsJob.java index 9f20f171fb7b..62a13d3f9fc4 100644 --- a/indexing-hadoop/src/main/java/org/apache/druid/indexer/DetermineHashedPartitionsJob.java +++ b/indexing-hadoop/src/main/java/org/apache/druid/indexer/DetermineHashedPartitionsJob.java @@ -194,6 +194,8 @@ public boolean run() actualSpecs.add( new HadoopyShardSpec( new HashBasedNumberedShardSpec( + i, + numberOfShards, i, numberOfShards, null, diff --git a/indexing-hadoop/src/main/java/org/apache/druid/indexer/DeterminePartitionsJob.java b/indexing-hadoop/src/main/java/org/apache/druid/indexer/DeterminePartitionsJob.java index d9e6a14ce56a..0b246be384b6 100644 --- a/indexing-hadoop/src/main/java/org/apache/druid/indexer/DeterminePartitionsJob.java +++ b/indexing-hadoop/src/main/java/org/apache/druid/indexer/DeterminePartitionsJob.java @@ -666,7 +666,10 @@ protected void innerReduce(Context context, SortableBytes keyBytes, Iterable constructShardSpecFromShardInfo(String partitionType, Ob if ("hashed".equals(partitionType)) { for (Integer[] shardInfo : (Integer[][]) shardInfoForEachShard) { specs.add(new HashBasedNumberedShardSpec( + shardInfo[0], + shardInfo[1], shardInfo[0], shardInfo[1], null, @@ -573,7 +575,8 @@ private List constructShardSpecFromShardInfo(String partitionType, Ob "host", shardInfo[0], shardInfo[1], - partitionNum++ + partitionNum++, + shardInfoForEachShard.length )); } } else { @@ -693,12 +696,12 @@ private void verifyJob(IndexGeneratorJob job) throws IOException if (forceExtendableShardSpecs) { NumberedShardSpec spec = (NumberedShardSpec) dataSegment.getShardSpec(); Assert.assertEquals(i, spec.getPartitionNum()); - Assert.assertEquals(shardInfo.length, spec.getPartitions()); + Assert.assertEquals(shardInfo.length, spec.getNumCorePartitions()); } else if ("hashed".equals(partitionType)) { Integer[] hashShardInfo = (Integer[]) shardInfo[i]; HashBasedNumberedShardSpec spec = (HashBasedNumberedShardSpec) dataSegment.getShardSpec(); Assert.assertEquals((int) hashShardInfo[0], spec.getPartitionNum()); - Assert.assertEquals((int) hashShardInfo[1], spec.getPartitions()); + Assert.assertEquals((int) hashShardInfo[1], spec.getNumCorePartitions()); } else if ("single".equals(partitionType)) { String[] singleDimensionShardInfo = (String[]) shardInfo[i]; SingleDimensionShardSpec spec = (SingleDimensionShardSpec) dataSegment.getShardSpec(); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingLocalSegmentAllocator.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingLocalSegmentAllocator.java index 9040c9acd3f9..0ad2e99435a5 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingLocalSegmentAllocator.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingLocalSegmentAllocator.java @@ -20,6 +20,8 @@ package org.apache.druid.indexing.common.task; import com.google.common.base.Preconditions; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import org.apache.druid.data.input.InputRow; import org.apache.druid.indexing.common.TaskLock; import org.apache.druid.indexing.common.TaskToolbox; @@ -27,15 +29,17 @@ import org.apache.druid.indexing.common.actions.SurrogateAction; import org.apache.druid.indexing.common.actions.TaskAction; import org.apache.druid.indexing.common.task.batch.parallel.SupervisorTaskAccess; +import org.apache.druid.indexing.common.task.batch.partition.CompletePartitionAnalysis; import org.apache.druid.java.util.common.ISE; -import org.apache.druid.java.util.common.granularity.Granularity; +import org.apache.druid.java.util.common.Pair; +import org.apache.druid.segment.indexing.granularity.GranularitySpec; import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; import org.apache.druid.timeline.partition.ShardSpec; import org.joda.time.Interval; import javax.annotation.Nullable; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -46,74 +50,70 @@ /** * Allocates all necessary segments locally at the beginning and reuses them. */ -public class CachingLocalSegmentAllocator implements CachingSegmentAllocator +public class CachingLocalSegmentAllocator implements SegmentAllocatorForBatch { - private final String taskId; - private final Map sequenceNameToSegmentId; - private final ShardSpecs shardSpecs; + private final String dataSource; + private final Map> sequenceNameToBucket; + private final Function versionFinder; + private final NonLinearlyPartitionedSequenceNameFunction sequenceNameFunction; + private final boolean isParallel; - @FunctionalInterface - interface IntervalToSegmentIdsCreator - { - /** - * @param versionFinder Returns the version for the specified interval - * - * @return Information for segment preallocation - */ - Map> create( - TaskToolbox toolbox, - String dataSource, - Function versionFinder - ); - } + private final Map sequenceNameToSegmentId = new HashMap<>(); + private final Object2IntMap intervalToNextPartitionId = new Object2IntOpenHashMap<>(); CachingLocalSegmentAllocator( TaskToolbox toolbox, String dataSource, String taskId, - Granularity queryGranularity, + GranularitySpec granularitySpec, @Nullable SupervisorTaskAccess supervisorTaskAccess, - IntervalToSegmentIdsCreator intervalToSegmentIdsCreator + CompletePartitionAnalysis partitionAnalysis ) throws IOException { - this.taskId = taskId; - this.sequenceNameToSegmentId = new HashMap<>(); + this.dataSource = dataSource; + this.sequenceNameToBucket = new HashMap<>(); final TaskAction> action; if (supervisorTaskAccess == null) { action = new LockListAction(); + isParallel = false; } else { action = new SurrogateAction<>(supervisorTaskAccess.getSupervisorTaskId(), new LockListAction()); + isParallel = true; } - final Map intervalToVersion = - toolbox.getTaskActionClient() - .submit(action) - .stream() - .collect(Collectors.toMap( - TaskLock::getInterval, - TaskLock::getVersion - )); - Function versionFinder = interval -> findVersion(intervalToVersion, interval); - - final Map> intervalToIds = intervalToSegmentIdsCreator.create( - toolbox, - dataSource, - versionFinder + this.versionFinder = createVersionFinder(toolbox, action); + final Map>> intervalToShardSpecs = partitionAnalysis.createBuckets( + toolbox + ); + + sequenceNameFunction = new NonLinearlyPartitionedSequenceNameFunction( + taskId, + new ShardSpecs(intervalToShardSpecs, granularitySpec.getQueryGranularity()) ); - final Map> shardSpecMap = new HashMap<>(); - for (Entry> entry : intervalToIds.entrySet()) { + for (Entry>> entry : intervalToShardSpecs.entrySet()) { final Interval interval = entry.getKey(); - final List idsPerInterval = intervalToIds.get(interval); + final List> buckets = entry.getValue(); - for (SegmentIdWithShardSpec segmentIdentifier : idsPerInterval) { - shardSpecMap.computeIfAbsent(interval, k -> new ArrayList<>()).add(segmentIdentifier.getShardSpec()); - // The shardSpecs for partitioning and publishing can be different if isExtendableShardSpecs = true. - sequenceNameToSegmentId.put(getSequenceName(interval, segmentIdentifier.getShardSpec()), segmentIdentifier); - } + buckets.forEach(bucket -> { + sequenceNameToBucket.put(sequenceNameFunction.getSequenceName(interval, bucket), Pair.of(interval, bucket)); + }); } - shardSpecs = new ShardSpecs(shardSpecMap, queryGranularity); + } + + static Function createVersionFinder( + TaskToolbox toolbox, + TaskAction> lockListAction + ) throws IOException + { + final Map intervalToVersion = + toolbox.getTaskActionClient() + .submit(lockListAction) + .stream() + .collect(Collectors.toMap(TaskLock::getInterval, TaskLock::getVersion)); + + return interval -> findVersion(intervalToVersion, interval); } private static String findVersion(Map intervalToVersion, Interval interval) @@ -133,28 +133,36 @@ public SegmentIdWithShardSpec allocate( boolean skipSegmentLineageCheck ) { - return Preconditions.checkNotNull( - sequenceNameToSegmentId.get(sequenceName), - "Missing segmentId for the sequence[%s]", - sequenceName + return sequenceNameToSegmentId.computeIfAbsent( + sequenceName, + k -> { + final Pair pair = Preconditions.checkNotNull( + sequenceNameToBucket.get(sequenceName), + "Missing bucket for sequence[%s]", + sequenceName + ); + final Interval interval = pair.lhs; + // Determines the partitionId if this segment allocator is used by the single-threaded task. + // In parallel ingestion, the partitionId is determined in the supervisor task. + // See ParallelIndexSupervisorTask.groupGenericPartitionLocationsPerPartition(). + // This code... isn't pretty, but should be simple enough to understand. + final ShardSpec shardSpec = isParallel + ? pair.rhs + : pair.rhs.convert( + intervalToNextPartitionId.computeInt( + interval, + (i, nextPartitionId) -> nextPartitionId == null ? 0 : nextPartitionId + 1 + ) + ); + final String version = versionFinder.apply(interval); + return new SegmentIdWithShardSpec(dataSource, interval, version, shardSpec); + } ); } - /** - * Create a sequence name from the given shardSpec and interval. - * - * See {@link org.apache.druid.timeline.partition.HashBasedNumberedShardSpec} as an example of partitioning. - */ - private String getSequenceName(Interval interval, ShardSpec shardSpec) - { - // Note: We do not use String format here since this can be called in a tight loop - // and it's faster to add strings together than it is to use String#format - return taskId + "_" + interval + "_" + shardSpec.getPartitionNum(); - } - @Override - public ShardSpecs getShardSpecs() + public SequenceNameFunction getSequenceNameFunction() { - return shardSpecs; + return sequenceNameFunction; } } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/IndexTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/IndexTask.java index 14d806b54c88..89d69604be26 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/IndexTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/IndexTask.java @@ -97,7 +97,6 @@ import org.apache.druid.segment.realtime.appenderator.AppenderatorsManager; import org.apache.druid.segment.realtime.appenderator.BaseAppenderatorDriver; import org.apache.druid.segment.realtime.appenderator.BatchAppenderatorDriver; -import org.apache.druid.segment.realtime.appenderator.SegmentAllocator; import org.apache.druid.segment.realtime.appenderator.SegmentsAndCommitMetadata; import org.apache.druid.segment.realtime.appenderator.TransactionalSegmentPublisher; import org.apache.druid.segment.realtime.firehose.ChatHandler; @@ -877,35 +876,33 @@ private TaskStatus generateAndPublishSegments( final IndexTuningConfig tuningConfig = ingestionSchema.getTuningConfig(); final long pushTimeout = tuningConfig.getPushTimeout(); - final SegmentAllocator segmentAllocator; + final SegmentAllocatorForBatch segmentAllocator; final SequenceNameFunction sequenceNameFunction; switch (partitionsSpec.getType()) { case HASH: case RANGE: - final CachingSegmentAllocator localSegmentAllocator = SegmentAllocators.forNonLinearPartitioning( + final SegmentAllocatorForBatch localSegmentAllocator = SegmentAllocators.forNonLinearPartitioning( toolbox, getDataSource(), getId(), - dataSchema.getGranularitySpec().getQueryGranularity(), + dataSchema.getGranularitySpec(), null, (CompletePartitionAnalysis) partitionAnalysis ); - sequenceNameFunction = new NonLinearlyPartitionedSequenceNameFunction( - getId(), - localSegmentAllocator.getShardSpecs() - ); + sequenceNameFunction = localSegmentAllocator.getSequenceNameFunction(); segmentAllocator = localSegmentAllocator; break; case LINEAR: segmentAllocator = SegmentAllocators.forLinearPartitioning( toolbox, + getId(), null, dataSchema, getTaskLockHelper(), ingestionSchema.getIOConfig().isAppendToExisting(), partitionAnalysis.getPartitionsSpec() ); - sequenceNameFunction = new LinearlyPartitionedSequenceNameFunction(getId()); + sequenceNameFunction = segmentAllocator.getSequenceNameFunction(); break; default: throw new UOE("[%s] secondary partition type is not supported", partitionsSpec.getType()); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/LocalSegmentAllocator.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/LocalSegmentAllocator.java index 2cb4db551c4a..c2488e6244ba 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/LocalSegmentAllocator.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/LocalSegmentAllocator.java @@ -43,11 +43,12 @@ /** * Segment allocator which allocates new segments locally per request. */ -class LocalSegmentAllocator implements SegmentAllocator +class LocalSegmentAllocator implements SegmentAllocatorForBatch { private final SegmentAllocator internalAllocator; + private final SequenceNameFunction sequenceNameFunction; - LocalSegmentAllocator(TaskToolbox toolbox, String dataSource, GranularitySpec granularitySpec) throws IOException + LocalSegmentAllocator(TaskToolbox toolbox, String taskId, String dataSource, GranularitySpec granularitySpec) throws IOException { final Map intervalToVersion = toolbox .getTaskActionClient() @@ -80,6 +81,7 @@ class LocalSegmentAllocator implements SegmentAllocator new BuildingNumberedShardSpec(partitionId) ); }; + sequenceNameFunction = new LinearlyPartitionedSequenceNameFunction(taskId); } @Nullable @@ -93,4 +95,10 @@ public SegmentIdWithShardSpec allocate( { return internalAllocator.allocate(row, sequenceName, previousSegmentId, skipSegmentLineageCheck); } + + @Override + public SequenceNameFunction getSequenceNameFunction() + { + return sequenceNameFunction; + } } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/NonLinearlyPartitionedSequenceNameFunction.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/NonLinearlyPartitionedSequenceNameFunction.java index c34aa2995983..44fd520f913b 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/NonLinearlyPartitionedSequenceNameFunction.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/NonLinearlyPartitionedSequenceNameFunction.java @@ -20,7 +20,7 @@ package org.apache.druid.indexing.common.task; import org.apache.druid.data.input.InputRow; -import org.apache.druid.timeline.partition.ShardSpec; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; import org.joda.time.Interval; /** @@ -30,7 +30,6 @@ * Note that all segment IDs should be allocated upfront to use this function. * * @see org.apache.druid.indexer.partitions.SecondaryPartitionType - * @see CachingSegmentAllocator */ public class NonLinearlyPartitionedSequenceNameFunction implements SequenceNameFunction { @@ -55,10 +54,10 @@ public String getSequenceName(Interval interval, InputRow inputRow) * * See {@link org.apache.druid.timeline.partition.HashBasedNumberedShardSpec} as an example of partitioning. */ - private String getSequenceName(Interval interval, ShardSpec shardSpec) + public String getSequenceName(Interval interval, BucketNumberedShardSpec bucket) { // Note: We do not use String format here since this can be called in a tight loop // and it's faster to add strings together than it is to use String#format - return taskId + "_" + interval + "_" + shardSpec.getPartitionNum(); + return taskId + "_" + interval + "_" + bucket.getBucketId(); } } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/OverlordCoordinatingSegmentAllocator.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/OverlordCoordinatingSegmentAllocator.java index 1598dee0c301..87daaa865506 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/OverlordCoordinatingSegmentAllocator.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/OverlordCoordinatingSegmentAllocator.java @@ -31,7 +31,6 @@ import org.apache.druid.java.util.common.ISE; import org.apache.druid.segment.indexing.DataSchema; import org.apache.druid.segment.indexing.granularity.GranularitySpec; -import org.apache.druid.segment.realtime.appenderator.SegmentAllocator; import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; import org.apache.druid.timeline.partition.NumberedOverwritePartialShardSpec; import org.apache.druid.timeline.partition.NumberedPartialShardSpec; @@ -44,12 +43,14 @@ /** * Segment allocator which allocates new segments using the overlord per request. */ -public class OverlordCoordinatingSegmentAllocator implements SegmentAllocator +public class OverlordCoordinatingSegmentAllocator implements SegmentAllocatorForBatch { private final ActionBasedSegmentAllocator internalAllocator; + private final LinearlyPartitionedSequenceNameFunction sequenceNameFunction; OverlordCoordinatingSegmentAllocator( final TaskToolbox toolbox, + final String taskId, final @Nullable SupervisorTaskAccess supervisorTaskAccess, final DataSchema dataSchema, final TaskLockHelper taskLockHelper, @@ -101,6 +102,7 @@ public class OverlordCoordinatingSegmentAllocator implements SegmentAllocator } } ); + this.sequenceNameFunction = new LinearlyPartitionedSequenceNameFunction(taskId); } @Nullable @@ -146,4 +148,10 @@ private static PartialShardSpec createPartialShardSpec( ); } } + + @Override + public SequenceNameFunction getSequenceNameFunction() + { + return sequenceNameFunction; + } } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingSegmentAllocator.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SegmentAllocatorForBatch.java similarity index 57% rename from indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingSegmentAllocator.java rename to indexing-service/src/main/java/org/apache/druid/indexing/common/task/SegmentAllocatorForBatch.java index 176d45e2d2c4..f2bf5030a87f 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/CachingSegmentAllocator.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SegmentAllocatorForBatch.java @@ -22,17 +22,13 @@ import org.apache.druid.segment.realtime.appenderator.SegmentAllocator; /** - * SegmentAllocator that allocates all necessary segments upfront. This allocator should be used for the hash or range - * secondary partitioning. * - * In the hash or range secondary partitioning, the information about all partition buckets should be known before - * the task starts to allocate segments. For example, for the hash partitioning, the task should know how many hash - * buckets it will create, what is the hash value allocated for each bucket, etc. Similar for the range partitioning. */ -public interface CachingSegmentAllocator extends SegmentAllocator +public interface SegmentAllocatorForBatch extends SegmentAllocator { /** - * Returns the {@link org.apache.druid.timeline.partition.ShardSpec}s of all segments allocated upfront. + * + * @return */ - ShardSpecs getShardSpecs(); + SequenceNameFunction getSequenceNameFunction(); } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SegmentAllocators.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SegmentAllocators.java index de88c68f65a6..47df3f235b7b 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SegmentAllocators.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SegmentAllocators.java @@ -23,8 +23,8 @@ import org.apache.druid.indexing.common.TaskToolbox; import org.apache.druid.indexing.common.task.batch.parallel.SupervisorTaskAccess; import org.apache.druid.indexing.common.task.batch.partition.CompletePartitionAnalysis; -import org.apache.druid.java.util.common.granularity.Granularity; import org.apache.druid.segment.indexing.DataSchema; +import org.apache.druid.segment.indexing.granularity.GranularitySpec; import org.apache.druid.segment.realtime.appenderator.SegmentAllocator; import javax.annotation.Nullable; @@ -36,8 +36,9 @@ public final class SegmentAllocators * Creates a new {@link SegmentAllocator} for the linear partitioning. * supervisorTaskAccess can be null if this method is called by the {@link IndexTask}. */ - public static SegmentAllocator forLinearPartitioning( + public static SegmentAllocatorForBatch forLinearPartitioning( final TaskToolbox toolbox, + final String taskId, final @Nullable SupervisorTaskAccess supervisorTaskAccess, final DataSchema dataSchema, final TaskLockHelper taskLockHelper, @@ -48,6 +49,7 @@ public static SegmentAllocator forLinearPartitioning( if (appendToExisting || taskLockHelper.isUseSegmentLock()) { return new OverlordCoordinatingSegmentAllocator( toolbox, + taskId, supervisorTaskAccess, dataSchema, taskLockHelper, @@ -58,12 +60,14 @@ public static SegmentAllocator forLinearPartitioning( if (supervisorTaskAccess == null) { return new LocalSegmentAllocator( toolbox, + taskId, dataSchema.getDataSource(), dataSchema.getGranularitySpec() ); } else { return new SupervisorTaskCoordinatingSegmentAllocator( supervisorTaskAccess.getSupervisorTaskId(), + taskId, supervisorTaskAccess.getTaskClient() ); } @@ -74,11 +78,11 @@ public static SegmentAllocator forLinearPartitioning( * Creates a new {@link SegmentAllocator} for the hash and range partitioning. * supervisorTaskAccess can be null if this method is called by the {@link IndexTask}. */ - public static CachingSegmentAllocator forNonLinearPartitioning( + public static SegmentAllocatorForBatch forNonLinearPartitioning( final TaskToolbox toolbox, final String dataSource, final String taskId, - final Granularity queryGranularity, + final GranularitySpec granularitySpec, final @Nullable SupervisorTaskAccess supervisorTaskAccess, final CompletePartitionAnalysis partitionAnalysis ) throws IOException @@ -87,9 +91,9 @@ public static CachingSegmentAllocator forNonLinearPartitioning( toolbox, dataSource, taskId, - queryGranularity, + granularitySpec, supervisorTaskAccess, - partitionAnalysis::convertToIntervalToSegmentIds + partitionAnalysis ); } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/ShardSpecs.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/ShardSpecs.java index 42f7ce1cd531..3db4beba2735 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/ShardSpecs.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/ShardSpecs.java @@ -22,7 +22,7 @@ import org.apache.druid.data.input.InputRow; import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.granularity.Granularity; -import org.apache.druid.timeline.partition.ShardSpec; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; import org.joda.time.Interval; import java.util.List; @@ -33,10 +33,10 @@ */ public class ShardSpecs { - private final Map> map; - private Granularity queryGranularity; + private final Map>> map; + private final Granularity queryGranularity; - ShardSpecs(final Map> map, Granularity queryGranularity) + ShardSpecs(final Map>> map, Granularity queryGranularity) { this.map = map; this.queryGranularity = queryGranularity; @@ -50,13 +50,13 @@ public class ShardSpecs * * @return a shardSpec */ - ShardSpec getShardSpec(Interval interval, InputRow row) + BucketNumberedShardSpec getShardSpec(Interval interval, InputRow row) { - final List shardSpecs = map.get(interval); + final List> shardSpecs = map.get(interval); if (shardSpecs == null || shardSpecs.isEmpty()) { throw new ISE("Failed to get shardSpec for interval[%s]", interval); } final long truncatedTimestamp = queryGranularity.bucketStart(row.getTimestamp()).getMillis(); - return shardSpecs.get(0).getLookup(shardSpecs).getShardSpec(truncatedTimestamp, row); + return (BucketNumberedShardSpec) shardSpecs.get(0).getLookup(shardSpecs).getShardSpec(truncatedTimestamp, row); } } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SupervisorTaskCoordinatingSegmentAllocator.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SupervisorTaskCoordinatingSegmentAllocator.java index 7fde4b879d00..d0956aadbc8f 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SupervisorTaskCoordinatingSegmentAllocator.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/SupervisorTaskCoordinatingSegmentAllocator.java @@ -21,7 +21,6 @@ import org.apache.druid.data.input.InputRow; import org.apache.druid.indexing.common.task.batch.parallel.ParallelIndexSupervisorTaskClient; -import org.apache.druid.segment.realtime.appenderator.SegmentAllocator; import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; import java.io.IOException; @@ -29,18 +28,21 @@ /** * Segment allocator that allocates new segments using the supervisor task per request. */ -public class SupervisorTaskCoordinatingSegmentAllocator implements SegmentAllocator +public class SupervisorTaskCoordinatingSegmentAllocator implements SegmentAllocatorForBatch { private final String supervisorTaskId; private final ParallelIndexSupervisorTaskClient taskClient; + private final SequenceNameFunction sequenceNameFunction; SupervisorTaskCoordinatingSegmentAllocator( String supervisorTaskId, + String taskId, ParallelIndexSupervisorTaskClient taskClient ) { this.supervisorTaskId = supervisorTaskId; this.taskClient = taskClient; + this.sequenceNameFunction = new LinearlyPartitionedSequenceNameFunction(taskId); } @Override @@ -53,4 +55,10 @@ public SegmentIdWithShardSpec allocate( { return taskClient.allocateSegment(supervisorTaskId, row.getTimestamp()); } + + @Override + public SequenceNameFunction getSequenceNameFunction() + { + return sequenceNameFunction; + } } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/Task.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/Task.java index 4f18c81bc7c7..20a7da6e8be1 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/Task.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/Task.java @@ -31,7 +31,6 @@ import org.apache.druid.indexing.common.task.batch.parallel.PartialDimensionDistributionTask; import org.apache.druid.indexing.common.task.batch.parallel.PartialGenericSegmentMergeTask; import org.apache.druid.indexing.common.task.batch.parallel.PartialHashSegmentGenerateTask; -import org.apache.druid.indexing.common.task.batch.parallel.PartialHashSegmentMergeTask; import org.apache.druid.indexing.common.task.batch.parallel.PartialRangeSegmentGenerateTask; import org.apache.druid.indexing.common.task.batch.parallel.SinglePhaseSubTask; import org.apache.druid.query.Query; @@ -63,7 +62,6 @@ // for backward compatibility @Type(name = SinglePhaseSubTask.OLD_TYPE_NAME, value = LegacySinglePhaseSubTask.class), @Type(name = PartialHashSegmentGenerateTask.TYPE, value = PartialHashSegmentGenerateTask.class), - @Type(name = PartialHashSegmentMergeTask.TYPE, value = PartialHashSegmentMergeTask.class), @Type(name = PartialRangeSegmentGenerateTask.TYPE, value = PartialRangeSegmentGenerateTask.class), @Type(name = PartialDimensionDistributionTask.TYPE, value = PartialDimensionDistributionTask.class), @Type(name = PartialGenericSegmentMergeTask.TYPE, value = PartialGenericSegmentMergeTask.class), diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GeneratedHashPartitionsReport.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GeneratedHashPartitionsReport.java deleted file mode 100644 index 85574b7b254b..000000000000 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GeneratedHashPartitionsReport.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; - -/** - * Report containing the {@link HashPartitionStat}s created by a {@link PartialHashSegmentGenerateTask}. - * This report is collected by {@link ParallelIndexSupervisorTask} and - * used to generate {@link PartialHashSegmentMergeIOConfig}. - */ -class GeneratedHashPartitionsReport extends GeneratedPartitionsReport implements SubTaskReport -{ - public static final String TYPE = "generated_partitions"; - - @JsonCreator - GeneratedHashPartitionsReport( - @JsonProperty("taskId") String taskId, - @JsonProperty("partitionStats") List partitionStats - ) - { - super(taskId, partitionStats); - } -} diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionLocation.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionLocation.java index bbfd1e2ddfa2..74c4c1738b0d 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionLocation.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionLocation.java @@ -22,14 +22,14 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import org.apache.druid.timeline.partition.ShardSpec; +import org.apache.druid.timeline.partition.BuildingShardSpec; import org.joda.time.Interval; /** * This class represents the intermediary data server where the partition of {@code interval} and {@code shardSpec} * is stored. */ -public class GenericPartitionLocation extends PartitionLocation +public class GenericPartitionLocation extends PartitionLocation { @JsonCreator public GenericPartitionLocation( @@ -38,7 +38,7 @@ public GenericPartitionLocation( @JsonProperty("useHttps") boolean useHttps, @JsonProperty("subTaskId") String subTaskId, @JsonProperty("interval") Interval interval, - @JsonProperty("shardSpec") ShardSpec shardSpec + @JsonProperty("shardSpec") BuildingShardSpec shardSpec ) { super(host, port, useHttps, subTaskId, interval, shardSpec); @@ -46,13 +46,13 @@ public GenericPartitionLocation( @JsonIgnore @Override - public int getPartitionId() + public int getBucketId() { - return getSecondaryPartition().getPartitionNum(); + return getSecondaryPartition().getBucketId(); } @JsonProperty - ShardSpec getShardSpec() + BuildingShardSpec getShardSpec() { return getSecondaryPartition(); } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionStat.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionStat.java index 5f4d16db2b19..a4ac80bdec04 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionStat.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionStat.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; import org.apache.druid.timeline.partition.ShardSpec; import org.joda.time.Interval; @@ -33,12 +34,12 @@ * partition key). The {@link ShardSpec} is later used by {@link PartialGenericSegmentMergeTask} to merge the partial * segments. */ -public class GenericPartitionStat extends PartitionStat +public class GenericPartitionStat extends PartitionStat { private static final String PROP_SHARD_SPEC = "shardSpec"; // Secondary partition key - private final ShardSpec shardSpec; + private final BucketNumberedShardSpec shardSpec; @JsonCreator public GenericPartitionStat( @@ -46,7 +47,7 @@ public GenericPartitionStat( @JsonProperty("taskExecutorPort") int taskExecutorPort, @JsonProperty("useHttps") boolean useHttps, @JsonProperty("interval") Interval interval, - @JsonProperty(PROP_SHARD_SPEC) ShardSpec shardSpec, + @JsonProperty(PROP_SHARD_SPEC) BucketNumberedShardSpec shardSpec, @JsonProperty("numRows") @Nullable Integer numRows, @JsonProperty("sizeBytes") @Nullable Long sizeBytes ) @@ -56,14 +57,14 @@ public GenericPartitionStat( } @Override - public int getPartitionId() + public int getBucketId() { - return shardSpec.getPartitionNum(); + return shardSpec.getBucketId(); } @JsonProperty(PROP_SHARD_SPEC) @Override - ShardSpec getSecondaryPartition() + BucketNumberedShardSpec getSecondaryPartition() { return shardSpec; } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionLocation.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionLocation.java deleted file mode 100644 index 604eb7a6f504..000000000000 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionLocation.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import org.joda.time.Interval; - -/** - * This class represents the intermediary data server where the partition of {@code interval} and {@code partitionId} - * is stored. - */ -public class HashPartitionLocation extends PartitionLocation -{ - @JsonCreator - public HashPartitionLocation( - @JsonProperty("host") String host, - @JsonProperty("port") int port, - @JsonProperty("useHttps") boolean useHttps, - @JsonProperty("subTaskId") String subTaskId, - @JsonProperty("interval") Interval interval, - @JsonProperty("partitionId") int partitionId - ) - { - super(host, port, useHttps, subTaskId, interval, partitionId); - } - - @JsonProperty - @Override - public int getPartitionId() - { - return getSecondaryPartition(); - } -} diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionStat.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionStat.java deleted file mode 100644 index 21019abe0fe7..000000000000 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionStat.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import org.joda.time.Interval; - -import javax.annotation.Nullable; -import java.util.Objects; - -/** - * Statistics about a partition created by {@link PartialHashSegmentGenerateTask}. Each partition is a set of data - * of the same time chunk (primary partition key) and the same partitionId (secondary partition key). This class - * holds the statistics of a single partition created by a task. - */ -public class HashPartitionStat extends PartitionStat -{ - // Secondary partition key - private final int partitionId; - - @JsonCreator - public HashPartitionStat( - @JsonProperty("taskExecutorHost") String taskExecutorHost, - @JsonProperty("taskExecutorPort") int taskExecutorPort, - @JsonProperty("useHttps") boolean useHttps, - @JsonProperty("interval") Interval interval, - @JsonProperty("partitionId") int partitionId, - @JsonProperty("numRows") @Nullable Integer numRows, - @JsonProperty("sizeBytes") @Nullable Long sizeBytes - ) - { - super(taskExecutorHost, taskExecutorPort, useHttps, interval, numRows, sizeBytes); - this.partitionId = partitionId; - } - - @JsonProperty - @Override - public int getPartitionId() - { - return partitionId; - } - - @JsonIgnore - @Override - Integer getSecondaryPartition() - { - return partitionId; - } - - @Override - public boolean equals(Object o) - { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - HashPartitionStat that = (HashPartitionStat) o; - return partitionId == that.partitionId; - } - - @Override - public int hashCode() - { - return Objects.hash(super.hashCode(), partitionId); - } -} diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTask.java index cb1bc3986510..bed85dea6926 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTask.java @@ -29,6 +29,8 @@ import com.google.common.base.Throwables; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import org.apache.druid.client.indexing.IndexingServiceClient; import org.apache.druid.data.input.FiniteFirehoseFactory; import org.apache.druid.data.input.InputFormat; @@ -79,6 +81,7 @@ import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.partition.BuildingNumberedShardSpec; +import org.apache.druid.timeline.partition.BuildingShardSpec; import org.apache.druid.timeline.partition.NumberedShardSpec; import org.apache.druid.timeline.partition.PartitionBoundaries; import org.apache.druid.utils.CollectionUtils; @@ -336,24 +339,6 @@ PartialRangeSegmentGenerateParallelIndexTaskRunner createPartialRangeSegmentGene ); } - @VisibleForTesting - PartialHashSegmentMergeParallelIndexTaskRunner createPartialHashSegmentMergeRunner( - TaskToolbox toolbox, - List ioConfigs - ) - { - return new PartialHashSegmentMergeParallelIndexTaskRunner( - toolbox, - getId(), - getGroupId(), - getIngestionSchema().getDataSchema(), - ioConfigs, - getIngestionSchema().getTuningConfig(), - getContext(), - indexingServiceClient - ); - } - @VisibleForTesting PartialGenericSegmentMergeParallelIndexTaskRunner createPartialGenericSegmentMergeRunner( TaskToolbox toolbox, @@ -544,10 +529,8 @@ private TaskStatus runMultiPhaseParallel(TaskToolbox toolbox) throws Exception private TaskStatus runHashPartitionMultiPhaseParallel(TaskToolbox toolbox) throws Exception { // 1. Partial segment generation phase - ParallelIndexTaskRunner indexingRunner = createRunner( - toolbox, - this::createPartialHashSegmentGenerateRunner - ); + ParallelIndexTaskRunner> indexingRunner + = createRunner(toolbox, this::createPartialHashSegmentGenerateRunner); TaskState state = runNextPhase(indexingRunner); if (state.isFailure()) { @@ -557,16 +540,16 @@ private TaskStatus runHashPartitionMultiPhaseParallel(TaskToolbox toolbox) throw // 2. Partial segment merge phase // partition (interval, partitionId) -> partition locations - Map, List> partitionToLocations = - groupHashPartitionLocationsPerPartition(indexingRunner.getReports()); - final List ioConfigs = createHashMergeIOConfigs( + Map, List> partitionToLocations = + groupGenericPartitionLocationsPerPartition(indexingRunner.getReports()); + final List ioConfigs = createGenericMergeIOConfigs( ingestionSchema.getTuningConfig().getTotalNumMergeTasks(), partitionToLocations ); - final ParallelIndexTaskRunner mergeRunner = createRunner( + final ParallelIndexTaskRunner mergeRunner = createRunner( toolbox, - tb -> createPartialHashSegmentMergeRunner(tb, ioConfigs) + tb -> createPartialGenericSegmentMergeRunner(tb, ioConfigs) ); state = runNextPhase(mergeRunner); if (state.isSuccess()) { @@ -659,38 +642,35 @@ private PartitionBoundaries determineRangePartition(Collection, List> groupHashPartitionLocationsPerPartition( - Map subTaskIdToReport - ) - { - BiFunction createPartitionLocationFunction = - (subtaskId, partitionStat) -> - new HashPartitionLocation( - partitionStat.getTaskExecutorHost(), - partitionStat.getTaskExecutorPort(), - partitionStat.isUseHttps(), - subtaskId, - partitionStat.getInterval(), - partitionStat.getSecondaryPartition() - ); - - return groupPartitionLocationsPerPartition(subTaskIdToReport, createPartitionLocationFunction); - } - private static Map, List> groupGenericPartitionLocationsPerPartition( Map> subTaskIdToReport ) { - BiFunction createPartitionLocationFunction = - (subtaskId, partitionStat) -> - new GenericPartitionLocation( - partitionStat.getTaskExecutorHost(), - partitionStat.getTaskExecutorPort(), - partitionStat.isUseHttps(), - subtaskId, - partitionStat.getInterval(), - partitionStat.getSecondaryPartition() - ); + final Map, BuildingShardSpec> intervalAndIntegerToShardSpec = new HashMap<>(); + final Object2IntMap intervalToNextPartitionId = new Object2IntOpenHashMap<>(); + final BiFunction createPartitionLocationFunction = + (subtaskId, partitionStat) -> { + final BuildingShardSpec shardSpec = intervalAndIntegerToShardSpec.computeIfAbsent( + Pair.of(partitionStat.getInterval(), partitionStat.getBucketId()), + key -> { + // Lazily determine the partitionId to create packed partitionIds for the core partitions. + // See the Javadoc of BucketNumberedShardSpec for details. + final int partitionId = intervalToNextPartitionId.computeInt( + partitionStat.getInterval(), + ((interval, nextPartitionId) -> nextPartitionId == null ? 0 : nextPartitionId + 1) + ); + return partitionStat.getSecondaryPartition().convert(partitionId); + } + ); + return new GenericPartitionLocation( + partitionStat.getTaskExecutorHost(), + partitionStat.getTaskExecutorPort(), + partitionStat.isUseHttps(), + subtaskId, + partitionStat.getInterval(), + shardSpec + ); + }; return groupPartitionLocationsPerPartition(subTaskIdToReport, createPartitionLocationFunction); } @@ -708,7 +688,7 @@ Map, List> groupPartitionLocationsPerPartition( final GeneratedPartitionsReport report = entry.getValue(); for (S partitionStat : report.getPartitionStats()) { final List locationsOfSamePartition = partitionToLocations.computeIfAbsent( - Pair.of(partitionStat.getInterval(), partitionStat.getPartitionId()), + Pair.of(partitionStat.getInterval(), partitionStat.getBucketId()), k -> new ArrayList<>() ); locationsOfSamePartition.add(createPartitionLocationFunction.apply(subTaskId, partitionStat)); @@ -718,18 +698,6 @@ Map, List> groupPartitionLocationsPerPartition( return partitionToLocations; } - private static List createHashMergeIOConfigs( - int totalNumMergeTasks, - Map, List> partitionToLocations - ) - { - return createMergeIOConfigs( - totalNumMergeTasks, - partitionToLocations, - PartialHashSegmentMergeIOConfig::new - ); - } - private static List createGenericMergeIOConfigs( int totalNumMergeTasks, Map, List> partitionToLocations diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTaskRunner.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTaskRunner.java index 49b2b48ecd61..05103e85c6ab 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTaskRunner.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTaskRunner.java @@ -38,8 +38,9 @@ * uses {@link SinglePhaseParallelIndexTaskRunner} for it. * * For perfect rollup, parallel indexing is executed in multiple phases. The supervisor task currently uses - * {@link PartialHashSegmentGenerateParallelIndexTaskRunner} and {@link PartialHashSegmentMergeParallelIndexTaskRunner}, - * and can use more runners in the future. + * {@link PartialHashSegmentGenerateParallelIndexTaskRunner}, {@link PartialRangeSegmentGenerateParallelIndexTaskRunner}, + * and {@link PartialGenericSegmentMergeParallelIndexTaskRunner}. + * More runners can be added in the future. */ public interface ParallelIndexTaskRunner { diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTuningConfig.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTuningConfig.java index 73e2ac7c7414..935eeb48c21e 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTuningConfig.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTuningConfig.java @@ -57,14 +57,14 @@ public class ParallelIndexTuningConfig extends IndexTuningConfig /** * Max number of segments to merge at the same time. - * Used only by {@link PartialHashSegmentMergeTask}. + * Used only by {@link PartialGenericSegmentMergeTask}. * This configuration was temporarily added to avoid using too much memory while merging segments, * and will be removed once {@link org.apache.druid.segment.IndexMerger} is improved to not use much memory. */ private final int maxNumSegmentsToMerge; /** - * Total number of tasks for partial segment merge (that is, number of {@link PartialHashSegmentMergeTask}s). + * Total number of tasks for partial segment merge (that is, number of {@link PartialGenericSegmentMergeTask}s). * Used only when this task runs with shuffle. */ private final int totalNumMergeTasks; diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialGenericSegmentMergeTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialGenericSegmentMergeTask.java index 858eff4ae63d..fed80d9252f8 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialGenericSegmentMergeTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialGenericSegmentMergeTask.java @@ -29,6 +29,8 @@ import org.apache.druid.indexing.common.TaskToolbox; import org.apache.druid.indexing.common.task.IndexTaskClientFactory; import org.apache.druid.indexing.common.task.TaskResource; +import org.apache.druid.java.util.common.ISE; +import org.apache.druid.timeline.partition.BuildingShardSpec; import org.apache.druid.timeline.partition.ShardSpec; import org.joda.time.Interval; @@ -39,12 +41,12 @@ /** * {@link ParallelIndexTaskRunner} for the phase to merge generic partitioned segments in multi-phase parallel indexing. */ -public class PartialGenericSegmentMergeTask extends PartialSegmentMergeTask +public class PartialGenericSegmentMergeTask extends PartialSegmentMergeTask { public static final String TYPE = "partial_index_generic_merge"; private final PartialGenericSegmentMergeIngestionSpec ingestionSchema; - private final Table intervalAndIntegerToShardSpec; + private final Table> intervalAndIntegerToShardSpec; @JsonCreator public PartialGenericSegmentMergeTask( @@ -82,24 +84,28 @@ public PartialGenericSegmentMergeTask( ); } - private static Table createIntervalAndIntegerToShardSpec( + private static Table> createIntervalAndIntegerToShardSpec( List partitionLocations ) { - Table intervalAndIntegerToShardSpec = HashBasedTable.create(); + final Table> intervalAndIntegerToShardSpec = HashBasedTable.create(); partitionLocations.forEach( p -> { - ShardSpec currShardSpec = intervalAndIntegerToShardSpec.get(p.getInterval(), p.getPartitionId()); - Preconditions.checkArgument( - currShardSpec == null || p.getShardSpec().equals(currShardSpec), - "interval %s, partitionId %s mismatched shard specs: %s", - p.getInterval(), - p.getPartitionId(), - partitionLocations - ); - - intervalAndIntegerToShardSpec.put(p.getInterval(), p.getPartitionId(), p.getShardSpec()); + final ShardSpec currShardSpec = intervalAndIntegerToShardSpec.get(p.getInterval(), p.getBucketId()); + if (currShardSpec == null) { + intervalAndIntegerToShardSpec.put(p.getInterval(), p.getBucketId(), p.getShardSpec()); + } else { + if (!p.getShardSpec().equals(currShardSpec)) { + throw new ISE( + "interval %s, bucketId %s mismatched shard specs: %s and %s", + p.getInterval(), + p.getBucketId(), + currShardSpec, + p.getShardSpec() + ); + } + } } ); @@ -119,7 +125,7 @@ public String getType() } @Override - ShardSpec createShardSpec(TaskToolbox toolbox, Interval interval, int partitionId) + BuildingShardSpec createShardSpec(TaskToolbox toolbox, Interval interval, int partitionId) { return Preconditions.checkNotNull( intervalAndIntegerToShardSpec.get(interval, partitionId), diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateParallelIndexTaskRunner.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateParallelIndexTaskRunner.java index ef8869c42727..e067eb909196 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateParallelIndexTaskRunner.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateParallelIndexTaskRunner.java @@ -27,11 +27,9 @@ /** * {@link ParallelIndexTaskRunner} for the phase to create hash partitioned segments in multi-phase parallel indexing. - * - * @see PartialHashSegmentMergeParallelIndexTaskRunner */ class PartialHashSegmentGenerateParallelIndexTaskRunner - extends InputSourceSplitParallelIndexTaskRunner + extends InputSourceSplitParallelIndexTaskRunner> { private static final String PHASE_NAME = "partial segment generation"; diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateTask.java index bf33faef292e..1bfda30522af 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentGenerateTask.java @@ -26,8 +26,8 @@ import org.apache.druid.indexer.partitions.HashedPartitionsSpec; import org.apache.druid.indexing.common.TaskToolbox; import org.apache.druid.indexing.common.actions.TaskActionClient; -import org.apache.druid.indexing.common.task.CachingSegmentAllocator; import org.apache.druid.indexing.common.task.IndexTaskClientFactory; +import org.apache.druid.indexing.common.task.SegmentAllocatorForBatch; import org.apache.druid.indexing.common.task.SegmentAllocators; import org.apache.druid.indexing.common.task.TaskResource; import org.apache.druid.indexing.common.task.batch.parallel.iterator.DefaultIndexTaskInputRowIteratorBuilder; @@ -35,6 +35,7 @@ import org.apache.druid.segment.indexing.granularity.GranularitySpec; import org.apache.druid.segment.realtime.appenderator.AppenderatorsManager; import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; import org.apache.druid.timeline.partition.PartialShardSpec; import org.joda.time.Interval; @@ -51,7 +52,7 @@ * hashing the segment granularity and partition dimensions in {@link HashedPartitionsSpec}. Partitioned segments are * stored in local storage using {@link org.apache.druid.indexing.worker.ShuffleDataSegmentPusher}. */ -public class PartialHashSegmentGenerateTask extends PartialSegmentGenerateTask +public class PartialHashSegmentGenerateTask extends PartialSegmentGenerateTask { public static final String TYPE = "partial_index_generate"; private static final String PROP_SPEC = "spec"; @@ -127,7 +128,7 @@ public boolean isReady(TaskActionClient taskActionClient) throws Exception } @Override - CachingSegmentAllocator createSegmentAllocator(TaskToolbox toolbox, ParallelIndexSupervisorTaskClient taskClient) + SegmentAllocatorForBatch createSegmentAllocator(TaskToolbox toolbox, ParallelIndexSupervisorTaskClient taskClient) throws IOException { final GranularitySpec granularitySpec = ingestionSchema.getDataSchema().getGranularitySpec(); @@ -137,29 +138,29 @@ CachingSegmentAllocator createSegmentAllocator(TaskToolbox toolbox, ParallelInde toolbox, getDataSource(), getId(), - granularitySpec.getQueryGranularity(), + granularitySpec, new SupervisorTaskAccess(supervisorTaskId, taskClient), createHashPartitionAnalysisFromPartitionsSpec(granularitySpec, partitionsSpec) ); } @Override - GeneratedHashPartitionsReport createGeneratedPartitionsReport(TaskToolbox toolbox, List segments) + GeneratedPartitionsMetadataReport createGeneratedPartitionsReport(TaskToolbox toolbox, List segments) { - List partitionStats = segments.stream() - .map(segment -> createPartitionStat(toolbox, segment)) - .collect(Collectors.toList()); - return new GeneratedHashPartitionsReport(getId(), partitionStats); + List partitionStats = segments.stream() + .map(segment -> createPartitionStat(toolbox, segment)) + .collect(Collectors.toList()); + return new GeneratedPartitionsMetadataReport(getId(), partitionStats); } - private HashPartitionStat createPartitionStat(TaskToolbox toolbox, DataSegment segment) + private GenericPartitionStat createPartitionStat(TaskToolbox toolbox, DataSegment segment) { - return new HashPartitionStat( + return new GenericPartitionStat( toolbox.getTaskExecutorNode().getHost(), toolbox.getTaskExecutorNode().getPortToUse(), toolbox.getTaskExecutorNode().isEnableTlsPort(), segment.getInterval(), - segment.getShardSpec().getPartitionNum(), + (BucketNumberedShardSpec) segment.getShardSpec(), null, // numRows is not supported yet null // sizeBytes is not supported yet ); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIOConfig.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIOConfig.java deleted file mode 100644 index 2bc00ce3d9b6..000000000000 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIOConfig.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import org.apache.druid.segment.indexing.IOConfig; - -import java.util.List; - -@JsonTypeName(PartialHashSegmentMergeTask.TYPE) -class PartialHashSegmentMergeIOConfig extends PartialSegmentMergeIOConfig - implements IOConfig -{ - @JsonCreator - PartialHashSegmentMergeIOConfig( - @JsonProperty("partitionLocations") List partitionLocations - ) - { - super(partitionLocations); - } -} diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIngestionSpec.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIngestionSpec.java deleted file mode 100644 index abfef7608809..000000000000 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIngestionSpec.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import org.apache.druid.segment.indexing.DataSchema; - -class PartialHashSegmentMergeIngestionSpec extends PartialSegmentMergeIngestionSpec -{ - @JsonCreator - PartialHashSegmentMergeIngestionSpec( - @JsonProperty("dataSchema") DataSchema dataSchema, - @JsonProperty("ioConfig") PartialHashSegmentMergeIOConfig ioConfig, - @JsonProperty("tuningConfig") ParallelIndexTuningConfig tuningConfig - ) - { - super(dataSchema, ioConfig, tuningConfig); - } -} diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeParallelIndexTaskRunner.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeParallelIndexTaskRunner.java deleted file mode 100644 index c693513c6527..000000000000 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeParallelIndexTaskRunner.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.druid.client.indexing.IndexingServiceClient; -import org.apache.druid.data.input.InputSplit; -import org.apache.druid.indexing.common.TaskToolbox; -import org.apache.druid.segment.indexing.DataSchema; - -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * {@link ParallelIndexTaskRunner} for the phase to merge hash partitioned segments in multi-phase parallel indexing. - * - * @see PartialHashSegmentGenerateParallelIndexTaskRunner - */ -class PartialHashSegmentMergeParallelIndexTaskRunner - extends ParallelIndexPhaseRunner -{ - private static final String PHASE_NAME = "partial segment merge"; - - private final DataSchema dataSchema; - private final List mergeIOConfigs; - - PartialHashSegmentMergeParallelIndexTaskRunner( - TaskToolbox toolbox, - String taskId, - String groupId, - DataSchema dataSchema, - List mergeIOConfigs, - ParallelIndexTuningConfig tuningConfig, - Map context, - IndexingServiceClient indexingServiceClient - ) - { - super(toolbox, taskId, groupId, tuningConfig, context, indexingServiceClient); - - this.dataSchema = dataSchema; - this.mergeIOConfigs = mergeIOConfigs; - } - - @Override - public String getName() - { - return PHASE_NAME; - } - - @Override - Iterator> subTaskSpecIterator() - { - return mergeIOConfigs.stream().map(this::newTaskSpec).iterator(); - } - - @Override - int estimateTotalNumSubTasks() - { - return mergeIOConfigs.size(); - } - - @VisibleForTesting - SubTaskSpec newTaskSpec(PartialHashSegmentMergeIOConfig ioConfig) - { - final PartialHashSegmentMergeIngestionSpec ingestionSpec = - new PartialHashSegmentMergeIngestionSpec( - dataSchema, - ioConfig, - getTuningConfig() - ); - return new SubTaskSpec( - getTaskId() + "_" + getAndIncrementNextSpecId(), - getGroupId(), - getTaskId(), - getContext(), - new InputSplit<>(ioConfig.getPartitionLocations()) - ) - { - @Override - public PartialHashSegmentMergeTask newSubTask(int numAttempts) - { - return new PartialHashSegmentMergeTask( - null, - getGroupId(), - null, - getSupervisorTaskId(), - numAttempts, - ingestionSpec, - getContext(), - null, - null, - null - ); - } - }; - } -} diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeTask.java deleted file mode 100644 index cf5aaea2edc9..000000000000 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeTask.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import com.fasterxml.jackson.annotation.JacksonInject; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.Preconditions; -import org.apache.druid.client.indexing.IndexingServiceClient; -import org.apache.druid.indexer.partitions.HashedPartitionsSpec; -import org.apache.druid.indexer.partitions.PartitionsSpec; -import org.apache.druid.indexing.common.TaskToolbox; -import org.apache.druid.indexing.common.task.IndexTaskClientFactory; -import org.apache.druid.indexing.common.task.TaskResource; -import org.apache.druid.timeline.partition.HashBasedNumberedShardSpec; -import org.joda.time.Interval; - -import javax.annotation.Nullable; -import java.util.Map; - -/** - * The worker task of {@link PartialHashSegmentMergeParallelIndexTaskRunner}. This task reads partitioned segments - * created by {@link PartialHashSegmentGenerateTask}s, merges them, and pushes to deep storage. The pushed segments are - * reported to {@link PartialHashSegmentMergeParallelIndexTaskRunner}. - */ - -public class PartialHashSegmentMergeTask - extends PartialSegmentMergeTask -{ - public static final String TYPE = "partial_index_merge"; - - private final HashedPartitionsSpec partitionsSpec; - private final PartialHashSegmentMergeIngestionSpec ingestionSchema; - - @JsonCreator - public PartialHashSegmentMergeTask( - // id shouldn't be null except when this task is created by ParallelIndexSupervisorTask - @JsonProperty("id") @Nullable String id, - @JsonProperty("groupId") final String groupId, - @JsonProperty("resource") final TaskResource taskResource, - @JsonProperty("supervisorTaskId") final String supervisorTaskId, - @JsonProperty("numAttempts") final int numAttempts, // zero-based counting - @JsonProperty("spec") final PartialHashSegmentMergeIngestionSpec ingestionSchema, - @JsonProperty("context") final Map context, - @JacksonInject IndexingServiceClient indexingServiceClient, - @JacksonInject IndexTaskClientFactory taskClientFactory, - @JacksonInject ShuffleClient shuffleClient - ) - { - super( - getOrMakeId(id, TYPE, ingestionSchema.getDataSchema().getDataSource()), - groupId, - taskResource, - supervisorTaskId, - ingestionSchema.getDataSchema(), - ingestionSchema.getIOConfig(), - ingestionSchema.getTuningConfig(), - numAttempts, - context, - indexingServiceClient, - taskClientFactory, - shuffleClient - ); - - this.ingestionSchema = ingestionSchema; - - PartitionsSpec inputPartitionsSpec = ingestionSchema.getTuningConfig().getGivenOrDefaultPartitionsSpec(); - Preconditions.checkArgument(inputPartitionsSpec instanceof HashedPartitionsSpec, "hashed partitionsSpec required"); - partitionsSpec = (HashedPartitionsSpec) inputPartitionsSpec; - Preconditions.checkNotNull(partitionsSpec.getNumShards(), "hashed partitionsSpec numShards required"); - } - - @JsonProperty("spec") - private PartialHashSegmentMergeIngestionSpec getIngestionSchema() - { - return ingestionSchema; - } - - @Override - public String getType() - { - return TYPE; - } - - @Override - HashBasedNumberedShardSpec createShardSpec(TaskToolbox toolbox, Interval interval, int partitionId) - { - return new HashBasedNumberedShardSpec( - partitionId, - Preconditions.checkNotNull(partitionsSpec.getNumShards(), "numShards"), - partitionsSpec.getPartitionDimensions(), - toolbox.getJsonMapper() - ); - } -} diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateParallelIndexTaskRunner.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateParallelIndexTaskRunner.java index e0f9461d166b..39a7e6576c90 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateParallelIndexTaskRunner.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateParallelIndexTaskRunner.java @@ -29,8 +29,6 @@ /** * {@link ParallelIndexTaskRunner} for the phase to create range partitioned segments in multi-phase parallel indexing. - * - * @see PartialHashSegmentMergeParallelIndexTaskRunner */ class PartialRangeSegmentGenerateParallelIndexTaskRunner extends InputSourceSplitParallelIndexTaskRunner> diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateTask.java index 60bbb7aac9c8..949c3748ac4a 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialRangeSegmentGenerateTask.java @@ -28,8 +28,8 @@ import org.apache.druid.indexer.partitions.SingleDimensionPartitionsSpec; import org.apache.druid.indexing.common.TaskToolbox; import org.apache.druid.indexing.common.actions.TaskActionClient; -import org.apache.druid.indexing.common.task.CachingSegmentAllocator; import org.apache.druid.indexing.common.task.IndexTaskClientFactory; +import org.apache.druid.indexing.common.task.SegmentAllocatorForBatch; import org.apache.druid.indexing.common.task.SegmentAllocators; import org.apache.druid.indexing.common.task.TaskResource; import org.apache.druid.indexing.common.task.batch.parallel.iterator.RangePartitionIndexTaskInputRowIteratorBuilder; @@ -37,6 +37,7 @@ import org.apache.druid.indexing.worker.ShuffleDataSegmentPusher; import org.apache.druid.segment.realtime.appenderator.AppenderatorsManager; import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; import org.apache.druid.timeline.partition.PartitionBoundaries; import org.joda.time.Interval; @@ -150,7 +151,7 @@ public boolean isReady(TaskActionClient taskActionClient) } @Override - CachingSegmentAllocator createSegmentAllocator(TaskToolbox toolbox, ParallelIndexSupervisorTaskClient taskClient) + SegmentAllocatorForBatch createSegmentAllocator(TaskToolbox toolbox, ParallelIndexSupervisorTaskClient taskClient) throws IOException { final RangePartitionAnalysis partitionAnalysis = new RangePartitionAnalysis( @@ -161,7 +162,7 @@ CachingSegmentAllocator createSegmentAllocator(TaskToolbox toolbox, ParallelInde toolbox, getDataSource(), getId(), - ingestionSchema.getDataSchema().getGranularitySpec().getQueryGranularity(), + ingestionSchema.getDataSchema().getGranularitySpec(), new SupervisorTaskAccess(supervisorTaskId, taskClient), partitionAnalysis ); @@ -183,7 +184,7 @@ private GenericPartitionStat createPartitionStat(TaskToolbox toolbox, DataSegmen toolbox.getTaskExecutorNode().getPortToUse(), toolbox.getTaskExecutorNode().isEnableTlsPort(), segment.getInterval(), - segment.getShardSpec(), + (BucketNumberedShardSpec) segment.getShardSpec(), null, // numRows is not supported yet null // sizeBytes is not supported yet ); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialSegmentGenerateTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialSegmentGenerateTask.java index 65e570a9d84d..cbde9285cded 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialSegmentGenerateTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialSegmentGenerateTask.java @@ -28,11 +28,10 @@ import org.apache.druid.indexing.common.stats.DropwizardRowIngestionMeters; import org.apache.druid.indexing.common.stats.RowIngestionMeters; import org.apache.druid.indexing.common.task.BatchAppenderators; -import org.apache.druid.indexing.common.task.CachingSegmentAllocator; import org.apache.druid.indexing.common.task.ClientBasedTaskInfoProvider; import org.apache.druid.indexing.common.task.IndexTaskClientFactory; import org.apache.druid.indexing.common.task.InputSourceProcessor; -import org.apache.druid.indexing.common.task.NonLinearlyPartitionedSequenceNameFunction; +import org.apache.druid.indexing.common.task.SegmentAllocatorForBatch; import org.apache.druid.indexing.common.task.SequenceNameFunction; import org.apache.druid.indexing.common.task.TaskResource; import org.apache.druid.indexing.common.task.Tasks; @@ -129,7 +128,7 @@ public final TaskStatus runTask(TaskToolbox toolbox) throws Exception /** * @return {@link SegmentAllocator} suitable for the desired segment partitioning strategy. */ - abstract CachingSegmentAllocator createSegmentAllocator( + abstract SegmentAllocatorForBatch createSegmentAllocator( TaskToolbox toolbox, ParallelIndexSupervisorTaskClient taskClient ) throws IOException; @@ -171,11 +170,8 @@ private List generateSegments( final PartitionsSpec partitionsSpec = tuningConfig.getGivenOrDefaultPartitionsSpec(); final long pushTimeout = tuningConfig.getPushTimeout(); - final CachingSegmentAllocator segmentAllocator = createSegmentAllocator(toolbox, taskClient); - final SequenceNameFunction sequenceNameFunction = new NonLinearlyPartitionedSequenceNameFunction( - getId(), - segmentAllocator.getShardSpecs() - ); + final SegmentAllocatorForBatch segmentAllocator = createSegmentAllocator(toolbox, taskClient); + final SequenceNameFunction sequenceNameFunction = segmentAllocator.getSequenceNameFunction(); final Appenderator appenderator = BatchAppenderators.newAppenderator( getId(), diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialSegmentMergeTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialSegmentMergeTask.java index 96596be4dc73..b105d5e8da09 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialSegmentMergeTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartialSegmentMergeTask.java @@ -139,10 +139,10 @@ public boolean isReady(TaskActionClient taskActionClient) public TaskStatus runTask(TaskToolbox toolbox) throws Exception { // Group partitionLocations by interval and partitionId - final Map>> intervalToPartitions = new HashMap<>(); + final Map>> intervalToBuckets = new HashMap<>(); for (P location : ioConfig.getPartitionLocations()) { - intervalToPartitions.computeIfAbsent(location.getInterval(), k -> new Int2ObjectOpenHashMap<>()) - .computeIfAbsent(location.getPartitionId(), k -> new ArrayList<>()) + intervalToBuckets.computeIfAbsent(location.getInterval(), k -> new Int2ObjectOpenHashMap<>()) + .computeIfAbsent(location.getBucketId(), k -> new ArrayList<>()) .add(location); } @@ -168,7 +168,7 @@ public TaskStatus runTask(TaskToolbox toolbox) throws Exception final Stopwatch fetchStopwatch = Stopwatch.createStarted(); final Map>> intervalToUnzippedFiles = fetchSegmentFiles( toolbox, - intervalToPartitions + intervalToBuckets ); final long fetchTime = fetchStopwatch.elapsed(TimeUnit.SECONDS); fetchStopwatch.stop(); @@ -202,7 +202,7 @@ public TaskStatus runTask(TaskToolbox toolbox) throws Exception private Map>> fetchSegmentFiles( TaskToolbox toolbox, - Map>> intervalToPartitions + Map>> intervalToBuckets ) throws IOException { final File tempDir = toolbox.getIndexingTmpDir(); @@ -211,26 +211,26 @@ private Map>> fetchSegmentFiles( final Map>> intervalToUnzippedFiles = new HashMap<>(); // Fetch partition files - for (Entry>> entryPerInterval : intervalToPartitions.entrySet()) { + for (Entry>> entryPerInterval : intervalToBuckets.entrySet()) { final Interval interval = entryPerInterval.getKey(); - for (Int2ObjectMap.Entry> entryPerPartitionId : + for (Int2ObjectMap.Entry> entryPerBucketId : entryPerInterval.getValue().int2ObjectEntrySet()) { - final int partitionId = entryPerPartitionId.getIntKey(); + final int bucketId = entryPerBucketId.getIntKey(); final File partitionDir = FileUtils.getFile( tempDir, interval.getStart().toString(), interval.getEnd().toString(), - Integer.toString(partitionId) + Integer.toString(bucketId) ); FileUtils.forceMkdir(partitionDir); - for (P location : entryPerPartitionId.getValue()) { + for (P location : entryPerBucketId.getValue()) { final File zippedFile = shuffleClient.fetchSegmentFile(partitionDir, supervisorTaskId, location); try { final File unzippedDir = new File(partitionDir, StringUtils.format("unzipped_%s", location.getSubTaskId())); FileUtils.forceMkdir(unzippedDir); CompressionUtils.unzip(zippedFile, unzippedDir); intervalToUnzippedFiles.computeIfAbsent(interval, k -> new Int2ObjectOpenHashMap<>()) - .computeIfAbsent(partitionId, k -> new ArrayList<>()) + .computeIfAbsent(bucketId, k -> new ArrayList<>()) .add(unzippedDir); } finally { @@ -247,7 +247,7 @@ private Map>> fetchSegmentFiles( /** * Create a {@link ShardSpec} suitable for the desired secondary partitioning strategy. */ - abstract S createShardSpec(TaskToolbox toolbox, Interval interval, int partitionId); + abstract S createShardSpec(TaskToolbox toolbox, Interval interval, int bucketId); private Set mergeAndPushSegments( TaskToolbox toolbox, @@ -262,9 +262,9 @@ private Set mergeAndPushSegments( final Set pushedSegments = new HashSet<>(); for (Entry>> entryPerInterval : intervalToUnzippedFiles.entrySet()) { final Interval interval = entryPerInterval.getKey(); - for (Int2ObjectMap.Entry> entryPerPartitionId : entryPerInterval.getValue().int2ObjectEntrySet()) { - final int partitionId = entryPerPartitionId.getIntKey(); - final List segmentFilesToMerge = entryPerPartitionId.getValue(); + for (Int2ObjectMap.Entry> entryPerBucketId : entryPerInterval.getValue().int2ObjectEntrySet()) { + final int bucketId = entryPerBucketId.getIntKey(); + final List segmentFilesToMerge = entryPerBucketId.getValue(); final Pair> mergedFileAndDimensionNames = mergeSegmentsInSamePartition( dataSchema, tuningConfig, @@ -290,7 +290,7 @@ private Set mergeAndPushSegments( null, // will be filled in the segmentPusher mergedFileAndDimensionNames.rhs, metricNames, - createShardSpec(toolbox, interval, partitionId), + createShardSpec(toolbox, interval, bucketId), null, // will be filled in the segmentPusher 0 // will be filled in the segmentPusher ), diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartitionLocation.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartitionLocation.java index e6578c50924f..da382cec0604 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartitionLocation.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartitionLocation.java @@ -29,7 +29,7 @@ /** * This class represents the intermediary data server where the partition of {@link #interval} and - * {@link #getPartitionId()} is stored. + * {@link #getBucketId()} is stored. */ abstract class PartitionLocation { @@ -93,13 +93,13 @@ public T getSecondaryPartition() return secondaryPartition; } - abstract int getPartitionId(); + abstract int getBucketId(); final URI toIntermediaryDataServerURI(String supervisorTaskId) { return URI.create( StringUtils.format( - "%s://%s:%d/druid/worker/v1/shuffle/task/%s/%s/partition?startTime=%s&endTime=%s&partitionId=%d", + "%s://%s:%d/druid/worker/v1/shuffle/task/%s/%s/partition?startTime=%s&endTime=%s&bucketId=%d", useHttps ? "https" : "http", host, port, @@ -107,7 +107,7 @@ final URI toIntermediaryDataServerURI(String supervisorTaskId) StringUtils.urlEncode(subTaskId), interval.getStart(), interval.getEnd(), - getPartitionId() + getBucketId() ) ); } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartitionStat.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartitionStat.java index 66974c297eea..c7f1a55c2a2e 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartitionStat.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/PartitionStat.java @@ -104,7 +104,7 @@ public final Long getSizeBytes() /** * @return Uniquely identifying index from 0..N-1 of the N partitions */ - abstract int getPartitionId(); + abstract int getBucketId(); /** * @return Definition of secondary partition. For example, for range partitioning, this should include the start/end. diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseSubTask.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseSubTask.java index 9f1dc52c1ff4..61908a8f255b 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseSubTask.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseSubTask.java @@ -316,6 +316,7 @@ private Set generateAndPushSegments( final boolean explicitIntervals = granularitySpec.bucketIntervals().isPresent(); final SegmentAllocator segmentAllocator = SegmentAllocators.forLinearPartitioning( toolbox, + getId(), new SupervisorTaskAccess(getSupervisorTaskId(), taskClient), getIngestionSchema().getDataSchema(), getTaskLockHelper(), diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/SubTaskReport.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/SubTaskReport.java index 564b3af8ab6f..26f20f6f8688 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/SubTaskReport.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/SubTaskReport.java @@ -30,7 +30,6 @@ @JsonTypeInfo(use = Id.NAME, property = "type", defaultImpl = PushedSegmentsReport.class) @JsonSubTypes(value = { @Type(name = PushedSegmentsReport.TYPE, value = PushedSegmentsReport.class), - @Type(name = GeneratedHashPartitionsReport.TYPE, value = GeneratedHashPartitionsReport.class), @Type(name = DimensionDistributionReport.TYPE, value = DimensionDistributionReport.class), @Type(name = GeneratedPartitionsMetadataReport.TYPE, value = GeneratedPartitionsMetadataReport.class) }) diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/CompletePartitionAnalysis.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/CompletePartitionAnalysis.java index 2e4328043f43..efecdda5e096 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/CompletePartitionAnalysis.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/CompletePartitionAnalysis.java @@ -21,12 +21,11 @@ import org.apache.druid.indexer.partitions.PartitionsSpec; import org.apache.druid.indexing.common.TaskToolbox; -import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; import org.joda.time.Interval; import java.util.List; import java.util.Map; -import java.util.function.Function; /** * This interface represents the PartitionAnalysis that has the complete picture of secondary partitions to create. @@ -35,9 +34,5 @@ */ public interface CompletePartitionAnalysis extends PartitionAnalysis { - Map> convertToIntervalToSegmentIds( - TaskToolbox toolbox, - String dataSource, - Function versionFinder - ); + Map>> createBuckets(TaskToolbox toolbox); } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/HashPartitionAnalysis.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/HashPartitionAnalysis.java index a4b3a86a1b55..5773f095c10f 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/HashPartitionAnalysis.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/HashPartitionAnalysis.java @@ -23,8 +23,8 @@ import org.apache.druid.indexer.partitions.HashedPartitionsSpec; import org.apache.druid.indexing.common.TaskToolbox; import org.apache.druid.java.util.common.IAE; -import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; -import org.apache.druid.timeline.partition.HashBasedNumberedShardSpec; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; +import org.apache.druid.timeline.partition.HashBucketShardSpec; import org.joda.time.Interval; import java.util.Collections; @@ -33,7 +33,6 @@ import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; -import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -92,37 +91,23 @@ public void forEach(BiConsumer consumer) } @Override - public Map> convertToIntervalToSegmentIds( - TaskToolbox toolbox, - String dataSource, - Function versionFinder - ) + public Map>> createBuckets(TaskToolbox toolbox) { - final Map> intervalToSegmentIds = - Maps.newHashMapWithExpectedSize(getNumTimePartitions()); - + final Map>> intervalToLookup = Maps.newHashMapWithExpectedSize( + intervalToNumBuckets.size() + ); forEach((interval, numBuckets) -> { - intervalToSegmentIds.put( - interval, - IntStream.range(0, numBuckets) - .mapToObj(i -> { - final HashBasedNumberedShardSpec shardSpec = new HashBasedNumberedShardSpec( - i, - numBuckets, - partitionsSpec.getPartitionDimensions(), - toolbox.getJsonMapper() - ); - return new SegmentIdWithShardSpec( - dataSource, - interval, - versionFinder.apply(interval), - shardSpec - ); - }) - .collect(Collectors.toList()) - ); + final List> buckets = IntStream + .range(0, numBuckets) + .mapToObj(i -> new HashBucketShardSpec( + i, + numBuckets, + partitionsSpec.getPartitionDimensions(), + toolbox.getJsonMapper() + )) + .collect(Collectors.toList()); + intervalToLookup.put(interval, buckets); }); - - return intervalToSegmentIds; + return intervalToLookup; } } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/RangePartitionAnalysis.java b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/RangePartitionAnalysis.java index b2753931ba1b..c8a2b8804d07 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/RangePartitionAnalysis.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/partition/RangePartitionAnalysis.java @@ -23,19 +23,17 @@ import org.apache.druid.indexer.partitions.SingleDimensionPartitionsSpec; import org.apache.druid.indexing.common.TaskToolbox; import org.apache.druid.java.util.common.IAE; -import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; import org.apache.druid.timeline.partition.PartitionBoundaries; -import org.apache.druid.timeline.partition.SingleDimensionShardSpec; +import org.apache.druid.timeline.partition.RangeBucketShardSpec; import org.joda.time.Interval; -import javax.annotation.Nullable; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; -import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -90,44 +88,13 @@ public int getNumTimePartitions() return intervalToPartitionBoundaries.size(); } - @Override - public Map> convertToIntervalToSegmentIds( - TaskToolbox toolbox, - String dataSource, - Function versionFinder - ) - { - final String partitionDimension = partitionsSpec.getPartitionDimension(); - final Map> intervalToSegmentIds = Maps.newHashMapWithExpectedSize( - getNumTimePartitions() - ); - - forEach((interval, partitionBoundaries) -> - intervalToSegmentIds.put( - interval, - translatePartitionBoundaries( - dataSource, - interval, - partitionDimension, - partitionBoundaries, - versionFinder - ) - ) - ); - - return intervalToSegmentIds; - } - /** * Translate {@link PartitionBoundaries} into the corresponding * {@link SingleDimensionPartitionsSpec} with segment id. */ - private static List translatePartitionBoundaries( - String dataSource, - Interval interval, + private static List> translatePartitionBoundaries( String partitionDimension, - PartitionBoundaries partitionBoundaries, - Function versionFinder + PartitionBoundaries partitionBoundaries ) { if (partitionBoundaries.isEmpty()) { @@ -135,40 +102,30 @@ private static List translatePartitionBoundaries( } return IntStream.range(0, partitionBoundaries.size() - 1) - .mapToObj(i -> createSegmentIdWithShardSpec( - dataSource, - interval, - versionFinder.apply(interval), + .mapToObj(i -> new RangeBucketShardSpec( + i, partitionDimension, partitionBoundaries.get(i), - partitionBoundaries.get(i + 1), - i + partitionBoundaries.get(i + 1) )) .collect(Collectors.toList()); } - private static SegmentIdWithShardSpec createSegmentIdWithShardSpec( - String dataSource, - Interval interval, - String version, - String partitionDimension, - String partitionStart, - @Nullable String partitionEnd, - int partitionNum - ) + @Override + public Map>> createBuckets(TaskToolbox toolbox) { - // The shardSpec created here will be reused in PartialGenericSegmentMergeTask. This is ok because - // all PartialSegmentGenerateTasks create the same set of segmentIds (and thus shardSpecs). - return new SegmentIdWithShardSpec( - dataSource, - interval, - version, - new SingleDimensionShardSpec( - partitionDimension, - partitionStart, - partitionEnd, - partitionNum - ) + final String partitionDimension = partitionsSpec.getPartitionDimension(); + final Map>> intervalToSegmentIds = Maps.newHashMapWithExpectedSize( + getNumTimePartitions() ); + + forEach((interval, partitionBoundaries) -> + intervalToSegmentIds.put( + interval, + translatePartitionBoundaries(partitionDimension, partitionBoundaries) + ) + ); + + return intervalToSegmentIds; } } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/worker/IntermediaryDataManager.java b/indexing-service/src/main/java/org/apache/druid/indexing/worker/IntermediaryDataManager.java index 78090ca5181f..6df598c24031 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/worker/IntermediaryDataManager.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/worker/IntermediaryDataManager.java @@ -70,7 +70,7 @@ * and phase 2 tasks read those files via HTTP. * * The directory where segment files are placed is structured as - * {@link StorageLocation#path}/supervisorTaskId/startTimeOfSegment/endTimeOfSegment/partitionIdOfSegment. + * {@link StorageLocation#path}/supervisorTaskId/startTimeOfSegment/endTimeOfSegment/bucketIdOfSegment. * * This class provides interfaces to store, find, and remove segment files. * It also has a self-cleanup mechanism to clean up stale segment files. It periodically checks the last access time @@ -335,11 +335,11 @@ long addSegment(String supervisorTaskId, String subTaskId, DataSegment segment, } @Nullable - public File findPartitionFile(String supervisorTaskId, String subTaskId, Interval interval, int partitionId) + public File findPartitionFile(String supervisorTaskId, String subTaskId, Interval interval, int bucketId) { TaskIdUtils.validateId("supervisorTaskId", supervisorTaskId); for (StorageLocation location : shuffleDataLocations) { - final File partitionDir = new File(location.getPath(), getPartitionDir(supervisorTaskId, interval, partitionId)); + final File partitionDir = new File(location.getPath(), getPartitionDir(supervisorTaskId, interval, bucketId)); if (partitionDir.exists()) { supervisorTaskCheckTimes.put(supervisorTaskId, getExpiryTimeFromNow()); final File[] segmentFiles = partitionDir.listFiles(); @@ -384,23 +384,23 @@ private static String getPartitionFilePath( String supervisorTaskId, String subTaskId, Interval interval, - int partitionId + int bucketId ) { - return Paths.get(getPartitionDir(supervisorTaskId, interval, partitionId), subTaskId).toString(); + return Paths.get(getPartitionDir(supervisorTaskId, interval, bucketId), subTaskId).toString(); } private static String getPartitionDir( String supervisorTaskId, Interval interval, - int partitionId + int bucketId ) { return Paths.get( supervisorTaskId, interval.getStart().toString(), interval.getEnd().toString(), - String.valueOf(partitionId) + String.valueOf(bucketId) ).toString(); } } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/ShuffleResource.java b/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/ShuffleResource.java index d1adcb9f2d22..0e0e9364e211 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/ShuffleResource.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/ShuffleResource.java @@ -75,7 +75,7 @@ public Response getPartition( @PathParam("subTaskId") String subTaskId, @QueryParam("startTime") String startTime, @QueryParam("endTime") String endTime, - @QueryParam("partitionId") int partitionId + @QueryParam("bucketId") int bucketId ) { final Interval interval = new Interval(DateTimes.of(startTime), DateTimes.of(endTime)); @@ -83,16 +83,16 @@ public Response getPartition( supervisorTaskId, subTaskId, interval, - partitionId + bucketId ); if (partitionFile == null) { final String errorMessage = StringUtils.format( - "Can't find the partition for supervisorTask[%s], subTask[%s], interval[%s], and partitionId[%s]", + "Can't find the partition for supervisorTask[%s], subTask[%s], interval[%s], and bucketId[%s]", supervisorTaskId, subTaskId, interval, - partitionId + bucketId ); return Response.status(Status.NOT_FOUND).entity(errorMessage).build(); } else { diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/actions/SegmentAllocateActionTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/actions/SegmentAllocateActionTest.java index c5301bf66419..ce1e1fc1fccd 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/actions/SegmentAllocateActionTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/actions/SegmentAllocateActionTest.java @@ -880,14 +880,14 @@ public void testCannotAddToExistingSingleDimensionShardSpecs() throws Exception .dataSource(DATA_SOURCE) .interval(Granularities.HOUR.bucket(PARTY_TIME)) .version(PARTY_TIME.toString()) - .shardSpec(new SingleDimensionShardSpec("foo", null, "bar", 0)) + .shardSpec(new SingleDimensionShardSpec("foo", null, "bar", 0, 2)) .size(0) .build(), DataSegment.builder() .dataSource(DATA_SOURCE) .interval(Granularities.HOUR.bucket(PARTY_TIME)) .version(PARTY_TIME.toString()) - .shardSpec(new SingleDimensionShardSpec("foo", "bar", null, 1)) + .shardSpec(new SingleDimensionShardSpec("foo", "bar", null, 1, 2)) .size(0) .build() ) @@ -914,14 +914,14 @@ public void testWithPartialShardSpecAndOvershadowingSegments() throws IOExceptio .dataSource(DATA_SOURCE) .interval(Granularities.HOUR.bucket(PARTY_TIME)) .version(PARTY_TIME.toString()) - .shardSpec(new HashBasedNumberedShardSpec(0, 2, ImmutableList.of("dim1"), objectMapper)) + .shardSpec(new HashBasedNumberedShardSpec(0, 2, 0, 2, ImmutableList.of("dim1"), objectMapper)) .size(0) .build(), DataSegment.builder() .dataSource(DATA_SOURCE) .interval(Granularities.HOUR.bucket(PARTY_TIME)) .version(PARTY_TIME.toString()) - .shardSpec(new HashBasedNumberedShardSpec(1, 2, ImmutableList.of("dim1"), objectMapper)) + .shardSpec(new HashBasedNumberedShardSpec(1, 2, 1, 2, ImmutableList.of("dim1"), objectMapper)) .size(0) .build() ) @@ -935,7 +935,7 @@ public void testWithPartialShardSpecAndOvershadowingSegments() throws IOExceptio "seq", null, true, - new HashBasedNumberedPartialShardSpec(ImmutableList.of("dim1"), 2), + new HashBasedNumberedPartialShardSpec(ImmutableList.of("dim1"), 1, 2), lockGranularity ); final SegmentIdWithShardSpec segmentIdentifier = action.perform(task, taskActionTestKit.getTaskActionToolbox()); @@ -946,7 +946,7 @@ public void testWithPartialShardSpecAndOvershadowingSegments() throws IOExceptio Assert.assertTrue(shardSpec instanceof HashBasedNumberedShardSpec); final HashBasedNumberedShardSpec hashBasedNumberedShardSpec = (HashBasedNumberedShardSpec) shardSpec; - Assert.assertEquals(2, hashBasedNumberedShardSpec.getPartitions()); + Assert.assertEquals(2, hashBasedNumberedShardSpec.getNumCorePartitions()); Assert.assertEquals(ImmutableList.of("dim1"), hashBasedNumberedShardSpec.getPartitionDimensions()); } @@ -1029,10 +1029,7 @@ private void assertSameIdentifier(final SegmentIdWithShardSpec expected, final S if (expected.getShardSpec().getClass() == NumberedShardSpec.class && actual.getShardSpec().getClass() == NumberedShardSpec.class) { - Assert.assertEquals( - ((NumberedShardSpec) expected.getShardSpec()).getPartitions(), - ((NumberedShardSpec) actual.getShardSpec()).getPartitions() - ); + Assert.assertEquals(expected.getShardSpec().getNumCorePartitions(), actual.getShardSpec().getNumCorePartitions()); } else if (expected.getShardSpec().getClass() == LinearShardSpec.class && actual.getShardSpec().getClass() == LinearShardSpec.class) { // do nothing diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java index 6e3fcf54dcf2..513c13c56e44 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/IndexTaskTest.java @@ -239,13 +239,13 @@ public void testDeterminePartitions() throws Exception Assert.assertEquals(Intervals.of("2014/P1D"), segments.get(0).getInterval()); Assert.assertEquals(HashBasedNumberedShardSpec.class, segments.get(0).getShardSpec().getClass()); Assert.assertEquals(0, segments.get(0).getShardSpec().getPartitionNum()); - Assert.assertEquals(2, ((NumberedShardSpec) segments.get(0).getShardSpec()).getPartitions()); + Assert.assertEquals(2, ((NumberedShardSpec) segments.get(0).getShardSpec()).getNumCorePartitions()); Assert.assertEquals("test", segments.get(1).getDataSource()); Assert.assertEquals(Intervals.of("2014/P1D"), segments.get(1).getInterval()); Assert.assertEquals(HashBasedNumberedShardSpec.class, segments.get(1).getShardSpec().getClass()); Assert.assertEquals(1, segments.get(1).getShardSpec().getPartitionNum()); - Assert.assertEquals(2, ((NumberedShardSpec) segments.get(1).getShardSpec()).getPartitions()); + Assert.assertEquals(2, ((NumberedShardSpec) segments.get(1).getShardSpec()).getNumCorePartitions()); } @Test diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RangePartitionCachingLocalSegmentAllocatorTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RangePartitionCachingLocalSegmentAllocatorTest.java index 12a16152fcb1..e841fba0f370 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RangePartitionCachingLocalSegmentAllocatorTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/RangePartitionCachingLocalSegmentAllocatorTest.java @@ -19,6 +19,7 @@ package org.apache.druid.indexing.common.task; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.apache.druid.data.input.InputRow; import org.apache.druid.indexer.partitions.SingleDimensionPartitionsSpec; @@ -30,11 +31,13 @@ import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.StringUtils; -import org.apache.druid.java.util.common.granularity.NoneGranularity; +import org.apache.druid.java.util.common.granularity.Granularities; +import org.apache.druid.segment.indexing.granularity.UniformGranularitySpec; +import org.apache.druid.segment.realtime.appenderator.SegmentAllocator; import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.partition.PartitionBoundaries; -import org.apache.druid.timeline.partition.SingleDimensionShardSpec; +import org.apache.druid.timeline.partition.RangeBucketShardSpec; import org.easymock.EasyMock; import org.joda.time.Interval; import org.junit.Assert; @@ -82,7 +85,7 @@ public class RangePartitionCachingLocalSegmentAllocatorTest INTERVAL_NORMAL, NORMAL_PARTITIONS ); - private CachingSegmentAllocator target; + private SegmentAllocator target; private SequenceNameFunction sequenceNameFunction; @Rule @@ -105,11 +108,11 @@ public void setup() throws IOException toolbox, DATASOURCE, TASKID, - new NoneGranularity(), + new UniformGranularitySpec(Granularities.HOUR, Granularities.NONE, ImmutableList.of()), new SupervisorTaskAccessWithNullClient(SUPERVISOR_TASKID), partitionAnalysis ); - sequenceNameFunction = new NonLinearlyPartitionedSequenceNameFunction(TASKID, target.getShardSpecs()); + sequenceNameFunction = ((CachingLocalSegmentAllocator) target).getSequenceNameFunction(); } @Test @@ -163,37 +166,37 @@ public void getSequenceName() } @SuppressWarnings("SameParameterValue") - private void testAllocate(InputRow row, Interval interval, int partitionNum) + private void testAllocate(InputRow row, Interval interval, int bucketId) { - String partitionEnd = getPartitionEnd(interval, partitionNum); - testAllocate(row, interval, partitionNum, partitionEnd); + String partitionEnd = getPartitionEnd(interval, bucketId); + testAllocate(row, interval, bucketId, partitionEnd); } @Nullable - private static String getPartitionEnd(Interval interval, int partitionNum) + private static String getPartitionEnd(Interval interval, int bucketId) { PartitionBoundaries partitions = INTERVAL_TO_PARTITONS.get(interval); - boolean isLastPartition = (partitionNum + 1) == partitions.size(); - return isLastPartition ? null : partitions.get(partitionNum + 1); + boolean isLastPartition = (bucketId + 1) == partitions.size(); + return isLastPartition ? null : partitions.get(bucketId + 1); } - private void testAllocate(InputRow row, Interval interval, int partitionNum, @Nullable String partitionEnd) + private void testAllocate(InputRow row, Interval interval, int bucketId, @Nullable String partitionEnd) { - String partitionStart = getPartitionStart(interval, partitionNum); - testAllocate(row, interval, partitionNum, partitionStart, partitionEnd); + String partitionStart = getPartitionStart(interval, bucketId); + testAllocate(row, interval, bucketId, partitionStart, partitionEnd); } @Nullable - private static String getPartitionStart(Interval interval, int partitionNum) + private static String getPartitionStart(Interval interval, int bucketId) { - boolean isFirstPartition = partitionNum == 0; - return isFirstPartition ? null : INTERVAL_TO_PARTITONS.get(interval).get(partitionNum); + boolean isFirstPartition = bucketId == 0; + return isFirstPartition ? null : INTERVAL_TO_PARTITONS.get(interval).get(bucketId); } private void testAllocate( InputRow row, Interval interval, - int partitionNum, + int bucketId, @Nullable String partitionStart, @Nullable String partitionEnd ) @@ -202,12 +205,12 @@ private void testAllocate( SegmentIdWithShardSpec segmentIdWithShardSpec = allocate(row, sequenceName); Assert.assertEquals( - SegmentId.of(DATASOURCE, interval, INTERVAL_TO_VERSION.get(interval), partitionNum), + SegmentId.of(DATASOURCE, interval, INTERVAL_TO_VERSION.get(interval), bucketId), segmentIdWithShardSpec.asSegmentId() ); - SingleDimensionShardSpec shardSpec = (SingleDimensionShardSpec) segmentIdWithShardSpec.getShardSpec(); + RangeBucketShardSpec shardSpec = (RangeBucketShardSpec) segmentIdWithShardSpec.getShardSpec(); Assert.assertEquals(PARTITION_DIMENSION, shardSpec.getDimension()); - Assert.assertEquals(partitionNum, shardSpec.getPartitionNum()); + Assert.assertEquals(bucketId, shardSpec.getBucketId()); Assert.assertEquals(partitionStart, shardSpec.getStart()); Assert.assertEquals(partitionEnd, shardSpec.getEnd()); } diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/ShardSpecsTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/ShardSpecsTest.java index 64b0a0cad15f..0818605fc8ea 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/ShardSpecsTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/ShardSpecsTest.java @@ -20,6 +20,7 @@ package org.apache.druid.indexing.common.task; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import org.apache.druid.data.input.InputRow; @@ -28,7 +29,8 @@ import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.granularity.Granularities; -import org.apache.druid.timeline.partition.HashBasedNumberedShardSpec; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; +import org.apache.druid.timeline.partition.HashBucketShardSpec; import org.apache.druid.timeline.partition.ShardSpec; import org.joda.time.Interval; import org.junit.Assert; @@ -50,11 +52,11 @@ public ShardSpecsTest() @Test public void testShardSpecSelectionWithNullPartitionDimension() { - ShardSpec spec1 = new HashBasedNumberedShardSpec(0, 2, null, jsonMapper); - ShardSpec spec2 = new HashBasedNumberedShardSpec(1, 2, null, jsonMapper); + HashBucketShardSpec spec1 = new HashBucketShardSpec(0, 2, null, jsonMapper); + HashBucketShardSpec spec2 = new HashBucketShardSpec(1, 2, null, jsonMapper); - Map> shardSpecMap = new HashMap<>(); - shardSpecMap.put(Intervals.of("2014-01-01T00:00:00.000Z/2014-01-02T00:00:00.000Z"), Lists.newArrayList(spec1, spec2)); + Map>> shardSpecMap = new HashMap<>(); + shardSpecMap.put(Intervals.of("2014-01-01T00:00:00.000Z/2014-01-02T00:00:00.000Z"), ImmutableList.of(spec1, spec2)); ShardSpecs shardSpecs = new ShardSpecs(shardSpecMap, Granularities.HOUR); String visitorId = "visitorId"; diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractMultiPhaseParallelIndexingTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractMultiPhaseParallelIndexingTest.java index 7970e1dd9b7f..64949deefb34 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractMultiPhaseParallelIndexingTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractMultiPhaseParallelIndexingTest.java @@ -117,31 +117,23 @@ Set runTestTask( maxNumConcurrentSubTasks ); + return runTask(task, expectedTaskStatus); + } + + Set runTask(ParallelIndexSupervisorTask task, TaskState expectedTaskStatus) + { task.addToContext(Tasks.FORCE_TIME_CHUNK_LOCK_KEY, lockGranularity == LockGranularity.TIME_CHUNK); TaskStatus taskStatus = getIndexingServiceClient().runAndWait(task); Assert.assertEquals(expectedTaskStatus, taskStatus.getStatusCode()); return getIndexingServiceClient().getPublishedSegments(task); } - private ParallelIndexSupervisorTask newTask( - @Nullable TimestampSpec timestampSpec, - @Nullable DimensionsSpec dimensionsSpec, - @Nullable InputFormat inputFormat, - @Nullable ParseSpec parseSpec, - Interval interval, - File inputDir, - String filter, + ParallelIndexTuningConfig newTuningConfig( DimensionBasedPartitionsSpec partitionsSpec, int maxNumConcurrentSubTasks ) { - GranularitySpec granularitySpec = new UniformGranularitySpec( - Granularities.DAY, - Granularities.MINUTE, - interval == null ? null : Collections.singletonList(interval) - ); - - ParallelIndexTuningConfig tuningConfig = new ParallelIndexTuningConfig( + return new ParallelIndexTuningConfig( null, null, null, @@ -169,6 +161,27 @@ private ParallelIndexSupervisorTask newTask( null, null ); + } + + private ParallelIndexSupervisorTask newTask( + @Nullable TimestampSpec timestampSpec, + @Nullable DimensionsSpec dimensionsSpec, + @Nullable InputFormat inputFormat, + @Nullable ParseSpec parseSpec, + Interval interval, + File inputDir, + String filter, + DimensionBasedPartitionsSpec partitionsSpec, + int maxNumConcurrentSubTasks + ) + { + GranularitySpec granularitySpec = new UniformGranularitySpec( + Granularities.DAY, + Granularities.MINUTE, + interval == null ? null : Collections.singletonList(interval) + ); + + ParallelIndexTuningConfig tuningConfig = newTuningConfig(partitionsSpec, maxNumConcurrentSubTasks); final ParallelIndexIngestionSpec ingestionSpec; @@ -185,9 +198,7 @@ private ParallelIndexSupervisorTask newTask( "dataSource", timestampSpec, dimensionsSpec, - new AggregatorFactory[]{ - new LongSumAggregatorFactory("val", "val") - }, + new AggregatorFactory[]{new LongSumAggregatorFactory("val", "val")}, granularitySpec, null ), diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractParallelIndexSupervisorTaskTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractParallelIndexSupervisorTaskTest.java index ca33815f28eb..470a00372b48 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractParallelIndexSupervisorTaskTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractParallelIndexSupervisorTaskTest.java @@ -521,7 +521,6 @@ public static void prepareObjectMapper( new NamedType(ParallelIndexSupervisorTask.class, ParallelIndexSupervisorTask.TYPE), new NamedType(SinglePhaseSubTask.class, SinglePhaseSubTask.TYPE), new NamedType(PartialHashSegmentGenerateTask.class, PartialHashSegmentGenerateTask.TYPE), - new NamedType(PartialHashSegmentMergeTask.class, PartialHashSegmentMergeTask.TYPE), new NamedType(PartialRangeSegmentGenerateTask.class, PartialRangeSegmentGenerateTask.TYPE), new NamedType(PartialGenericSegmentMergeTask.class, PartialGenericSegmentMergeTask.TYPE), new NamedType(PartialDimensionDistributionTask.class, PartialDimensionDistributionTask.TYPE) @@ -646,7 +645,7 @@ public > File fetchSegmentFile( supervisorTaskId, location.getSubTaskId(), location.getInterval(), - location.getPartitionId() + location.getBucketId() ); if (zippedFile == null) { throw new ISE("Can't find segment file for location[%s] at path[%s]", location); diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GeneratedHashPartitionsReportTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GeneratedHashPartitionsReportTest.java deleted file mode 100644 index 1343b9425833..000000000000 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GeneratedHashPartitionsReportTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.druid.segment.TestHelper; -import org.junit.Before; -import org.junit.Test; - -import java.util.Collections; - -public class GeneratedHashPartitionsReportTest -{ - private static final ObjectMapper OBJECT_MAPPER = ParallelIndexTestingFactory.createObjectMapper(); - - private GeneratedHashPartitionsReport target; - - @Before - public void setup() - { - target = new GeneratedHashPartitionsReport( - "task-id", - Collections.singletonList( - new HashPartitionStat( - ParallelIndexTestingFactory.TASK_EXECUTOR_HOST, - ParallelIndexTestingFactory.TASK_EXECUTOR_PORT, - ParallelIndexTestingFactory.USE_HTTPS, - ParallelIndexTestingFactory.INTERVAL, - ParallelIndexTestingFactory.PARTITION_ID, - ParallelIndexTestingFactory.NUM_ROWS, - ParallelIndexTestingFactory.SIZE_BYTES - ) - ) - ); - } - - @Test - public void serializesDeserializes() - { - TestHelper.testSerializesDeserializes(OBJECT_MAPPER, target); - } -} diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionLocationTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionLocationTest.java index 956dbc8fd150..4e46e388f6c6 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionLocationTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionLocationTest.java @@ -53,6 +53,6 @@ public void serializesDeserializes() @Test public void hasPartitionIdThatMatchesShardSpec() { - Assert.assertEquals(ParallelIndexTestingFactory.PARTITION_ID, target.getPartitionId()); + Assert.assertEquals(ParallelIndexTestingFactory.PARTITION_ID, target.getBucketId()); } } diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionStatTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionStatTest.java index 2bcac8edfd47..ffeab43f60d9 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionStatTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/GenericPartitionStatTest.java @@ -21,10 +21,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.druid.segment.TestHelper; +import org.apache.druid.timeline.partition.HashBucketShardSpec; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import java.util.Collections; + public class GenericPartitionStatTest { private static final ObjectMapper OBJECT_MAPPER = ParallelIndexTestingFactory.createObjectMapper(); @@ -39,7 +42,12 @@ public void setup() ParallelIndexTestingFactory.TASK_EXECUTOR_PORT, ParallelIndexTestingFactory.USE_HTTPS, ParallelIndexTestingFactory.INTERVAL, - ParallelIndexTestingFactory.HASH_BASED_NUMBERED_SHARD_SPEC, + new HashBucketShardSpec( + ParallelIndexTestingFactory.PARTITION_ID, + ParallelIndexTestingFactory.PARTITION_ID + 1, + Collections.singletonList("dim"), + new ObjectMapper() + ), ParallelIndexTestingFactory.NUM_ROWS, ParallelIndexTestingFactory.SIZE_BYTES ); @@ -54,6 +62,6 @@ public void serializesDeserializes() @Test public void hasPartitionIdThatMatchesSecondaryPartition() { - Assert.assertEquals(target.getSecondaryPartition().getPartitionNum(), target.getPartitionId()); + Assert.assertEquals(target.getSecondaryPartition().getBucketId(), target.getBucketId()); } } diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionAdjustingCorePartitionSizeTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionAdjustingCorePartitionSizeTest.java new file mode 100644 index 000000000000..e19b208e3691 --- /dev/null +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionAdjustingCorePartitionSizeTest.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.indexing.common.task.batch.parallel; + +import com.google.common.collect.ImmutableList; +import org.apache.druid.data.input.InputFormat; +import org.apache.druid.data.input.impl.CsvInputFormat; +import org.apache.druid.data.input.impl.DimensionsSpec; +import org.apache.druid.data.input.impl.TimestampSpec; +import org.apache.druid.indexer.TaskState; +import org.apache.druid.indexer.partitions.DimensionBasedPartitionsSpec; +import org.apache.druid.indexer.partitions.HashedPartitionsSpec; +import org.apache.druid.indexing.common.LockGranularity; +import org.apache.druid.java.util.common.Intervals; +import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.partition.HashBasedNumberedShardSpec; +import org.joda.time.Interval; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +@RunWith(Parameterized.class) +public class HashPartitionAdjustingCorePartitionSizeTest extends AbstractMultiPhaseParallelIndexingTest +{ + private static final TimestampSpec TIMESTAMP_SPEC = new TimestampSpec("ts", "auto", null); + private static final DimensionsSpec DIMENSIONS_SPEC = new DimensionsSpec( + DimensionsSpec.getDefaultSchemas(Arrays.asList("ts", "dim1", "dim2")) + ); + private static final InputFormat INPUT_FORMAT = new CsvInputFormat( + Arrays.asList("ts", "dim1", "dim2", "val"), + null, + false, + false, + 0 + ); + private static final Interval INTERVAL_TO_INDEX = Intervals.of("2020-01-01/P1M"); + + @Parameterized.Parameters(name = "{0}, maxNumConcurrentSubTasks={1}") + public static Iterable constructorFeeder() + { + return ImmutableList.of( + new Object[]{LockGranularity.TIME_CHUNK, 2}, + new Object[]{LockGranularity.TIME_CHUNK, 1}, + new Object[]{LockGranularity.SEGMENT, 2} + ); + } + + private final int maxNumConcurrentSubTasks; + + public HashPartitionAdjustingCorePartitionSizeTest(LockGranularity lockGranularity, int maxNumConcurrentSubTasks) + { + super(lockGranularity, true); + this.maxNumConcurrentSubTasks = maxNumConcurrentSubTasks; + } + + @Test + public void testLessPartitionsThanBuckets() throws IOException + { + final File inputDir = temporaryFolder.newFolder(); + for (int i = 0; i < 3; i++) { + try (final Writer writer = + Files.newBufferedWriter(new File(inputDir, "test_" + i).toPath(), StandardCharsets.UTF_8)) { + writer.write(StringUtils.format("2020-01-01T00:00:00,%s,b1,%d\n", "a" + (i + 1), 10 * (i + 1))); + } + } + final DimensionBasedPartitionsSpec partitionsSpec = new HashedPartitionsSpec( + null, + 10, + ImmutableList.of("dim1") + ); + final List segments = new ArrayList<>( + runTestTask( + TIMESTAMP_SPEC, + DIMENSIONS_SPEC, + INPUT_FORMAT, + null, + INTERVAL_TO_INDEX, + inputDir, + "test_*", + partitionsSpec, + maxNumConcurrentSubTasks, + TaskState.SUCCESS + ) + ); + Assert.assertEquals(3, segments.size()); + segments.sort(Comparator.comparing(segment -> segment.getShardSpec().getPartitionNum())); + int prevPartitionId = -1; + for (DataSegment segment : segments) { + Assert.assertSame(HashBasedNumberedShardSpec.class, segment.getShardSpec().getClass()); + final HashBasedNumberedShardSpec shardSpec = (HashBasedNumberedShardSpec) segment.getShardSpec(); + Assert.assertEquals(3, shardSpec.getNumCorePartitions()); + Assert.assertEquals(10, shardSpec.getNumBuckets()); + Assert.assertEquals(ImmutableList.of("dim1"), shardSpec.getPartitionDimensions()); + Assert.assertEquals(prevPartitionId + 1, shardSpec.getPartitionNum()); + prevPartitionId = shardSpec.getPartitionNum(); + } + } + + @Test + public void testEqualNumberOfPartitionsToBuckets() throws IOException + { + final File inputDir = temporaryFolder.newFolder(); + for (int i = 0; i < 10; i++) { + try (final Writer writer = + Files.newBufferedWriter(new File(inputDir, "test_" + i).toPath(), StandardCharsets.UTF_8)) { + writer.write(StringUtils.format("2020-01-01T00:00:00,%s,b1,%d\n", "aa" + (i + 10), 10 * (i + 1))); + } + } + final DimensionBasedPartitionsSpec partitionsSpec = new HashedPartitionsSpec( + null, + 5, + ImmutableList.of("dim1") + ); + final Set segments = runTestTask( + TIMESTAMP_SPEC, + DIMENSIONS_SPEC, + INPUT_FORMAT, + null, + INTERVAL_TO_INDEX, + inputDir, + "test_*", + partitionsSpec, + maxNumConcurrentSubTasks, + TaskState.SUCCESS + ); + Assert.assertEquals(5, segments.size()); + segments.forEach(segment -> { + Assert.assertSame(HashBasedNumberedShardSpec.class, segment.getShardSpec().getClass()); + final HashBasedNumberedShardSpec shardSpec = (HashBasedNumberedShardSpec) segment.getShardSpec(); + Assert.assertEquals(5, shardSpec.getNumCorePartitions()); + Assert.assertEquals(5, shardSpec.getNumBuckets()); + Assert.assertEquals(ImmutableList.of("dim1"), shardSpec.getPartitionDimensions()); + }); + } +} diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionCachingLocalSegmentAllocatorTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionCachingLocalSegmentAllocatorTest.java index 16c20b342acc..ef8f09562311 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionCachingLocalSegmentAllocatorTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionCachingLocalSegmentAllocatorTest.java @@ -29,18 +29,19 @@ import org.apache.druid.indexing.common.TaskToolbox; import org.apache.druid.indexing.common.actions.LockListAction; import org.apache.druid.indexing.common.actions.TaskActionClient; -import org.apache.druid.indexing.common.task.CachingSegmentAllocator; -import org.apache.druid.indexing.common.task.NonLinearlyPartitionedSequenceNameFunction; +import org.apache.druid.indexing.common.task.CachingLocalSegmentAllocator; import org.apache.druid.indexing.common.task.SegmentAllocators; import org.apache.druid.indexing.common.task.SequenceNameFunction; import org.apache.druid.indexing.common.task.SupervisorTaskAccessWithNullClient; import org.apache.druid.indexing.common.task.batch.partition.HashPartitionAnalysis; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.StringUtils; -import org.apache.druid.java.util.common.granularity.NoneGranularity; +import org.apache.druid.java.util.common.granularity.Granularities; +import org.apache.druid.segment.indexing.granularity.UniformGranularitySpec; +import org.apache.druid.segment.realtime.appenderator.SegmentAllocator; import org.apache.druid.segment.realtime.appenderator.SegmentIdWithShardSpec; import org.apache.druid.timeline.SegmentId; -import org.apache.druid.timeline.partition.HashBasedNumberedShardSpec; +import org.apache.druid.timeline.partition.HashBucketShardSpec; import org.easymock.EasyMock; import org.joda.time.Interval; import org.junit.Assert; @@ -70,7 +71,7 @@ public class HashPartitionCachingLocalSegmentAllocatorTest Collections.singletonList(DIMENSION) ); - private CachingSegmentAllocator target; + private SegmentAllocator target; private SequenceNameFunction sequenceNameFunction; @Before @@ -83,11 +84,11 @@ public void setup() throws IOException toolbox, DATASOURCE, TASKID, - new NoneGranularity(), + new UniformGranularitySpec(Granularities.HOUR, Granularities.NONE, ImmutableList.of()), new SupervisorTaskAccessWithNullClient(SUPERVISOR_TASKID), partitionAnalysis ); - sequenceNameFunction = new NonLinearlyPartitionedSequenceNameFunction(TASKID, target.getShardSpecs()); + sequenceNameFunction = ((CachingLocalSegmentAllocator) target).getSequenceNameFunction(); } @Test @@ -102,10 +103,10 @@ public void allocatesCorrectShardSpec() throws IOException SegmentId.of(DATASOURCE, INTERVAL, VERSION, PARTITION_NUM), segmentIdWithShardSpec.asSegmentId() ); - HashBasedNumberedShardSpec shardSpec = (HashBasedNumberedShardSpec) segmentIdWithShardSpec.getShardSpec(); + HashBucketShardSpec shardSpec = (HashBucketShardSpec) segmentIdWithShardSpec.getShardSpec(); Assert.assertEquals(PARTITION_DIMENSIONS, shardSpec.getPartitionDimensions()); - Assert.assertEquals(NUM_PARTITONS, shardSpec.getPartitions()); - Assert.assertEquals(PARTITION_NUM, shardSpec.getPartitionNum()); + Assert.assertEquals(NUM_PARTITONS, shardSpec.getNumBuckets()); + Assert.assertEquals(PARTITION_NUM, shardSpec.getBucketId()); } @Test diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionMultiPhaseParallelIndexingTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionMultiPhaseParallelIndexingTest.java index bcd3cbe85842..7fcabd0911ba 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionMultiPhaseParallelIndexingTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionMultiPhaseParallelIndexingTest.java @@ -75,24 +75,31 @@ public class HashPartitionMultiPhaseParallelIndexingTest extends AbstractMultiPh false, 0 ); - private static final int MAX_NUM_CONCURRENT_SUB_TASKS = 2; private static final Interval INTERVAL_TO_INDEX = Intervals.of("2017-12/P1M"); @Parameterized.Parameters(name = "{0}, useInputFormatApi={1}") public static Iterable constructorFeeder() { return ImmutableList.of( - new Object[]{LockGranularity.TIME_CHUNK, false}, - new Object[]{LockGranularity.TIME_CHUNK, true}, - new Object[]{LockGranularity.SEGMENT, true} + new Object[]{LockGranularity.TIME_CHUNK, false, 2}, + new Object[]{LockGranularity.TIME_CHUNK, true, 2}, + new Object[]{LockGranularity.TIME_CHUNK, true, 1}, + new Object[]{LockGranularity.SEGMENT, true, 2} ); } + private final int maxNumConcurrentSubTasks; + private File inputDir; - public HashPartitionMultiPhaseParallelIndexingTest(LockGranularity lockGranularity, boolean useInputFormatApi) + public HashPartitionMultiPhaseParallelIndexingTest( + LockGranularity lockGranularity, + boolean useInputFormatApi, + int maxNumConcurrentSubTasks + ) { super(lockGranularity, useInputFormatApi); + this.maxNumConcurrentSubTasks = maxNumConcurrentSubTasks; } @Before @@ -132,7 +139,7 @@ public void testRun() throws Exception inputDir, "test_*", new HashedPartitionsSpec(null, 2, ImmutableList.of("dim1", "dim2")), - MAX_NUM_CONCURRENT_SUB_TASKS, + maxNumConcurrentSubTasks, TaskState.SUCCESS ); } else { @@ -145,7 +152,7 @@ public void testRun() throws Exception inputDir, "test_*", new HashedPartitionsSpec(null, 2, ImmutableList.of("dim1", "dim2")), - MAX_NUM_CONCURRENT_SUB_TASKS, + maxNumConcurrentSubTasks, TaskState.SUCCESS ); } diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionStatTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionStatTest.java deleted file mode 100644 index 1eb6f867cd8e..000000000000 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HashPartitionStatTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.druid.segment.TestHelper; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class HashPartitionStatTest -{ - private static final ObjectMapper OBJECT_MAPPER = ParallelIndexTestingFactory.createObjectMapper(); - - private HashPartitionStat target; - - @Before - public void setup() - { - target = new HashPartitionStat( - ParallelIndexTestingFactory.TASK_EXECUTOR_HOST, - ParallelIndexTestingFactory.TASK_EXECUTOR_PORT, - ParallelIndexTestingFactory.USE_HTTPS, - ParallelIndexTestingFactory.INTERVAL, - ParallelIndexTestingFactory.PARTITION_ID, - ParallelIndexTestingFactory.NUM_ROWS, - ParallelIndexTestingFactory.SIZE_BYTES - ); - } - - @Test - public void serializesDeserializes() - { - TestHelper.testSerializesDeserializes(OBJECT_MAPPER, target); - } - - @Test - public void hasPartitionIdThatMatchesSecondaryPartition() - { - Assert.assertEquals(target.getSecondaryPartition().intValue(), target.getPartitionId()); - } -} diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HttpShuffleClientTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HttpShuffleClientTest.java index 45df76d58da2..3ddb63b65fbe 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HttpShuffleClientTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/HttpShuffleClientTest.java @@ -203,7 +203,7 @@ private TestPartitionLocation() } @Override - int getPartitionId() + int getBucketId() { return getSecondaryPartition(); } diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTaskResourceTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTaskResourceTest.java index 88dac06b89ed..9f8a07dfaebd 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTaskResourceTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTaskResourceTest.java @@ -677,6 +677,7 @@ public TaskStatus run(final TaskToolbox toolbox) throws Exception .getGivenOrDefaultPartitionsSpec(); final SegmentAllocator segmentAllocator = SegmentAllocators.forLinearPartitioning( toolbox, + getId(), new SupervisorTaskAccess(getSupervisorTaskId(), taskClient), getIngestionSchema().getDataSchema(), getTaskLockHelper(), diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTaskTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTaskTest.java index b882aac207ab..3ae79a3b6966 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTaskTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexSupervisorTaskTest.java @@ -19,9 +19,11 @@ package org.apache.druid.indexing.common.task.batch.parallel; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Ordering; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.Pair; +import org.apache.druid.timeline.partition.BuildingHashBasedNumberedShardSpec; import org.hamcrest.Matchers; import org.joda.time.Interval; import org.junit.Assert; @@ -45,8 +47,8 @@ public class ParallelIndexSupervisorTaskTest public static class CreateMergeIoConfigsTest { private static final int TOTAL_NUM_MERGE_TASKS = 10; - private static final Function, PartialHashSegmentMergeIOConfig> - CREATE_PARTIAL_SEGMENT_MERGE_IO_CONFIG = PartialHashSegmentMergeIOConfig::new; + private static final Function, PartialGenericSegmentMergeIOConfig> + CREATE_PARTIAL_SEGMENT_MERGE_IO_CONFIG = PartialGenericSegmentMergeIOConfig::new; @Parameterized.Parameters(name = "count = {0}") public static Iterable data() @@ -66,14 +68,14 @@ public static Iterable data() @Test public void handlesLastPartitionCorrectly() { - List assignedPartitionLocation = createMergeIOConfigs(); + List assignedPartitionLocation = createMergeIOConfigs(); assertNoMissingPartitions(count, assignedPartitionLocation); } @Test public void sizesPartitionsEvenly() { - List assignedPartitionLocation = createMergeIOConfigs(); + List assignedPartitionLocation = createMergeIOConfigs(); List actualPartitionSizes = assignedPartitionLocation.stream() .map(i -> i.getPartitionLocations().size()) .collect(Collectors.toList()); @@ -89,7 +91,7 @@ public void sizesPartitionsEvenly() ); } - private List createMergeIOConfigs() + private List createMergeIOConfigs() { return ParallelIndexSupervisorTask.createMergeIOConfigs( TOTAL_NUM_MERGE_TASKS, @@ -98,7 +100,7 @@ private List createMergeIOConfigs() ); } - private static Map, List> createPartitionToLocations(int count) + private static Map, List> createPartitionToLocations(int count) { return IntStream.range(0, count).boxed().collect( Collectors.toMap( @@ -108,15 +110,15 @@ private static Map, List> createP ); } - private static HashPartitionLocation createPartitionLocation(int id) + private static GenericPartitionLocation createPartitionLocation(int id) { - return new HashPartitionLocation( + return new GenericPartitionLocation( "host", 0, false, "subTaskId", createInterval(id), - id + new BuildingHashBasedNumberedShardSpec(id, id, id + 1, null, new ObjectMapper()) ); } @@ -127,7 +129,7 @@ private static Interval createInterval(int id) private static void assertNoMissingPartitions( int count, - List assignedPartitionLocation + List assignedPartitionLocation ) { List expectedIds = IntStream.range(0, count).boxed().collect(Collectors.toList()); @@ -136,7 +138,7 @@ private static void assertNoMissingPartitions( .flatMap( i -> i.getPartitionLocations() .stream() - .map(HashPartitionLocation::getPartitionId) + .map(GenericPartitionLocation::getBucketId) ) .sorted() .collect(Collectors.toList()); diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTestingFactory.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTestingFactory.java index 08dd92c5f67a..de7ee8b6cc40 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTestingFactory.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTestingFactory.java @@ -44,7 +44,7 @@ import org.apache.druid.segment.indexing.granularity.GranularitySpec; import org.apache.druid.segment.realtime.appenderator.AppenderatorsManager; import org.apache.druid.segment.transform.TransformSpec; -import org.apache.druid.timeline.partition.HashBasedNumberedShardSpec; +import org.apache.druid.timeline.partition.BuildingHashBasedNumberedShardSpec; import org.easymock.EasyMock; import org.joda.time.Duration; import org.joda.time.Interval; @@ -100,7 +100,8 @@ public > File fetchSegmentFile( private static final String SCHEMA_DIMENSION = "dim"; private static final String DATASOURCE = "datasource"; - static final HashBasedNumberedShardSpec HASH_BASED_NUMBERED_SHARD_SPEC = new HashBasedNumberedShardSpec( + static final BuildingHashBasedNumberedShardSpec HASH_BASED_NUMBERED_SHARD_SPEC = new BuildingHashBasedNumberedShardSpec( + PARTITION_ID, PARTITION_ID, PARTITION_ID + 1, Collections.singletonList("dim"), diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIOConfigTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIOConfigTest.java deleted file mode 100644 index 413c34d9d668..000000000000 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIOConfigTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.druid.segment.TestHelper; -import org.junit.Before; -import org.junit.Test; - -import java.util.Collections; - -public class PartialHashSegmentMergeIOConfigTest -{ - private static final ObjectMapper OBJECT_MAPPER = ParallelIndexTestingFactory.createObjectMapper(); - private static final HashPartitionLocation HASH_PARTITION_LOCATION = new HashPartitionLocation( - ParallelIndexTestingFactory.HOST, - ParallelIndexTestingFactory.PORT, - ParallelIndexTestingFactory.USE_HTTPS, - ParallelIndexTestingFactory.SUBTASK_ID, - ParallelIndexTestingFactory.INTERVAL, - ParallelIndexTestingFactory.PARTITION_ID - ); - - private PartialHashSegmentMergeIOConfig target; - - @Before - public void setup() - { - target = new PartialHashSegmentMergeIOConfig(Collections.singletonList(HASH_PARTITION_LOCATION)); - } - - @Test - public void serializesDeserializes() - { - TestHelper.testSerializesDeserializes(OBJECT_MAPPER, target); - } -} diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIngestionSpecTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIngestionSpecTest.java deleted file mode 100644 index d734739a0146..000000000000 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeIngestionSpecTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.druid.indexer.partitions.HashedPartitionsSpec; -import org.apache.druid.segment.TestHelper; -import org.junit.Before; -import org.junit.Test; - -import java.util.Collections; - -public class PartialHashSegmentMergeIngestionSpecTest -{ - private static final ObjectMapper OBJECT_MAPPER = ParallelIndexTestingFactory.createObjectMapper(); - private static final HashPartitionLocation HASH_PARTITION_LOCATION = new HashPartitionLocation( - ParallelIndexTestingFactory.HOST, - ParallelIndexTestingFactory.PORT, - ParallelIndexTestingFactory.USE_HTTPS, - ParallelIndexTestingFactory.SUBTASK_ID, - ParallelIndexTestingFactory.INTERVAL, - ParallelIndexTestingFactory.PARTITION_ID - ); - private static final PartialHashSegmentMergeIOConfig IO_CONFIG = - new PartialHashSegmentMergeIOConfig(Collections.singletonList(HASH_PARTITION_LOCATION)); - private static final HashedPartitionsSpec PARTITIONS_SPEC = new HashedPartitionsSpec( - null, - 1, - Collections.emptyList() - ); - - private PartialHashSegmentMergeIngestionSpec target; - - @Before - public void setup() - { - target = new PartialHashSegmentMergeIngestionSpec( - ParallelIndexTestingFactory.createDataSchema(ParallelIndexTestingFactory.INPUT_INTERVALS), - IO_CONFIG, - new ParallelIndexTestingFactory.TuningConfigBuilder() - .partitionsSpec(PARTITIONS_SPEC) - .build() - ); - } - - @Test - public void serializesDeserializes() - { - TestHelper.testSerializesDeserializes(OBJECT_MAPPER, target); - } -} diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeTaskTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeTaskTest.java deleted file mode 100644 index d6fe0bb864c4..000000000000 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/PartialHashSegmentMergeTaskTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.indexing.common.task.batch.parallel; - -import org.apache.druid.indexer.partitions.HashedPartitionsSpec; -import org.apache.druid.segment.TestHelper; -import org.hamcrest.Matchers; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.util.Collections; - -public class PartialHashSegmentMergeTaskTest extends AbstractParallelIndexSupervisorTaskTest -{ - private static final HashPartitionLocation HASH_PARTITION_LOCATION = new HashPartitionLocation( - ParallelIndexTestingFactory.HOST, - ParallelIndexTestingFactory.PORT, - ParallelIndexTestingFactory.USE_HTTPS, - ParallelIndexTestingFactory.SUBTASK_ID, - ParallelIndexTestingFactory.INTERVAL, - ParallelIndexTestingFactory.PARTITION_ID - ); - private static final PartialHashSegmentMergeIOConfig IO_CONFIG = - new PartialHashSegmentMergeIOConfig(Collections.singletonList(HASH_PARTITION_LOCATION)); - private static final HashedPartitionsSpec PARTITIONS_SPEC = new HashedPartitionsSpec( - null, - 1, - Collections.emptyList() - ); - private static final PartialHashSegmentMergeIngestionSpec INGESTION_SPEC = - new PartialHashSegmentMergeIngestionSpec( - ParallelIndexTestingFactory.createDataSchema(ParallelIndexTestingFactory.INPUT_INTERVALS), - IO_CONFIG, - new ParallelIndexTestingFactory.TuningConfigBuilder() - .partitionsSpec(PARTITIONS_SPEC) - .build() - ); - - private PartialHashSegmentMergeTask target; - - @Before - public void setup() - { - target = new PartialHashSegmentMergeTask( - ParallelIndexTestingFactory.AUTOMATIC_ID, - ParallelIndexTestingFactory.GROUP_ID, - ParallelIndexTestingFactory.TASK_RESOURCE, - ParallelIndexTestingFactory.SUPERVISOR_TASK_ID, - ParallelIndexTestingFactory.NUM_ATTEMPTS, - INGESTION_SPEC, - ParallelIndexTestingFactory.CONTEXT, - ParallelIndexTestingFactory.INDEXING_SERVICE_CLIENT, - ParallelIndexTestingFactory.TASK_CLIENT_FACTORY, - ParallelIndexTestingFactory.SHUFFLE_CLIENT - ); - } - - @Test - public void serializesDeserializes() - { - TestHelper.testSerializesDeserializes(getObjectMapper(), target); - } - - @Test - public void hasCorrectPrefixForAutomaticId() - { - String id = target.getId(); - Assert.assertThat(id, Matchers.startsWith(PartialHashSegmentMergeTask.TYPE)); - } -} diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/RangePartitionAdjustingCorePartitionSizeTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/RangePartitionAdjustingCorePartitionSizeTest.java new file mode 100644 index 000000000000..2e4aa44dc5f7 --- /dev/null +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/RangePartitionAdjustingCorePartitionSizeTest.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.indexing.common.task.batch.parallel; + +import com.google.common.collect.ImmutableList; +import org.apache.druid.data.input.InputFormat; +import org.apache.druid.data.input.impl.CsvInputFormat; +import org.apache.druid.data.input.impl.DimensionsSpec; +import org.apache.druid.data.input.impl.TimestampSpec; +import org.apache.druid.indexer.TaskState; +import org.apache.druid.indexer.partitions.DimensionBasedPartitionsSpec; +import org.apache.druid.indexer.partitions.SingleDimensionPartitionsSpec; +import org.apache.druid.indexing.common.LockGranularity; +import org.apache.druid.java.util.common.Intervals; +import org.apache.druid.java.util.common.StringUtils; +import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.partition.SingleDimensionShardSpec; +import org.joda.time.Interval; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +@RunWith(Parameterized.class) +public class RangePartitionAdjustingCorePartitionSizeTest extends AbstractMultiPhaseParallelIndexingTest +{ + private static final TimestampSpec TIMESTAMP_SPEC = new TimestampSpec("ts", "auto", null); + private static final DimensionsSpec DIMENSIONS_SPEC = new DimensionsSpec( + DimensionsSpec.getDefaultSchemas(Arrays.asList("ts", "dim1", "dim2")) + ); + private static final InputFormat INPUT_FORMAT = new CsvInputFormat( + Arrays.asList("ts", "dim1", "dim2", "val"), + null, + false, + false, + 0 + ); + private static final Interval INTERVAL_TO_INDEX = Intervals.of("2020-01-01/P1M"); + + @Parameterized.Parameters(name = "{0}, maxNumConcurrentSubTasks={1}") + public static Iterable constructorFeeder() + { + return ImmutableList.of( + new Object[]{LockGranularity.TIME_CHUNK, 2}, + new Object[]{LockGranularity.TIME_CHUNK, 1}, + new Object[]{LockGranularity.SEGMENT, 2} + ); + } + + private final int maxNumConcurrentSubTasks; + + public RangePartitionAdjustingCorePartitionSizeTest(LockGranularity lockGranularity, int maxNumConcurrentSubTasks) + { + super(lockGranularity, true); + this.maxNumConcurrentSubTasks = maxNumConcurrentSubTasks; + } + + @Test + public void testLessPartitionsThanBuckets() throws IOException + { + final File inputDir = temporaryFolder.newFolder(); + for (int i = 0; i < 2; i++) { + try (final Writer writer = + Files.newBufferedWriter(new File(inputDir, "test_" + i).toPath(), StandardCharsets.UTF_8)) { + writer.write(StringUtils.format("2020-01-01T00:00:00,aaa,b1,10\n")); + } + } + for (int i = 0; i < 3; i++) { + try (final Writer writer = + Files.newBufferedWriter(new File(inputDir, "test_" + (i + 2)).toPath(), StandardCharsets.UTF_8)) { + writer.write(StringUtils.format("2020-01-01T00:00:00,zzz,b1,10\n")); + } + } + final DimensionBasedPartitionsSpec partitionsSpec = new SingleDimensionPartitionsSpec( + 2, + null, + "dim1", + false + ); + final List segments = new ArrayList<>( + runTestTask( + TIMESTAMP_SPEC, + DIMENSIONS_SPEC, + INPUT_FORMAT, + null, + INTERVAL_TO_INDEX, + inputDir, + "test_*", + partitionsSpec, + maxNumConcurrentSubTasks, + TaskState.SUCCESS + ) + ); + Assert.assertEquals(1, segments.size()); + final DataSegment segment = segments.get(0); + Assert.assertSame(SingleDimensionShardSpec.class, segment.getShardSpec().getClass()); + final SingleDimensionShardSpec shardSpec = (SingleDimensionShardSpec) segment.getShardSpec(); + Assert.assertEquals(1, shardSpec.getNumCorePartitions()); + Assert.assertEquals(0, shardSpec.getPartitionNum()); + Assert.assertEquals("dim1", shardSpec.getDimension()); + } + + @Test + public void testEqualNumberOfPartitionsToBuckets() throws IOException + { + final File inputDir = temporaryFolder.newFolder(); + for (int i = 0; i < 10; i++) { + try (final Writer writer = + Files.newBufferedWriter(new File(inputDir, "test_" + i).toPath(), StandardCharsets.UTF_8)) { + writer.write(StringUtils.format("2020-01-01T00:00:00,%s,b1,%d\n", "aa" + (i + 10), 10 * (i + 1))); + } + } + final DimensionBasedPartitionsSpec partitionsSpec = new SingleDimensionPartitionsSpec( + 2, + null, + "dim1", + false + ); + final Set segments = runTestTask( + TIMESTAMP_SPEC, + DIMENSIONS_SPEC, + INPUT_FORMAT, + null, + INTERVAL_TO_INDEX, + inputDir, + "test_*", + partitionsSpec, + maxNumConcurrentSubTasks, + TaskState.SUCCESS + ); + Assert.assertEquals(5, segments.size()); + segments.forEach(segment -> { + Assert.assertSame(SingleDimensionShardSpec.class, segment.getShardSpec().getClass()); + final SingleDimensionShardSpec shardSpec = (SingleDimensionShardSpec) segment.getShardSpec(); + Assert.assertEquals(5, shardSpec.getNumCorePartitions()); + Assert.assertTrue(shardSpec.getPartitionNum() < shardSpec.getNumCorePartitions()); + Assert.assertEquals("dim1", shardSpec.getDimension()); + }); + } +} diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseParallelIndexingTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseParallelIndexingTest.java index e1e21a56719d..4abb539f333c 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseParallelIndexingTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/SinglePhaseParallelIndexingTest.java @@ -221,14 +221,14 @@ private void assertShardSpec(@Nullable Interval interval, boolean appendToExisti for (DataSegment segment : segmentsPerInterval) { Assert.assertSame(NumberedShardSpec.class, segment.getShardSpec().getClass()); final NumberedShardSpec shardSpec = (NumberedShardSpec) segment.getShardSpec(); - Assert.assertEquals(segmentsPerInterval.size(), shardSpec.getPartitions()); + Assert.assertEquals(segmentsPerInterval.size(), shardSpec.getNumCorePartitions()); } } } else { for (DataSegment segment : segments) { Assert.assertSame(NumberedShardSpec.class, segment.getShardSpec().getClass()); final NumberedShardSpec shardSpec = (NumberedShardSpec) segment.getShardSpec(); - Assert.assertEquals(0, shardSpec.getPartitions()); + Assert.assertEquals(0, shardSpec.getNumCorePartitions()); } } } @@ -248,7 +248,7 @@ private void assertShardSpecAfterOverwrite(@Nullable Interval interval, LockGran for (DataSegment segment : segmentsPerInterval) { Assert.assertSame(NumberedShardSpec.class, segment.getShardSpec().getClass()); final NumberedShardSpec shardSpec = (NumberedShardSpec) segment.getShardSpec(); - Assert.assertEquals(segmentsPerInterval.size(), shardSpec.getPartitions()); + Assert.assertEquals(segmentsPerInterval.size(), shardSpec.getNumCorePartitions()); } } } else { diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/TaskLockboxTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/TaskLockboxTest.java index 00f76a48952a..ae8fc3b64fe1 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/TaskLockboxTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/TaskLockboxTest.java @@ -971,8 +971,8 @@ public void testRequestForNewSegmentWithHashPartition() final Task task = NoopTask.create(); lockbox.add(task); - allocateSegmentsAndAssert(task, "seq", 3, new HashBasedNumberedPartialShardSpec(null, 3)); - allocateSegmentsAndAssert(task, "seq2", 5, new HashBasedNumberedPartialShardSpec(null, 5)); + allocateSegmentsAndAssert(task, "seq", 3, new HashBasedNumberedPartialShardSpec(null, 1, 3)); + allocateSegmentsAndAssert(task, "seq2", 5, new HashBasedNumberedPartialShardSpec(null, 3, 5)); } private void allocateSegmentsAndAssert( diff --git a/integration-tests/src/test/java/org/apache/druid/tests/indexer/AbstractITBatchIndexTest.java b/integration-tests/src/test/java/org/apache/druid/tests/indexer/AbstractITBatchIndexTest.java index 7309e7c8641f..5c64dcd626b0 100644 --- a/integration-tests/src/test/java/org/apache/druid/tests/indexer/AbstractITBatchIndexTest.java +++ b/integration-tests/src/test/java/org/apache/druid/tests/indexer/AbstractITBatchIndexTest.java @@ -25,7 +25,6 @@ import org.apache.druid.indexing.common.task.batch.parallel.PartialDimensionDistributionTask; import org.apache.druid.indexing.common.task.batch.parallel.PartialGenericSegmentMergeTask; import org.apache.druid.indexing.common.task.batch.parallel.PartialHashSegmentGenerateTask; -import org.apache.druid.indexing.common.task.batch.parallel.PartialHashSegmentMergeTask; import org.apache.druid.indexing.common.task.batch.parallel.PartialRangeSegmentGenerateTask; import org.apache.druid.indexing.common.task.batch.parallel.SinglePhaseSubTask; import org.apache.druid.java.util.common.ISE; @@ -312,7 +311,6 @@ private long countCompleteSubTasks(final String dataSource, final boolean perfec return t.getType().equals(SinglePhaseSubTask.TYPE); } else { return t.getType().equalsIgnoreCase(PartialHashSegmentGenerateTask.TYPE) - || t.getType().equalsIgnoreCase(PartialHashSegmentMergeTask.TYPE) || t.getType().equalsIgnoreCase(PartialDimensionDistributionTask.TYPE) || t.getType().equalsIgnoreCase(PartialRangeSegmentGenerateTask.TYPE) || t.getType().equalsIgnoreCase(PartialGenericSegmentMergeTask.TYPE); diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelper.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelper.java index 76093beb8c8a..ec4a65af3a4d 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelper.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelper.java @@ -21,7 +21,8 @@ import org.apache.druid.java.util.common.ISE; import org.apache.druid.timeline.DataSegment; -import org.apache.druid.timeline.partition.BuildingNumberedShardSpec; +import org.apache.druid.timeline.partition.BucketNumberedShardSpec; +import org.apache.druid.timeline.partition.BuildingShardSpec; import org.apache.druid.timeline.partition.OverwriteShardSpec; import org.apache.druid.timeline.partition.ShardSpec; import org.joda.time.Interval; @@ -42,7 +43,7 @@ public final class SegmentPublisherHelper * This method fills missing information in the shard spec if necessary when publishing segments. * * - When time chunk lock is used, the non-appending task should set the proper size of the core partitions for - * dynamically-partitioned segments. See {@link #annotateNumberedShardSpecFn}. + * dynamically-partitioned segments. See {@link #annotateCorePartitionSetSizeFn}. * - When segment lock is used, the overwriting task should set the proper size of the atomic update group. * See {@link #annotateAtomicUpdateGroupFn}. */ @@ -70,8 +71,10 @@ static Set annotateShardSpec(Set segments) final Function annotateFn; if (firstShardSpec instanceof OverwriteShardSpec) { annotateFn = annotateAtomicUpdateGroupFn(segmentsPerInterval.size()); - } else if (firstShardSpec instanceof BuildingNumberedShardSpec) { - annotateFn = annotateNumberedShardSpecFn(segmentsPerInterval.size()); + } else if (firstShardSpec instanceof BuildingShardSpec) { + annotateFn = annotateCorePartitionSetSizeFn(segmentsPerInterval.size()); + } else if (firstShardSpec instanceof BucketNumberedShardSpec) { + throw new ISE("Cannot publish segments with shardSpec[%s]", firstShardSpec); } else { annotateFn = null; } @@ -93,11 +96,11 @@ private static Function annotateAtomicUpdateGroupFn(in }; } - private static Function annotateNumberedShardSpecFn(int corePartitionSetSize) + private static Function annotateCorePartitionSetSizeFn(int corePartitionSetSize) { return segment -> { - final BuildingNumberedShardSpec shardSpec = (BuildingNumberedShardSpec) segment.getShardSpec(); - return segment.withShardSpec(shardSpec.toNumberedShardSpec(corePartitionSetSize)); + final BuildingShardSpec shardSpec = (BuildingShardSpec) segment.getShardSpec(); + return segment.withShardSpec(shardSpec.convert(corePartitionSetSize)); }; } diff --git a/server/src/test/java/org/apache/druid/client/CachingClusteredClientTest.java b/server/src/test/java/org/apache/druid/client/CachingClusteredClientTest.java index ed6ececbeb86..30fb7659c11e 100644 --- a/server/src/test/java/org/apache/druid/client/CachingClusteredClientTest.java +++ b/server/src/test/java/org/apache/druid/client/CachingClusteredClientTest.java @@ -125,10 +125,10 @@ import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.VersionedIntervalTimeline; import org.apache.druid.timeline.partition.NoneShardSpec; +import org.apache.druid.timeline.partition.NumberedPartitionChunk; import org.apache.druid.timeline.partition.ShardSpec; import org.apache.druid.timeline.partition.SingleDimensionShardSpec; import org.apache.druid.timeline.partition.SingleElementPartitionChunk; -import org.apache.druid.timeline.partition.StringPartitionChunk; import org.easymock.Capture; import org.easymock.EasyMock; import org.easymock.IAnswer; @@ -1486,19 +1486,19 @@ public void testSingleDimensionPruning() QueryRunner runner = new FinalizeResultsQueryRunner(getDefaultQueryRunner(), new TimeseriesQueryQueryToolChest()); final DruidServer lastServer = servers[random.nextInt(servers.length)]; - ServerSelector selector1 = makeMockSingleDimensionSelector(lastServer, "dim1", null, "b", 1); - ServerSelector selector2 = makeMockSingleDimensionSelector(lastServer, "dim1", "e", "f", 2); - ServerSelector selector3 = makeMockSingleDimensionSelector(lastServer, "dim1", "hi", "zzz", 3); - ServerSelector selector4 = makeMockSingleDimensionSelector(lastServer, "dim2", "a", "e", 4); - ServerSelector selector5 = makeMockSingleDimensionSelector(lastServer, "dim2", null, null, 5); - ServerSelector selector6 = makeMockSingleDimensionSelector(lastServer, "other", "b", null, 6); - - timeline.add(interval1, "v", new StringPartitionChunk<>(null, "a", 1, selector1)); - timeline.add(interval1, "v", new StringPartitionChunk<>("a", "b", 2, selector2)); - timeline.add(interval1, "v", new StringPartitionChunk<>("b", null, 3, selector3)); - timeline.add(interval2, "v", new StringPartitionChunk<>(null, "d", 4, selector4)); - timeline.add(interval2, "v", new StringPartitionChunk<>("d", null, 5, selector5)); - timeline.add(interval3, "v", new StringPartitionChunk<>(null, null, 6, selector6)); + ServerSelector selector1 = makeMockSingleDimensionSelector(lastServer, "dim1", null, "b", 0); + ServerSelector selector2 = makeMockSingleDimensionSelector(lastServer, "dim1", "e", "f", 1); + ServerSelector selector3 = makeMockSingleDimensionSelector(lastServer, "dim1", "hi", "zzz", 2); + ServerSelector selector4 = makeMockSingleDimensionSelector(lastServer, "dim2", "a", "e", 0); + ServerSelector selector5 = makeMockSingleDimensionSelector(lastServer, "dim2", null, null, 1); + ServerSelector selector6 = makeMockSingleDimensionSelector(lastServer, "other", "b", null, 0); + + timeline.add(interval1, "v", new NumberedPartitionChunk<>(0, 3, selector1)); + timeline.add(interval1, "v", new NumberedPartitionChunk<>(1, 3, selector2)); + timeline.add(interval1, "v", new NumberedPartitionChunk<>(2, 3, selector3)); + timeline.add(interval2, "v", new NumberedPartitionChunk<>(0, 2, selector4)); + timeline.add(interval2, "v", new NumberedPartitionChunk<>(1, 2, selector5)); + timeline.add(interval3, "v", new NumberedPartitionChunk<>(0, 1, selector6)); final Capture capture = Capture.newInstance(); final Capture contextCap = Capture.newInstance(); @@ -1514,10 +1514,10 @@ public void testSingleDimensionPruning() EasyMock.replay(mockRunner); List descriptors = new ArrayList<>(); - descriptors.add(new SegmentDescriptor(interval1, "v", 1)); - descriptors.add(new SegmentDescriptor(interval1, "v", 3)); - descriptors.add(new SegmentDescriptor(interval2, "v", 5)); - descriptors.add(new SegmentDescriptor(interval3, "v", 6)); + descriptors.add(new SegmentDescriptor(interval1, "v", 0)); + descriptors.add(new SegmentDescriptor(interval1, "v", 2)); + descriptors.add(new SegmentDescriptor(interval2, "v", 1)); + descriptors.add(new SegmentDescriptor(interval3, "v", 0)); MultipleSpecificSegmentSpec expected = new MultipleSpecificSegmentSpec(descriptors); runner.run(QueryPlus.wrap(query)).toList(); @@ -1538,7 +1538,13 @@ private ServerSelector makeMockSingleDimensionSelector( null, null, null, - new SingleDimensionShardSpec(dimension, start, end, partitionNum), + new SingleDimensionShardSpec( + dimension, + start, + end, + partitionNum, + SingleDimensionShardSpec.UNKNOWN_NUM_CORE_PARTITIONS + ), null, 9, 0L @@ -1966,7 +1972,7 @@ private List> populateTimeline( final ShardSpec shardSpec; if (numChunks == 1) { - shardSpec = new SingleDimensionShardSpec("dimAll", null, null, 0); + shardSpec = new SingleDimensionShardSpec("dimAll", null, null, 0, 1); } else { String start = null; String end = null; @@ -1976,7 +1982,7 @@ private List> populateTimeline( if (j + 1 < numChunks) { end = String.valueOf(j + 1); } - shardSpec = new SingleDimensionShardSpec("dim" + k, start, end, j); + shardSpec = new SingleDimensionShardSpec("dim" + k, start, end, j, numChunks); } DataSegment mockSegment = makeMock(mocks, DataSegment.class); ServerExpectation expectation = new ServerExpectation<>( diff --git a/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java b/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java index 5eb34ffbe28d..bd8e5ef3a8b0 100644 --- a/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java +++ b/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java @@ -1032,7 +1032,7 @@ public void testAllocatePendingSegmentsWithOvershadowingSegments() throws IOExce @Test public void testAllocatePendingSegmentsForHashBasedNumberedShardSpec() throws IOException { - final PartialShardSpec partialShardSpec = new HashBasedNumberedPartialShardSpec(null, 5); + final PartialShardSpec partialShardSpec = new HashBasedNumberedPartialShardSpec(null, 2, 5); final String dataSource = "ds"; final Interval interval = Intervals.of("2017-01-01/2017-02-01"); @@ -1048,7 +1048,8 @@ public void testAllocatePendingSegmentsForHashBasedNumberedShardSpec() throws IO HashBasedNumberedShardSpec shardSpec = (HashBasedNumberedShardSpec) id.getShardSpec(); Assert.assertEquals(0, shardSpec.getPartitionNum()); - Assert.assertEquals(5, shardSpec.getPartitions()); + Assert.assertEquals(0, shardSpec.getNumCorePartitions()); + Assert.assertEquals(5, shardSpec.getNumBuckets()); coordinator.announceHistoricalSegments( Collections.singleton( @@ -1078,7 +1079,8 @@ public void testAllocatePendingSegmentsForHashBasedNumberedShardSpec() throws IO shardSpec = (HashBasedNumberedShardSpec) id.getShardSpec(); Assert.assertEquals(1, shardSpec.getPartitionNum()); - Assert.assertEquals(5, shardSpec.getPartitions()); + Assert.assertEquals(0, shardSpec.getNumCorePartitions()); + Assert.assertEquals(5, shardSpec.getNumBuckets()); coordinator.announceHistoricalSegments( Collections.singleton( @@ -1101,13 +1103,14 @@ public void testAllocatePendingSegmentsForHashBasedNumberedShardSpec() throws IO "seq3", null, interval, - new HashBasedNumberedPartialShardSpec(null, 3), + new HashBasedNumberedPartialShardSpec(null, 2, 3), "version", true ); shardSpec = (HashBasedNumberedShardSpec) id.getShardSpec(); Assert.assertEquals(2, shardSpec.getPartitionNum()); - Assert.assertEquals(3, shardSpec.getPartitions()); + Assert.assertEquals(0, shardSpec.getNumCorePartitions()); + Assert.assertEquals(3, shardSpec.getNumBuckets()); } } diff --git a/server/src/test/java/org/apache/druid/segment/realtime/appenderator/SegmentIdWithShardSpecTest.java b/server/src/test/java/org/apache/druid/segment/realtime/appenderator/SegmentIdWithShardSpecTest.java index 55ff5892fd76..558736980a85 100644 --- a/server/src/test/java/org/apache/druid/segment/realtime/appenderator/SegmentIdWithShardSpecTest.java +++ b/server/src/test/java/org/apache/druid/segment/realtime/appenderator/SegmentIdWithShardSpecTest.java @@ -53,7 +53,7 @@ public void testSerde() throws Exception Assert.assertEquals(INTERVAL, id2.getInterval()); Assert.assertEquals(VERSION, id2.getVersion()); Assert.assertEquals(SHARD_SPEC_1.getPartitionNum(), id2.getShardSpec().getPartitionNum()); - Assert.assertEquals(SHARD_SPEC_1.getPartitions(), ((NumberedShardSpec) id2.getShardSpec()).getPartitions()); + Assert.assertEquals(SHARD_SPEC_1.getNumCorePartitions(), ((NumberedShardSpec) id2.getShardSpec()).getNumCorePartitions()); } @Test diff --git a/server/src/test/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelperTest.java b/server/src/test/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelperTest.java new file mode 100644 index 000000000000..1f2af7298809 --- /dev/null +++ b/server/src/test/java/org/apache/druid/segment/realtime/appenderator/SegmentPublisherHelperTest.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.realtime.appenderator; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import org.apache.druid.java.util.common.Intervals; +import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.partition.BuildingHashBasedNumberedShardSpec; +import org.apache.druid.timeline.partition.BuildingNumberedShardSpec; +import org.apache.druid.timeline.partition.BuildingSingleDimensionShardSpec; +import org.apache.druid.timeline.partition.HashBasedNumberedShardSpec; +import org.apache.druid.timeline.partition.HashBucketShardSpec; +import org.apache.druid.timeline.partition.NumberedOverwriteShardSpec; +import org.apache.druid.timeline.partition.NumberedShardSpec; +import org.apache.druid.timeline.partition.PartitionIds; +import org.apache.druid.timeline.partition.ShardSpec; +import org.apache.druid.timeline.partition.SingleDimensionShardSpec; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.Set; + +public class SegmentPublisherHelperTest +{ + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void testAnnotateAtomicUpdateGroupSize() + { + final Set segments = ImmutableSet.of( + newSegment( + new NumberedOverwriteShardSpec( + PartitionIds.NON_ROOT_GEN_START_PARTITION_ID, + 0, + 3, + (short) 1 + ) + ), + newSegment( + new NumberedOverwriteShardSpec( + PartitionIds.NON_ROOT_GEN_START_PARTITION_ID + 1, + 0, + 3, + (short) 1 + ) + ), + newSegment( + new NumberedOverwriteShardSpec( + PartitionIds.NON_ROOT_GEN_START_PARTITION_ID + 2, + 0, + 3, + (short) 1 + ) + ) + ); + final Set annotated = SegmentPublisherHelper.annotateShardSpec(segments); + for (DataSegment segment : annotated) { + Assert.assertSame(NumberedOverwriteShardSpec.class, segment.getShardSpec().getClass()); + final NumberedOverwriteShardSpec shardSpec = (NumberedOverwriteShardSpec) segment.getShardSpec(); + Assert.assertEquals(3, shardSpec.getAtomicUpdateGroupSize()); + } + } + + @Test + public void testAnnotateCorePartitionSetSizeForNumberedShardSpec() + { + final Set segments = ImmutableSet.of( + newSegment(new BuildingNumberedShardSpec(0)), + newSegment(new BuildingNumberedShardSpec(1)), + newSegment(new BuildingNumberedShardSpec(2)) + ); + final Set annotated = SegmentPublisherHelper.annotateShardSpec(segments); + for (DataSegment segment : annotated) { + Assert.assertSame(NumberedShardSpec.class, segment.getShardSpec().getClass()); + final NumberedShardSpec shardSpec = (NumberedShardSpec) segment.getShardSpec(); + Assert.assertEquals(3, shardSpec.getNumCorePartitions()); + } + } + + @Test + public void testAnnotateCorePartitionSetSizeForHashNumberedShardSpec() + { + final Set segments = ImmutableSet.of( + newSegment(new BuildingHashBasedNumberedShardSpec(0, 0, 3, null, new ObjectMapper())), + newSegment(new BuildingHashBasedNumberedShardSpec(1, 1, 3, null, new ObjectMapper())), + newSegment(new BuildingHashBasedNumberedShardSpec(2, 2, 3, null, new ObjectMapper())) + ); + final Set annotated = SegmentPublisherHelper.annotateShardSpec(segments); + for (DataSegment segment : annotated) { + Assert.assertSame(HashBasedNumberedShardSpec.class, segment.getShardSpec().getClass()); + final HashBasedNumberedShardSpec shardSpec = (HashBasedNumberedShardSpec) segment.getShardSpec(); + Assert.assertEquals(3, shardSpec.getNumCorePartitions()); + } + } + + @Test + public void testAnnotateCorePartitionSetSizeForSingleDimensionShardSpec() + { + final Set segments = ImmutableSet.of( + newSegment(new BuildingSingleDimensionShardSpec(0, "dim", null, "ccc", 0)), + newSegment(new BuildingSingleDimensionShardSpec(1, "dim", null, "ccc", 1)), + newSegment(new BuildingSingleDimensionShardSpec(2, "dim", null, "ccc", 2)) + ); + final Set annotated = SegmentPublisherHelper.annotateShardSpec(segments); + for (DataSegment segment : annotated) { + Assert.assertSame(SingleDimensionShardSpec.class, segment.getShardSpec().getClass()); + final SingleDimensionShardSpec shardSpec = (SingleDimensionShardSpec) segment.getShardSpec(); + Assert.assertEquals(3, shardSpec.getNumCorePartitions()); + } + } + + @Test + public void testAnnotateShardSpecDoNothing() + { + final Set segments = ImmutableSet.of( + newSegment(new NumberedShardSpec(0, 0)), + newSegment(new NumberedShardSpec(1, 0)), + newSegment(new NumberedShardSpec(2, 0)) + ); + final Set annotated = SegmentPublisherHelper.annotateShardSpec(segments); + Assert.assertEquals(segments, annotated); + } + + @Test + public void testAnnotateShardSpecThrowingExceptionForBucketNumberedShardSpec() + { + final Set segments = ImmutableSet.of( + newSegment(new HashBucketShardSpec(0, 3, null, new ObjectMapper())), + newSegment(new HashBucketShardSpec(1, 3, null, new ObjectMapper())), + newSegment(new HashBucketShardSpec(2, 3, null, new ObjectMapper())) + ); + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Cannot publish segments with shardSpec"); + SegmentPublisherHelper.annotateShardSpec(segments); + } + + private static DataSegment newSegment(ShardSpec shardSpec) + { + return new DataSegment( + "datasource", + Intervals.of("2020-01-01/P1d"), + "version", + null, + ImmutableList.of("dim"), + ImmutableList.of("met"), + shardSpec, + 9, + 10L + ); + } +} diff --git a/server/src/test/java/org/apache/druid/server/shard/NumberedShardSpecTest.java b/server/src/test/java/org/apache/druid/server/shard/NumberedShardSpecTest.java index 0cbfc3c347d7..e94b8068424e 100644 --- a/server/src/test/java/org/apache/druid/server/shard/NumberedShardSpecTest.java +++ b/server/src/test/java/org/apache/druid/server/shard/NumberedShardSpecTest.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; +import nl.jqno.equalsverifier.EqualsVerifier; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.server.ServerTestHelper; import org.apache.druid.timeline.Overshadowable; @@ -45,6 +46,12 @@ public class NumberedShardSpecTest { + @Test + public void testEquals() + { + EqualsVerifier.forClass(NumberedShardSpec.class).usingGetClass().verify(); + } + @Test public void testSerdeRoundTrip() throws Exception { @@ -53,7 +60,7 @@ public void testSerdeRoundTrip() throws Exception ShardSpec.class ); Assert.assertEquals(1, spec.getPartitionNum()); - Assert.assertEquals(2, ((NumberedShardSpec) spec).getPartitions()); + Assert.assertEquals(2, ((NumberedShardSpec) spec).getNumCorePartitions()); } @Test @@ -64,7 +71,7 @@ public void testSerdeBackwardsCompat() throws Exception ShardSpec.class ); Assert.assertEquals(1, spec.getPartitionNum()); - Assert.assertEquals(2, ((NumberedShardSpec) spec).getPartitions()); + Assert.assertEquals(2, ((NumberedShardSpec) spec).getNumCorePartitions()); } @Test diff --git a/server/src/test/java/org/apache/druid/server/shard/SingleDimensionShardSpecTest.java b/server/src/test/java/org/apache/druid/server/shard/SingleDimensionShardSpecTest.java index 2722179973e7..0787cf2c119c 100644 --- a/server/src/test/java/org/apache/druid/server/shard/SingleDimensionShardSpecTest.java +++ b/server/src/test/java/org/apache/druid/server/shard/SingleDimensionShardSpecTest.java @@ -158,7 +158,7 @@ private SingleDimensionShardSpec makeSpec(String start, String end) private SingleDimensionShardSpec makeSpec(String dimension, String start, String end) { - return new SingleDimensionShardSpec(dimension, start, end, 0); + return new SingleDimensionShardSpec(dimension, start, end, 0, SingleDimensionShardSpec.UNKNOWN_NUM_CORE_PARTITIONS); } private Map makeMap(String value) diff --git a/server/src/test/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpecTest.java b/server/src/test/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpecTest.java index 9ac4d27823d8..ecb110237498 100644 --- a/server/src/test/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpecTest.java +++ b/server/src/test/java/org/apache/druid/timeline/partition/HashBasedNumberedShardSpecTest.java @@ -19,10 +19,12 @@ package org.apache.druid.timeline.partition; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import nl.jqno.equalsverifier.EqualsVerifier; import org.apache.druid.data.input.InputRow; import org.apache.druid.data.input.MapBasedInputRow; import org.apache.druid.data.input.Row; @@ -41,6 +43,16 @@ public class HashBasedNumberedShardSpecTest { + @Test + public void testEquals() + { + EqualsVerifier.forClass(HashBasedNumberedShardSpec.class) + .withIgnoredFields("jsonMapper") + .withPrefabValues(ObjectMapper.class, new ObjectMapper(), new ObjectMapper()) + .usingGetClass() + .verify(); + } + @Test public void testSerdeRoundTrip() throws Exception { @@ -50,6 +62,8 @@ public void testSerdeRoundTrip() throws Exception new HashBasedNumberedShardSpec( 1, 2, + 1, + 3, ImmutableList.of("visitor_id"), ServerTestHelper.MAPPER ) @@ -57,7 +71,9 @@ public void testSerdeRoundTrip() throws Exception ShardSpec.class ); Assert.assertEquals(1, spec.getPartitionNum()); - Assert.assertEquals(2, ((HashBasedNumberedShardSpec) spec).getPartitions()); + Assert.assertEquals(2, spec.getNumCorePartitions()); + Assert.assertEquals(1, ((HashBasedNumberedShardSpec) spec).getBucketId()); + Assert.assertEquals(3, ((HashBasedNumberedShardSpec) spec).getNumBuckets()); Assert.assertEquals(ImmutableList.of("visitor_id"), ((HashBasedNumberedShardSpec) spec).getPartitionDimensions()); } @@ -69,24 +85,28 @@ public void testSerdeBackwardsCompat() throws Exception ShardSpec.class ); Assert.assertEquals(1, spec.getPartitionNum()); - Assert.assertEquals(2, ((HashBasedNumberedShardSpec) spec).getPartitions()); + Assert.assertEquals(2, ((HashBasedNumberedShardSpec) spec).getNumCorePartitions()); final ShardSpec specWithPartitionDimensions = ServerTestHelper.MAPPER.readValue( "{\"type\": \"hashed\", \"partitions\": 2, \"partitionNum\": 1, \"partitionDimensions\":[\"visitor_id\"]}", ShardSpec.class ); Assert.assertEquals(1, specWithPartitionDimensions.getPartitionNum()); - Assert.assertEquals(2, ((HashBasedNumberedShardSpec) specWithPartitionDimensions).getPartitions()); - Assert.assertEquals(ImmutableList.of("visitor_id"), ((HashBasedNumberedShardSpec) specWithPartitionDimensions).getPartitionDimensions()); + Assert.assertEquals(2, ((HashBasedNumberedShardSpec) specWithPartitionDimensions).getNumCorePartitions()); + Assert.assertEquals(2, ((HashBasedNumberedShardSpec) specWithPartitionDimensions).getNumBuckets()); + Assert.assertEquals( + ImmutableList.of("visitor_id"), + ((HashBasedNumberedShardSpec) specWithPartitionDimensions).getPartitionDimensions() + ); } @Test public void testPartitionChunks() { final List specs = ImmutableList.of( - new HashBasedNumberedShardSpec(0, 3, null, ServerTestHelper.MAPPER), - new HashBasedNumberedShardSpec(1, 3, null, ServerTestHelper.MAPPER), - new HashBasedNumberedShardSpec(2, 3, null, ServerTestHelper.MAPPER) + new HashBasedNumberedShardSpec(0, 3, 0, 3, null, ServerTestHelper.MAPPER), + new HashBasedNumberedShardSpec(1, 3, 1, 3, null, ServerTestHelper.MAPPER), + new HashBasedNumberedShardSpec(2, 3, 2, 3, null, ServerTestHelper.MAPPER) ); final List> chunks = Lists.transform( @@ -157,35 +177,26 @@ public void testIsInChunkWithMorePartitionsBeyondNumBucketsReturningTrue() @Test public void testGetGroupKey() { - final HashBasedNumberedShardSpec shardSpec1 = new HashBasedNumberedShardSpec( - 1, - 2, - ImmutableList.of("visitor_id"), - ServerTestHelper.MAPPER - ); + final List partitionDimensions1 = ImmutableList.of("visitor_id"); final DateTime time = DateTimes.nowUtc(); final InputRow inputRow = new MapBasedInputRow( time, ImmutableList.of("visitor_id", "cnt"), ImmutableMap.of("visitor_id", "v1", "cnt", 10) ); - Assert.assertEquals(ImmutableList.of(Collections.singletonList("v1")), shardSpec1.getGroupKey(time.getMillis(), inputRow)); - - final HashBasedNumberedShardSpec shardSpec2 = new HashBasedNumberedShardSpec( - 1, - 2, - null, - ServerTestHelper.MAPPER + Assert.assertEquals( + ImmutableList.of(Collections.singletonList("v1")), + HashBasedNumberedShardSpec.getGroupKey(partitionDimensions1, time.getMillis(), inputRow) ); - Assert.assertEquals(ImmutableList.of( + + Assert.assertEquals( + ImmutableList.of( time.getMillis(), - ImmutableMap.of( - "cnt", - Collections.singletonList(10), - "visitor_id", - Collections.singletonList("v1") - ) - ).toString(), shardSpec2.getGroupKey(time.getMillis(), inputRow).toString()); + ImmutableMap.of("cnt", Collections.singletonList(10), "visitor_id", Collections.singletonList("v1"))) + .toString(), + // empty list when partitionDimensions is null + HashBasedNumberedShardSpec.getGroupKey(ImmutableList.of(), time.getMillis(), inputRow).toString() + ); } public boolean assertExistsInOneSpec(List specs, InputRow row) @@ -202,7 +213,7 @@ public static class HashOverridenShardSpec extends HashBasedNumberedShardSpec { public HashOverridenShardSpec(int partitionNum, int partitions) { - super(partitionNum, partitions, null, ServerTestHelper.MAPPER); + super(partitionNum, partitions, partitionNum, partitions, null, ServerTestHelper.MAPPER); } @Override From 7510d0db40dbd239d57a75b905035de624efa5de Mon Sep 17 00:00:00 2001 From: Jonathan Wei Date: Thu, 18 Jun 2020 21:32:29 -0700 Subject: [PATCH 097/107] Fix join filter rewrites with nested queries (#10015) * Fix join filter rewrites with nested queries * Fix test, inspection, coverage * Remove clauses from group key * Fix import order Co-authored-by: Gian Merlino --- .../benchmark/JoinAndLookupBenchmark.java | 80 +- .../druid/query/groupby/GroupByQuery.java | 2 +- .../apache/druid/query/scan/ScanQuery.java | 2 +- .../query/timeseries/TimeseriesQuery.java | 2 +- .../apache/druid/query/topn/TopNQuery.java | 2 +- .../druid/segment/join/HashJoinSegment.java | 13 +- .../join/HashJoinSegmentStorageAdapter.java | 25 +- .../apache/druid/segment/join/Joinables.java | 123 +- .../join/filter/JoinFilterAnalyzer.java | 26 +- .../join/filter/JoinFilterPreAnalysis.java | 42 +- .../rewrite/JoinFilterPreAnalysisGroup.java | 149 ++ .../rewrite/JoinFilterRewriteConfig.java | 84 ++ ...BaseHashJoinSegmentStorageAdapterTest.java | 54 +- .../HashJoinSegmentStorageAdapterTest.java | 488 ++---- .../segment/join/HashJoinSegmentTest.java | 39 +- .../segment/join/JoinFilterAnalyzerTest.java | 1319 ++++++++++------- .../druid/segment/join/JoinablesTest.java | 129 +- .../appenderator/SinkQuerySegmentWalker.java | 16 +- .../druid/server/LocalQuerySegmentWalker.java | 15 +- .../server/coordination/ServerManager.java | 16 +- .../server/TestClusterQuerySegmentWalker.java | 15 +- .../druid/sql/calcite/CalciteQueryTest.java | 143 +- 22 files changed, 1584 insertions(+), 1200 deletions(-) create mode 100644 processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/JoinFilterPreAnalysisGroup.java create mode 100644 processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/JoinFilterRewriteConfig.java diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java index 567a34842c90..132a3215927b 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/JoinAndLookupBenchmark.java @@ -49,9 +49,8 @@ import org.apache.druid.segment.join.JoinTestHelper; import org.apache.druid.segment.join.JoinType; import org.apache.druid.segment.join.JoinableClause; -import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; -import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; -import org.apache.druid.segment.join.filter.JoinableClauses; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterPreAnalysisGroup; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import org.apache.druid.segment.join.lookup.LookupJoinable; import org.apache.druid.segment.join.table.IndexedTableJoinable; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; @@ -141,19 +140,20 @@ public void setup() throws IOException ) ) ); - JoinFilterPreAnalysis preAnalysisLookupStringKey = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClausesLookupStringKey), - VirtualColumns.EMPTY, - null, - false, - false, - false, - 0 + JoinFilterPreAnalysisGroup preAnalysisGroupLookupStringKey = new JoinFilterPreAnalysisGroup( + new JoinFilterRewriteConfig( + false, + false, + false, + 0 + ), + true ); + hashJoinLookupStringKeySegment = new HashJoinSegment( ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment), joinableClausesLookupStringKey, - preAnalysisLookupStringKey + preAnalysisGroupLookupStringKey ); List joinableClausesLookupLongKey = ImmutableList.of( @@ -168,19 +168,20 @@ public void setup() throws IOException ) ) ); - JoinFilterPreAnalysis preAnalysisLookupLongKey = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClausesLookupLongKey), - VirtualColumns.EMPTY, - null, - false, - false, - false, - 0 + + JoinFilterPreAnalysisGroup preAnalysisGroupLookupLongKey = new JoinFilterPreAnalysisGroup( + new JoinFilterRewriteConfig( + false, + false, + false, + 0 + ), + true ); hashJoinLookupLongKeySegment = new HashJoinSegment( ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment), joinableClausesLookupLongKey, - preAnalysisLookupLongKey + preAnalysisGroupLookupLongKey ); List joinableClausesIndexedTableStringKey = ImmutableList.of( @@ -195,19 +196,20 @@ public void setup() throws IOException ) ) ); - JoinFilterPreAnalysis preAnalysisIndexedTableStringKey = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClausesIndexedTableStringKey), - VirtualColumns.EMPTY, - null, - false, - false, - false, - 0 + + JoinFilterPreAnalysisGroup preAnalysisGroupIndexedStringKey = new JoinFilterPreAnalysisGroup( + new JoinFilterRewriteConfig( + false, + false, + false, + 0 + ), + true ); hashJoinIndexedTableStringKeySegment = new HashJoinSegment( ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment), joinableClausesIndexedTableStringKey, - preAnalysisIndexedTableStringKey + preAnalysisGroupIndexedStringKey ); List joinableClausesIndexedTableLonggKey = ImmutableList.of( @@ -222,19 +224,19 @@ public void setup() throws IOException ) ) ); - JoinFilterPreAnalysis preAnalysisIndexedTableLongKey = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClausesIndexedTableLonggKey), - VirtualColumns.EMPTY, - null, - false, - false, - false, - 0 + JoinFilterPreAnalysisGroup preAnalysisGroupIndexedLongKey = new JoinFilterPreAnalysisGroup( + new JoinFilterRewriteConfig( + false, + false, + false, + 0 + ), + true ); hashJoinIndexedTableLongKeySegment = new HashJoinSegment( ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment), joinableClausesIndexedTableLonggKey, - preAnalysisIndexedTableLongKey + preAnalysisGroupIndexedLongKey ); final Map countryCodeToNameMap = JoinTestHelper.createCountryIsoCodeToNameLookup().getMap(); diff --git a/processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java b/processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java index 7bd2a847c081..462644425dc1 100644 --- a/processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java +++ b/processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java @@ -249,8 +249,8 @@ private List> verifySubtotalsSpec( return subtotalsSpec; } - @Override @JsonProperty + @Override public VirtualColumns getVirtualColumns() { return virtualColumns; diff --git a/processing/src/main/java/org/apache/druid/query/scan/ScanQuery.java b/processing/src/main/java/org/apache/druid/query/scan/ScanQuery.java index 459b8326d217..be5d24b71b34 100644 --- a/processing/src/main/java/org/apache/druid/query/scan/ScanQuery.java +++ b/processing/src/main/java/org/apache/druid/query/scan/ScanQuery.java @@ -183,8 +183,8 @@ private Integer validateAndGetMaxSegmentPartitionsOrderedInMemory() return maxSegmentPartitionsOrderedInMemory; } - @Override @JsonProperty + @Override public VirtualColumns getVirtualColumns() { return virtualColumns; diff --git a/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQuery.java b/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQuery.java index 47ab8a8c670a..6603ee05a519 100644 --- a/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQuery.java +++ b/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQuery.java @@ -108,8 +108,8 @@ public String getType() return Query.TIMESERIES; } - @Override @JsonProperty + @Override public VirtualColumns getVirtualColumns() { return virtualColumns; diff --git a/processing/src/main/java/org/apache/druid/query/topn/TopNQuery.java b/processing/src/main/java/org/apache/druid/query/topn/TopNQuery.java index 3c301e584af8..f5bdec1982a0 100644 --- a/processing/src/main/java/org/apache/druid/query/topn/TopNQuery.java +++ b/processing/src/main/java/org/apache/druid/query/topn/TopNQuery.java @@ -113,8 +113,8 @@ public String getType() return TOPN; } - @Override @JsonProperty + @Override public VirtualColumns getVirtualColumns() { return virtualColumns; diff --git a/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegment.java b/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegment.java index 3ca240604da6..500e030c8c7d 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegment.java +++ b/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegment.java @@ -25,7 +25,7 @@ import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.StorageAdapter; -import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterPreAnalysisGroup; import org.apache.druid.timeline.SegmentId; import org.joda.time.Interval; @@ -44,23 +44,24 @@ public class HashJoinSegment implements SegmentReference { private final SegmentReference baseSegment; private final List clauses; - private final JoinFilterPreAnalysis joinFilterPreAnalysis; + private final JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup; /** * @param baseSegment The left-hand side base segment * @param clauses The right-hand side clauses. The caller is responsible for ensuring that there are no * duplicate prefixes or prefixes that shadow each other across the clauses - * @param joinFilterPreAnalysis Pre-analysis computed by {@link org.apache.druid.segment.join.filter.JoinFilterAnalyzer#computeJoinFilterPreAnalysis} + * @param joinFilterPreAnalysisGroup Pre-analysis group that holds all of the JoinFilterPreAnalysis results within + * the scope of a query */ public HashJoinSegment( SegmentReference baseSegment, List clauses, - JoinFilterPreAnalysis joinFilterPreAnalysis + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup ) { this.baseSegment = baseSegment; this.clauses = clauses; - this.joinFilterPreAnalysis = joinFilterPreAnalysis; + this.joinFilterPreAnalysisGroup = joinFilterPreAnalysisGroup; // Verify 'clauses' is nonempty (otherwise it's a waste to create this object, and the caller should know) if (clauses.isEmpty()) { @@ -93,7 +94,7 @@ public QueryableIndex asQueryableIndex() @Override public StorageAdapter asStorageAdapter() { - return new HashJoinSegmentStorageAdapter(baseSegment.asStorageAdapter(), clauses, joinFilterPreAnalysis); + return new HashJoinSegmentStorageAdapter(baseSegment.asStorageAdapter(), clauses, joinFilterPreAnalysisGroup); } @Override diff --git a/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapter.java b/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapter.java index 65c25d498b56..8e2518e5cc78 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapter.java +++ b/processing/src/main/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapter.java @@ -21,7 +21,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.granularity.Granularity; import org.apache.druid.java.util.common.guava.Sequence; import org.apache.druid.java.util.common.guava.Sequences; @@ -38,6 +37,7 @@ import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; import org.apache.druid.segment.join.filter.JoinFilterSplit; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterPreAnalysisGroup; import org.joda.time.DateTime; import org.joda.time.Interval; @@ -47,7 +47,6 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -55,22 +54,23 @@ public class HashJoinSegmentStorageAdapter implements StorageAdapter { private final StorageAdapter baseAdapter; private final List clauses; - private final JoinFilterPreAnalysis joinFilterPreAnalysis; + private final JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup; /** * @param baseAdapter A StorageAdapter for the left-hand side base segment * @param clauses The right-hand side clauses. The caller is responsible for ensuring that there are no - * duplicate prefixes or prefixes that shadow each other across the clauses + * @param joinFilterPreAnalysisGroup Pre-analysis group that holds all of the JoinFilterPreAnalysis results within + * the scope of a query */ HashJoinSegmentStorageAdapter( StorageAdapter baseAdapter, List clauses, - final JoinFilterPreAnalysis joinFilterPreAnalysis + final JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup ) { this.baseAdapter = baseAdapter; this.clauses = clauses; - this.joinFilterPreAnalysis = joinFilterPreAnalysis; + this.joinFilterPreAnalysisGroup = joinFilterPreAnalysisGroup; } @Override @@ -209,13 +209,16 @@ public Sequence makeCursors( @Nullable final QueryMetrics queryMetrics ) { - if (!Objects.equals(joinFilterPreAnalysis.getOriginalFilter(), filter)) { - throw new ISE( - "Filter provided to cursor [%s] does not match join pre-analysis filter [%s]", + JoinFilterPreAnalysis jfpa; + if (joinFilterPreAnalysisGroup.isSingleLevelMode()) { + jfpa = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + } else { + jfpa = joinFilterPreAnalysisGroup.getAnalysis( filter, - joinFilterPreAnalysis.getOriginalFilter() + virtualColumns ); } + final List preJoinVirtualColumns = new ArrayList<>(); final List postJoinVirtualColumns = new ArrayList<>(); @@ -225,7 +228,7 @@ public Sequence makeCursors( postJoinVirtualColumns ); - JoinFilterSplit joinFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + JoinFilterSplit joinFilterSplit = JoinFilterAnalyzer.splitFilter(jfpa); preJoinVirtualColumns.addAll(joinFilterSplit.getPushDownVirtualColumns()); // Soon, we will need a way to push filters past a join when possible. This could potentially be done right here diff --git a/processing/src/main/java/org/apache/druid/segment/join/Joinables.java b/processing/src/main/java/org/apache/druid/segment/join/Joinables.java index c9a9d2fc2478..6ebe970ae29e 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/Joinables.java +++ b/processing/src/main/java/org/apache/druid/segment/join/Joinables.java @@ -20,17 +20,20 @@ package org.apache.druid.segment.join; import org.apache.druid.java.util.common.IAE; -import org.apache.druid.query.filter.Filter; +import org.apache.druid.query.DataSource; +import org.apache.druid.query.JoinDataSource; +import org.apache.druid.query.Query; +import org.apache.druid.query.QueryDataSource; import org.apache.druid.query.planning.PreJoinableClause; import org.apache.druid.segment.SegmentReference; -import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.column.ColumnHolder; -import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; -import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; import org.apache.druid.segment.join.filter.JoinableClauses; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterPreAnalysisGroup; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import org.apache.druid.utils.JvmUtils; import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.concurrent.atomic.AtomicLong; @@ -71,33 +74,19 @@ public static boolean isPrefixedBy(final String columnName, final String prefix) /** * Creates a Function that maps base segments to {@link HashJoinSegment} if needed (i.e. if the number of join * clauses is > 0). If mapping is not needed, this method will return {@link Function#identity()}. - * - * @param clauses pre-joinable clauses - * @param joinableFactory factory for joinables - * @param cpuTimeAccumulator an accumulator that we will add CPU nanos to; this is part of the function to encourage - * callers to remember to track metrics on CPU time required for creation of Joinables - * @param enableFilterPushDown whether to enable filter push down optimizations to the base segment. In production - * this should generally be {@code QueryContexts.getEnableJoinFilterPushDown(query)}. - * @param enableFilterRewrite whether to enable filter rewrite optimizations for RHS columns. In production - * this should generally be {@code QueryContexts.getEnableJoinFilterRewrite(query)}. - * @param enableRewriteValueColumnFilters whether to enable filter rewrite optimizations for RHS columns that are not - * key columns. In production this should generally - * be {@code QueryContexts.getEnableJoinFilterRewriteValueColumnFilters(query)}. - * @param filterRewriteMaxSize the max allowed size of correlated value sets for RHS rewrites. In production - * this should generally be {@code QueryContexts.getJoinFilterRewriteMaxSize(query)}. - * @param originalFilter The original filter from the query. - * @param virtualColumns The virtual columns from the query. + * @param clauses Pre-joinable clauses + * @param joinableFactory Factory for joinables + * @param cpuTimeAccumulator An accumulator that we will add CPU nanos to; this is part of the function to encourage + * callers to remember to track metrics on CPU time required for creation of Joinables + * @param joinFilterRewriteConfig Configuration options for the join filter rewrites + * @param query The query being processed */ public static Function createSegmentMapFn( final List clauses, final JoinableFactory joinableFactory, final AtomicLong cpuTimeAccumulator, - final boolean enableFilterPushDown, - final boolean enableFilterRewrite, - final boolean enableRewriteValueColumnFilters, - final long filterRewriteMaxSize, - final Filter originalFilter, - final VirtualColumns virtualColumns + final JoinFilterRewriteConfig joinFilterRewriteConfig, + final Query query ) { // compute column correlations here and RHS correlated values @@ -108,21 +97,85 @@ public static Function createSegmentMapFn( return Function.identity(); } else { final JoinableClauses joinableClauses = JoinableClauses.createClauses(clauses, joinableFactory); - JoinFilterPreAnalysis jfpa = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, - virtualColumns, - originalFilter, - enableFilterPushDown, - enableFilterRewrite, - enableRewriteValueColumnFilters, - filterRewriteMaxSize + + List joinQueryLevels = new ArrayList<>(); + Joinables.gatherAllJoinQueryLevels(query, joinQueryLevels); + + final JoinFilterPreAnalysisGroup preAnalysisGroup = new JoinFilterPreAnalysisGroup( + joinFilterRewriteConfig, + joinQueryLevels.size() <= 1 // use single-level mode if there's one or fewer query levels with joins ); - return baseSegment -> new HashJoinSegment(baseSegment, joinableClauses.getJoinableClauses(), jfpa); + + for (Query joinQuery : joinQueryLevels) { + preAnalysisGroup.computeJoinFilterPreAnalysisIfAbsent( + joinQuery.getFilter() == null ? null : joinQuery.getFilter().toFilter(), + joinableClauses.getJoinableClauses(), + joinQuery.getVirtualColumns() + ); + } + + return baseSegment -> new HashJoinSegment(baseSegment, joinableClauses.getJoinableClauses(), preAnalysisGroup); } } ); } + /** + * Walks a query and its subqueries, finding any queries that read from a JoinDatasource, + * and adding them to a list provided by the caller. + * + * @param currentLevelQuery The query to analyze + * @param allJoinQueryLevels A mutable list provided by the caller. + */ + public static void gatherAllJoinQueryLevels(Query currentLevelQuery, List allJoinQueryLevels) + { + DataSource currentDatasource = currentLevelQuery.getDataSource(); + if (currentDatasource instanceof QueryDataSource) { + gatherAllJoinQueryLevels( + ((QueryDataSource) currentDatasource).getQuery(), + allJoinQueryLevels + ); + } + if (currentDatasource instanceof JoinDataSource) { + allJoinQueryLevels.add(currentLevelQuery); + gatherAllJoinQueryLevelsJoinDatasourceHelper( + (JoinDataSource) currentDatasource, + allJoinQueryLevels + ); + } + } + + private static void gatherAllJoinQueryLevelsJoinDatasourceHelper( + JoinDataSource joinDatasource, + List allJoinQueryLevels + ) + { + if (joinDatasource.getLeft() instanceof QueryDataSource) { + gatherAllJoinQueryLevels( + ((QueryDataSource) joinDatasource.getLeft()).getQuery(), + allJoinQueryLevels + ); + } + if (joinDatasource.getLeft() instanceof JoinDataSource) { + gatherAllJoinQueryLevelsJoinDatasourceHelper( + (JoinDataSource) joinDatasource.getLeft(), + allJoinQueryLevels + ); + } + if (joinDatasource.getRight() instanceof QueryDataSource) { + gatherAllJoinQueryLevels( + ((QueryDataSource) joinDatasource.getRight()).getQuery(), + allJoinQueryLevels + ); + } + if (joinDatasource.getRight() instanceof JoinDataSource) { + gatherAllJoinQueryLevelsJoinDatasourceHelper( + (JoinDataSource) joinDatasource.getRight(), + allJoinQueryLevels + ); + } + } + /** * Check if any prefixes in the provided list duplicate or shadow each other. * diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java index f4f7d31be1cf..06475ad89e51 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterAnalyzer.java @@ -32,6 +32,7 @@ import org.apache.druid.segment.filter.Filters; import org.apache.druid.segment.filter.OrFilter; import org.apache.druid.segment.filter.SelectorFilter; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; import java.util.ArrayList; @@ -85,23 +86,14 @@ public class JoinFilterAnalyzer * @param joinableClauses The joinable clauses from the query * @param virtualColumns The virtual columns from the query * @param originalFilter The original filter from the query - * @param enableFilterPushDown Whether to enable filter push down - * @param enableFilterRewrite Whether to enable rewrites of filters involving RHS columns - * @param enableRewriteValueColumnFilters Whether to enable rewrites of filters invovling RHS non-key columns - * @param filterRewriteMaxSize The maximum size of the correlated value set for rewritten filters. - * If the correlated value set size exceeds this, the filter will not be - * rewritten and pushed down. - * + * @param joinFilterRewriteConfig Configuration options for the join rewrites * @return A JoinFilterPreAnalysis containing information determined in this pre-analysis step. */ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( JoinableClauses joinableClauses, VirtualColumns virtualColumns, Filter originalFilter, - boolean enableFilterPushDown, - boolean enableFilterRewrite, - boolean enableRewriteValueColumnFilters, - long filterRewriteMaxSize + JoinFilterRewriteConfig joinFilterRewriteConfig ) { final List preJoinVirtualColumns = new ArrayList<>(); @@ -109,10 +101,8 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( joinableClauses.splitVirtualColumns(virtualColumns, preJoinVirtualColumns, postJoinVirtualColumns); JoinFilterPreAnalysis.Builder preAnalysisBuilder = - new JoinFilterPreAnalysis.Builder(joinableClauses, originalFilter, postJoinVirtualColumns) - .withEnableFilterPushDown(enableFilterPushDown) - .withEnableFilterRewrite(enableFilterRewrite); - if (originalFilter == null || !enableFilterPushDown) { + new JoinFilterPreAnalysis.Builder(joinFilterRewriteConfig, joinableClauses, originalFilter, postJoinVirtualColumns); + if (originalFilter == null || !joinFilterRewriteConfig.isEnableFilterPushDown()) { return preAnalysisBuilder.build(); } @@ -135,7 +125,7 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( preAnalysisBuilder .withNormalizedBaseTableClauses(normalizedBaseTableClauses) .withNormalizedJoinTableClauses(normalizedJoinTableClauses); - if (!enableFilterRewrite) { + if (!joinFilterRewriteConfig.isEnableFilterRewrite()) { return preAnalysisBuilder.build(); } @@ -146,8 +136,8 @@ public static JoinFilterPreAnalysis computeJoinFilterPreAnalysis( normalizedJoinTableClauses, equiconditions, joinableClauses, - enableRewriteValueColumnFilters, - filterRewriteMaxSize + joinFilterRewriteConfig.isEnableRewriteValueColumnFilters(), + joinFilterRewriteConfig.getFilterRewriteMaxSize() ); return preAnalysisBuilder.withCorrelations(correlations) diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java index ae3731db77a2..de842c3dde02 100644 --- a/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/JoinFilterPreAnalysis.java @@ -24,6 +24,7 @@ import org.apache.druid.segment.VirtualColumn; import org.apache.druid.segment.join.Equality; import org.apache.druid.segment.join.JoinableClause; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -38,11 +39,11 @@ * A JoinFilterPreAnalysis contains filter push down/rewrite information that does not have per-segment dependencies. * This includes: * - The query's JoinableClauses list - * - The query's original filter (if any) + * - The original filter that an analysis was performed ons * - A list of filter clauses from the original filter's CNF representation that only reference the base table * - A list of filter clauses from the original filter's CNF representation that reference RHS join tables * - A list of virtual columns that can only be computed post-join - * - Control flag booleans for whether filter push down and RHS rewrites are enabled. + * - The JoinFilterRewriteConfig that this pre-analysis is associated with. */ public class JoinFilterPreAnalysis { @@ -51,10 +52,9 @@ public class JoinFilterPreAnalysis private final List normalizedBaseTableClauses; private final List normalizedJoinTableClauses; private final JoinFilterCorrelations correlations; - private final boolean enableFilterPushDown; - private final boolean enableFilterRewrite; private final List postJoinVirtualColumns; private final Equiconditions equiconditions; + private final JoinFilterRewriteConfig rewriteConfig; private JoinFilterPreAnalysis( final JoinableClauses joinableClauses, @@ -63,9 +63,8 @@ private JoinFilterPreAnalysis( final List normalizedBaseTableClauses, final List normalizedJoinTableClauses, JoinFilterCorrelations correlations, - final boolean enableFilterPushDown, - final boolean enableFilterRewrite, - final Equiconditions equiconditions + final Equiconditions equiconditions, + final JoinFilterRewriteConfig rewriteConfig ) { this.joinableClauses = joinableClauses; @@ -74,8 +73,7 @@ private JoinFilterPreAnalysis( this.normalizedBaseTableClauses = normalizedBaseTableClauses; this.normalizedJoinTableClauses = normalizedJoinTableClauses; this.correlations = correlations; - this.enableFilterPushDown = enableFilterPushDown; - this.enableFilterRewrite = enableFilterRewrite; + this.rewriteConfig = rewriteConfig; this.equiconditions = equiconditions; } @@ -116,12 +114,12 @@ public Map> getCorrelationsByD public boolean isEnableFilterPushDown() { - return enableFilterPushDown; + return rewriteConfig.isEnableFilterPushDown(); } public boolean isEnableFilterRewrite() { - return enableFilterRewrite; + return rewriteConfig.isEnableFilterRewrite(); } public Equiconditions getEquiconditions() @@ -134,22 +132,23 @@ public Equiconditions getEquiconditions() */ public static class Builder { + @Nonnull private final JoinFilterRewriteConfig rewriteConfig; @Nonnull private final JoinableClauses joinableClauses; @Nullable private final Filter originalFilter; @Nullable private List normalizedBaseTableClauses; @Nullable private List normalizedJoinTableClauses; @Nullable private JoinFilterCorrelations correlations; - private boolean enableFilterPushDown = false; - private boolean enableFilterRewrite = false; @Nonnull private final List postJoinVirtualColumns; @Nonnull private Equiconditions equiconditions = new Equiconditions(Collections.emptyMap()); public Builder( + @Nonnull JoinFilterRewriteConfig rewriteConfig, @Nonnull JoinableClauses joinableClauses, @Nullable Filter originalFilter, @Nonnull List postJoinVirtualColumns ) { + this.rewriteConfig = rewriteConfig; this.joinableClauses = joinableClauses; this.originalFilter = originalFilter; this.postJoinVirtualColumns = postJoinVirtualColumns; @@ -175,18 +174,6 @@ public Builder withCorrelations( return this; } - public Builder withEnableFilterPushDown(boolean enableFilterPushDown) - { - this.enableFilterPushDown = enableFilterPushDown; - return this; - } - - public Builder withEnableFilterRewrite(boolean enableFilterRewrite) - { - this.enableFilterRewrite = enableFilterRewrite; - return this; - } - public Equiconditions computeEquiconditionsFromJoinableClauses() { Map> equiconditionsMap = new HashMap<>(); @@ -212,9 +199,8 @@ public JoinFilterPreAnalysis build() normalizedBaseTableClauses, normalizedJoinTableClauses, correlations, - enableFilterPushDown, - enableFilterRewrite, - equiconditions + equiconditions, + rewriteConfig ); } diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/JoinFilterPreAnalysisGroup.java b/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/JoinFilterPreAnalysisGroup.java new file mode 100644 index 000000000000..01d4b1d21f51 --- /dev/null +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/JoinFilterPreAnalysisGroup.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.join.filter.rewrite; + +import org.apache.druid.query.filter.Filter; +import org.apache.druid.segment.VirtualColumns; +import org.apache.druid.segment.join.JoinableClause; +import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; +import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; +import org.apache.druid.segment.join.filter.JoinableClauses; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * A JoinFilterPreAnalysisGroup holds all of the JoinFilterPreAnalysis objects for a given query and + * also stores the per-query parameters that control the filter rewrite operations (from the query context). + * + * The analyses map is keyed by (Filter, JoinableClause list, VirtualColumns): each Filter in the map belongs to a + * separate level of query (e.g. outer query, subquery level 1, etc.) + * + * If there is only a single Filter, then this class does not use the analyses map, instead of using a single reference + * for efficiency reasons. + */ +public class JoinFilterPreAnalysisGroup +{ + private final JoinFilterRewriteConfig joinFilterRewriteConfig; + private final Map analyses; + private final boolean isSingleLevelMode; + + /** + * Hashing and comparing filters can be expensive for large filters, so if we're only dealing with + * a single level of join query, then we can be more efficient by using a single reference instead of a map. + */ + private JoinFilterPreAnalysis preAnalysisForSingleLevelMode; + + public JoinFilterPreAnalysisGroup( + JoinFilterRewriteConfig joinFilterRewriteConfig, + boolean isSingleLevelMode + ) + { + this.joinFilterRewriteConfig = joinFilterRewriteConfig; + this.analyses = new HashMap<>(); + this.isSingleLevelMode = isSingleLevelMode; + } + + public boolean isSingleLevelMode() + { + return isSingleLevelMode; + } + + public JoinFilterPreAnalysis computeJoinFilterPreAnalysisIfAbsent( + Filter filter, + List clauses, + VirtualColumns virtualColumns + ) + { + if (isSingleLevelMode) { + preAnalysisForSingleLevelMode = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( + JoinableClauses.fromList(clauses), + virtualColumns, + filter, + joinFilterRewriteConfig + ); + return preAnalysisForSingleLevelMode; + } + + JoinFilterPreAnalysisGroupKey key = new JoinFilterPreAnalysisGroupKey(filter, virtualColumns); + return analyses.computeIfAbsent( + key, + (groupKey) -> { + return JoinFilterAnalyzer.computeJoinFilterPreAnalysis( + JoinableClauses.fromList(clauses), + virtualColumns, + filter, + joinFilterRewriteConfig + ); + } + ); + } + + public JoinFilterPreAnalysis getAnalysis( + Filter filter, + VirtualColumns virtualColumns + ) + { + JoinFilterPreAnalysisGroupKey key = new JoinFilterPreAnalysisGroupKey(filter, virtualColumns); + return analyses.get(key); + } + + public JoinFilterPreAnalysis getPreAnalysisForSingleLevelMode() + { + return preAnalysisForSingleLevelMode; + } + + public static class JoinFilterPreAnalysisGroupKey + { + private final Filter filter; + private final VirtualColumns virtualColumns; + + public JoinFilterPreAnalysisGroupKey( + Filter filter, + VirtualColumns virtualColumns + ) + { + this.filter = filter; + this.virtualColumns = virtualColumns; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + JoinFilterPreAnalysisGroupKey that = (JoinFilterPreAnalysisGroupKey) o; + return Objects.equals(filter, that.filter) && + Objects.equals(virtualColumns, that.virtualColumns); + } + + @Override + public int hashCode() + { + return Objects.hash(filter, virtualColumns); + } + } +} diff --git a/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/JoinFilterRewriteConfig.java b/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/JoinFilterRewriteConfig.java new file mode 100644 index 000000000000..ed948bcc14f7 --- /dev/null +++ b/processing/src/main/java/org/apache/druid/segment/join/filter/rewrite/JoinFilterRewriteConfig.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.segment.join.filter.rewrite; + +/** + * A config class that holds properties that control how join filter rewrites behave. + */ +public class JoinFilterRewriteConfig +{ + /** + * Whether to enable filter push down optimizations to the base segment. + * In production this should generally be {@code QueryContexts.getEnableJoinFilterPushDown(query)}. + */ + private final boolean enableFilterPushDown; + + /** + * Whether to enable filter rewrite optimizations for RHS columns. + * In production this should generally be {@code QueryContexts.getEnableJoinFilterRewrite(query)}. + */ + private final boolean enableFilterRewrite; + + /** + * Whether to enable filter rewrite optimizations for RHS columns that are not key columns. + * In production this should generally be {@code QueryContexts.getEnableJoinFilterRewriteValueColumnFilters(query)}. + */ + private final boolean enableRewriteValueColumnFilters; + + /** + * The max allowed size of correlated value sets for RHS rewrites. In production + * This should generally be {@code QueryContexts.getJoinFilterRewriteMaxSize(query)}. + */ + private final long filterRewriteMaxSize; + + public JoinFilterRewriteConfig( + boolean enableFilterPushDown, + boolean enableFilterRewrite, + boolean enableRewriteValueColumnFilters, + long filterRewriteMaxSize + ) + { + this.enableFilterPushDown = enableFilterPushDown; + this.enableFilterRewrite = enableFilterRewrite; + this.enableRewriteValueColumnFilters = enableRewriteValueColumnFilters; + this.filterRewriteMaxSize = filterRewriteMaxSize; + } + + public boolean isEnableFilterPushDown() + { + return enableFilterPushDown; + } + + public boolean isEnableFilterRewrite() + { + return enableFilterRewrite; + } + + public boolean isEnableRewriteValueColumnFilters() + { + return enableRewriteValueColumnFilters; + } + + public long getFilterRewriteMaxSize() + { + return filterRewriteMaxSize; + } + +} diff --git a/processing/src/test/java/org/apache/druid/segment/join/BaseHashJoinSegmentStorageAdapterTest.java b/processing/src/test/java/org/apache/druid/segment/join/BaseHashJoinSegmentStorageAdapterTest.java index 45befeba36ed..5fd495340b82 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/BaseHashJoinSegmentStorageAdapterTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/BaseHashJoinSegmentStorageAdapterTest.java @@ -24,12 +24,12 @@ import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.math.expr.ExprMacroTable; import org.apache.druid.query.QueryContexts; +import org.apache.druid.query.filter.Filter; import org.apache.druid.query.lookup.LookupExtractor; import org.apache.druid.segment.QueryableIndexSegment; import org.apache.druid.segment.VirtualColumns; -import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; -import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; -import org.apache.druid.segment.join.filter.JoinableClauses; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterPreAnalysisGroup; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import org.apache.druid.segment.join.lookup.LookupJoinable; import org.apache.druid.segment.join.table.IndexedTable; import org.apache.druid.segment.join.table.IndexedTableJoinable; @@ -44,9 +44,17 @@ import org.junit.rules.TemporaryFolder; import java.io.IOException; +import java.util.List; public class BaseHashJoinSegmentStorageAdapterTest { + public static JoinFilterRewriteConfig DEFAULT_JOIN_FILTER_REWRITE_CONFIG = new JoinFilterRewriteConfig( + true, + true, + true, + QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + ); + public static final String FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX = "c1."; public static final String FACT_TO_COUNTRY_ON_NUMBER_PREFIX = "c2."; public static final String FACT_TO_REGION_PREFIX = "r1."; @@ -187,20 +195,12 @@ protected JoinableClause regionToCountry(final JoinType joinType) protected HashJoinSegmentStorageAdapter makeFactToCountrySegment() { - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT))), - VirtualColumns.EMPTY, - null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE - ); + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup(); return new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT)), - preAnalysis + joinFilterPreAnalysisGroup ); } @@ -222,4 +222,32 @@ protected void compareExpressionVirtualColumns( actualVirtualColumn.getParsedExpression().get().toString() ); } + + protected static JoinFilterPreAnalysisGroup makeDefaultConfigPreAnalysisGroup() + { + return new JoinFilterPreAnalysisGroup( + DEFAULT_JOIN_FILTER_REWRITE_CONFIG, + true + ); + } + + protected static JoinFilterPreAnalysisGroup makeDefaultConfigPreAnalysisGroup( + Filter originalFilter, + List joinableClauses, + VirtualColumns virtualColumns + ) + { + JoinFilterPreAnalysisGroup group = new JoinFilterPreAnalysisGroup( + DEFAULT_JOIN_FILTER_REWRITE_CONFIG, + true + ); + + group.computeJoinFilterPreAnalysisIfAbsent( + originalFilter, + joinableClauses, + virtualColumns + ); + + return group; + } } diff --git a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java index 2e863b2487a2..ed6e1782f3a4 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentStorageAdapterTest.java @@ -23,12 +23,10 @@ import com.google.common.collect.Lists; import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.DateTimes; -import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.java.util.common.granularity.Granularities; import org.apache.druid.math.expr.ExprMacroTable; -import org.apache.druid.query.QueryContexts; import org.apache.druid.query.filter.ExpressionDimFilter; import org.apache.druid.query.filter.Filter; import org.apache.druid.query.filter.OrDimFilter; @@ -37,9 +35,7 @@ import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.filter.SelectorFilter; -import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; -import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; -import org.apache.druid.segment.join.filter.JoinableClauses; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterPreAnalysisGroup; import org.apache.druid.segment.join.lookup.LookupJoinable; import org.apache.druid.segment.join.table.IndexedTableJoinable; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; @@ -302,21 +298,16 @@ public void test_makeCursors_factToCountryLeft() { List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT)); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -371,21 +362,16 @@ public void test_makeCursors_factToCountryLeftUsingLookup() { List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.LEFT)); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -438,21 +424,16 @@ public void test_makeCursors_factToCountryLeftUsingLookup() public void test_makeCursors_factToCountryInner() { List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.INNER)); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -500,21 +481,16 @@ public void test_makeCursors_factToCountryInner() public void test_makeCursors_factToCountryInnerUsingLookup() { List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.INNER)); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -564,21 +540,16 @@ public void test_makeCursors_factToCountryInnerUsingCountryNumber() // is interpreted as 0 (a.k.a. Australia). List joinableClauses = ImmutableList.of(factToCountryOnNumber(JoinType.INNER)); Filter filter = new SelectorDimFilter("channel", "#en.wikipedia", null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -634,21 +605,16 @@ public void test_makeCursors_factToCountryInnerUsingCountryNumberUsingLookup() // is interpreted as 0 (a.k.a. Australia). List joinableClauses = ImmutableList.of(factToCountryNameUsingNumberLookup(JoinType.INNER)); Filter filter = new SelectorDimFilter("channel", "#en.wikipedia", null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -700,21 +666,16 @@ public void test_makeCursors_factToCountryLeftWithFilterOnFacts() { List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT)); Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -741,21 +702,16 @@ public void test_makeCursors_factToCountryLeftWithFilterOnFactsUsingLookup() { List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.LEFT)); Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -781,21 +737,16 @@ public void test_makeCursors_factToCountryRightWithFilterOnLeftIsNull() { List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.RIGHT)); Filter filter = new SelectorDimFilter("channel", null, null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -824,21 +775,16 @@ public void test_makeCursors_factToCountryRightWithFilterOnLeftIsNullUsingLookup { List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.RIGHT)); Filter filter = new SelectorDimFilter("channel", null, null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -866,21 +812,16 @@ public void test_makeCursors_factToCountryFullWithFilterOnLeftIsNull() { List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.FULL)); Filter filter = new SelectorDimFilter("channel", null, null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -909,21 +850,16 @@ public void test_makeCursors_factToCountryFullWithFilterOnLeftIsNullUsingLookup( { List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.FULL)); Filter filter = new SelectorDimFilter("channel", null, null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -956,21 +892,16 @@ public void test_makeCursors_factToCountryRightWithFilterOnJoinable() null ).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -1003,21 +934,16 @@ public void test_makeCursors_factToCountryRightWithFilterOnJoinableUsingLookup() null ).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -1050,21 +976,16 @@ public void test_makeCursors_factToCountryLeftWithFilterOnJoinable() new SelectorDimFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryNumber", "10", null) ).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -1097,21 +1018,16 @@ public void test_makeCursors_factToCountryLeftWithFilterOnJoinableUsingLookup() new SelectorDimFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", "Norway", null) ).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -1157,21 +1073,16 @@ public void test_makeCursors_factToCountryInnerWithFilterInsteadOfRealJoinCondit ExprMacroTable.nil() ).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -1238,22 +1149,16 @@ public void test_makeCursors_factToCountryInnerWithFilterInsteadOfRealJoinCondit StringUtils.format("\"%sk\" == countryIsoCode", FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX), ExprMacroTable.nil() ).toFilter(); - - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -1303,22 +1208,16 @@ public void test_makeCursors_factToRegionToCountryLeft() factToRegion(JoinType.LEFT), regionToCountry(JoinType.LEFT) ); - - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -1383,20 +1282,16 @@ public void test_makeCursors_factToCountryAlwaysTrue() ); Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -1450,21 +1345,17 @@ public void test_makeCursors_factToCountryAlwaysFalse() Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -1501,21 +1392,16 @@ public void test_makeCursors_factToCountryAlwaysTrueUsingLookup() Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -1569,21 +1455,17 @@ public void test_makeCursors_factToCountryAlwaysFalseUsingLookup() Filter filter = new SelectorDimFilter("channel", "#de.wikipedia", null).toFilter(); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -1629,21 +1511,16 @@ public void test_makeCursors_factToCountryUsingVirtualColumn() ) ); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - virtualColumns, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + virtualColumns ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -1695,21 +1572,16 @@ public void test_makeCursors_factToCountryUsingVirtualColumnUsingLookup() ) ); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - virtualColumns, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + virtualColumns ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -1753,21 +1625,16 @@ public void test_makeCursors_factToCountryUsingExpression() ) ); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -1810,21 +1677,16 @@ public void test_makeCursors_factToCountryUsingExpressionUsingLookup() ) ); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -1869,22 +1731,16 @@ public void test_makeCursors_factToRegionTheWrongWay() ); Filter filter = new SelectorDimFilter("regionIsoCode", "VA", null).toFilter(); - - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( filter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( filter, Intervals.ETERNITY, @@ -1930,21 +1786,17 @@ public void test_makeCursors_errorOnNonEquiJoin() ) ); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); JoinTestHelper.readCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -1976,21 +1828,17 @@ public void test_makeCursors_errorOnNonEquiJoinUsingLookup() ) ); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); JoinTestHelper.readCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -2022,21 +1870,17 @@ public void test_makeCursors_errorOnNonKeyBasedJoin() ) ); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); JoinTestHelper.readCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -2067,21 +1911,17 @@ public void test_makeCursors_errorOnNonKeyBasedJoinUsingLookup() ) ); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); JoinTestHelper.readCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( null, Intervals.ETERNITY, @@ -2100,21 +1940,16 @@ public void test_makeCursors_factToCountryLeft_filterExcludesAllLeftRows() Filter originalFilter = new SelectorFilter("page", "this matches nothing"); List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT)); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( originalFilter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( originalFilter, Intervals.ETERNITY, @@ -2139,21 +1974,16 @@ public void test_makeCursors_factToCountryLeft_filterExcludesAllLeftRowsUsingLoo { Filter originalFilter = new SelectorFilter("page", "this matches nothing"); List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.LEFT)); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( originalFilter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses, + VirtualColumns.EMPTY ); - JoinTestHelper.verifyCursors( new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - preAnalysis + joinFilterPreAnalysisGroup ).makeCursors( originalFilter, Intervals.ETERNITY, @@ -2176,35 +2006,25 @@ public void test_makeCursors_factToCountryLeft_filterExcludesAllLeftRowsUsingLoo public void test_makeCursors_originalFilterDoesNotMatchPreAnalysis_shouldThrowISE() { List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.LEFT)); + Filter filter = new SelectorFilter("page", "this matches nothing"); + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + filter, + joinableClauses, + VirtualColumns.EMPTY + ); - JoinFilterPreAnalysis preAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), + new HashJoinSegmentStorageAdapter( + factSegment.asStorageAdapter(), + joinableClauses, + joinFilterPreAnalysisGroup + ).makeCursors( + filter, + Intervals.ETERNITY, VirtualColumns.EMPTY, - null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + Granularities.ALL, + false, + null ); - Filter filter = new SelectorFilter("page", "this matches nothing"); - - try { - new HashJoinSegmentStorageAdapter( - factSegment.asStorageAdapter(), - joinableClauses, - preAnalysis - ).makeCursors( - filter, - Intervals.ETERNITY, - VirtualColumns.EMPTY, - Granularities.ALL, - false, - null - ); - Assert.fail(); - } - catch (ISE e) { - Assert.assertTrue(e.getMessage().startsWith("Filter provided to cursor [")); - } } + } diff --git a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java index 966d62023ffe..d04ebc83354a 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/HashJoinSegmentTest.java @@ -28,10 +28,8 @@ import org.apache.druid.segment.ReferenceCountingSegment; import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.StorageAdapter; -import org.apache.druid.segment.VirtualColumns; -import org.apache.druid.segment.join.filter.JoinFilterAnalyzer; -import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; -import org.apache.druid.segment.join.filter.JoinableClauses; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterPreAnalysisGroup; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import org.apache.druid.segment.join.table.IndexedTableJoinable; import org.apache.druid.testing.InitializedNullHandlingTest; import org.apache.druid.timeline.SegmentId; @@ -52,6 +50,13 @@ public class HashJoinSegmentTest extends InitializedNullHandlingTest { + private JoinFilterRewriteConfig DEFAULT_JOIN_FILTER_REWRITE_CONFIG = new JoinFilterRewriteConfig( + true, + true, + true, + QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + ); + @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -129,14 +134,9 @@ public Optional acquireReferences() ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, - null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = new JoinFilterPreAnalysisGroup( + DEFAULT_JOIN_FILTER_REWRITE_CONFIG, + true ); referencedSegment = ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment); @@ -188,7 +188,7 @@ public void close() hashJoinSegment = new HashJoinSegment( testWrapper, joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup ) { @Override @@ -213,20 +213,15 @@ public void test_constructor_noClauses() List joinableClauses = ImmutableList.of(); - JoinFilterPreAnalysis joinFilterPreAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, - null, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = new JoinFilterPreAnalysisGroup( + DEFAULT_JOIN_FILTER_REWRITE_CONFIG, + true ); final HashJoinSegment ignored = new HashJoinSegment( ReferenceCountingSegment.wrapRootGenerationSegment(baseSegment), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup ); } diff --git a/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java b/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java index 965dd2302f11..893e0542b8ce 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/JoinFilterAnalyzerTest.java @@ -46,6 +46,8 @@ import org.apache.druid.segment.join.filter.JoinFilterPreAnalysis; import org.apache.druid.segment.join.filter.JoinFilterSplit; import org.apache.druid.segment.join.filter.JoinableClauses; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterPreAnalysisGroup; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import org.apache.druid.segment.join.lookup.LookupJoinable; import org.apache.druid.segment.join.table.IndexedTableJoinable; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; @@ -66,24 +68,17 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnChannel() regionToCountry(JoinType.LEFT) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new SelectorFilter("channel", "#en.wikipedia"), - null, - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -116,6 +111,15 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnChannel() new Object[]{"History of Fourems", "Fourems Province", "Fourems"} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new SelectorFilter("channel", "#en.wikipedia"), + null, + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -142,24 +146,17 @@ public void test_filterPushDown_factToRegionExprToCountryLeftFilterOnCountryName regionExprToCountry ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - null, - new SelectorFilter("rtc.countryName", "United States"), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -179,6 +176,15 @@ public void test_filterPushDown_factToRegionExprToCountryLeftFilterOnCountryName new Object[]{"Cream Soda", "Ainigriv", "United States"} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + null, + new SelectorFilter("rtc.countryName", "United States"), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -196,29 +202,18 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnChannelAndCount regionToCountry(JoinType.LEFT) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", "#en.wikipedia"), - new InDimFilter("countryIsoCode", ImmutableSet.of("US"), null, null).toFilter() - ) - ), - new SelectorFilter("rtc.countryName", "United States"), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -242,6 +237,21 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnChannelAndCount new Object[]{"Old Anatolian Turkish", "Virginia", "United States"} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", "#en.wikipedia"), + new InDimFilter("countryIsoCode", ImmutableSet.of("US"), null, null).toFilter() + ) + ), + new SelectorFilter("rtc.countryName", "United States"), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -261,33 +271,18 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnNullColumns() regionToCountry(JoinType.LEFT) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup ); - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - null, - new AndFilter( - ImmutableList.of( - new SelectorFilter("countryIsoCode", null), - new SelectorFilter("countryNumber", null), - new SelectorFilter("rtc.countryName", null), - new SelectorFilter("r1.regionName", null) - ) - ), - ImmutableSet.of() - ); - - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); - JoinTestHelper.verifyCursors( adapter.makeCursors( originalFilter, @@ -312,8 +307,26 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnNullColumns() ) : ImmutableList.of() // when not running in SQL compatible mode, countryNumber does not have nulls ); - } + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + null, + new AndFilter( + ImmutableList.of( + new SelectorFilter("countryIsoCode", null), + new SelectorFilter("countryNumber", null), + new SelectorFilter("rtc.countryName", null), + new SelectorFilter("r1.regionName", null) + ) + ), + ImmutableSet.of() + ); + + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); + } + @Test public void test_filterPushDown_factToRegionToCountryLeftFilterOnInvalidColumns() { @@ -331,30 +344,17 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnInvalidColumns( ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new SelectorFilter("baseTableInvalidColumn", "abcd"), - new AndFilter( - ImmutableList.of( - new SelectorFilter("baseTableInvalidColumn2", null), - new SelectorFilter("rtc.invalidColumn", "abcd"), - new SelectorFilter("r1.invalidColumn", "abcd") - ) - ), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -372,6 +372,22 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnInvalidColumns( ), ImmutableList.of() ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new SelectorFilter("baseTableInvalidColumn", "abcd"), + new AndFilter( + ImmutableList.of( + new SelectorFilter("baseTableInvalidColumn2", null), + new SelectorFilter("rtc.invalidColumn", "abcd"), + new SelectorFilter("r1.invalidColumn", "abcd") + ) + ), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -388,15 +404,15 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnChannelVirtualC ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); - HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup ); VirtualColumns virtualColumns = VirtualColumns.create( @@ -410,14 +426,6 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnChannelVirtualC ) ); - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new SelectorFilter("v1", "virtual-column-#en.wikipedia"), - null, - ImmutableSet.of() - ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); - JoinTestHelper.verifyCursors( adapter.makeCursors( originalFilter, @@ -449,6 +457,16 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnChannelVirtualC new Object[]{"History of Fourems", "Fourems Province", "Fourems"} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new SelectorFilter("v1", "virtual-column-#en.wikipedia"), + null, + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -472,29 +490,16 @@ public void test_filterPushDown_factToRegionFilterOnRHSRegionNameExprVirtualColu factToRegion(JoinType.LEFT) )); - JoinFilterPreAnalysis joinFilterPreAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, - virtualColumns, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( originalFilter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses.getJoinableClauses(), + virtualColumns ); - HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses.getJoinableClauses(), - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - null, - new SelectorFilter("v0", "VIRGINIA"), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -513,8 +518,17 @@ public void test_filterPushDown_factToRegionFilterOnRHSRegionNameExprVirtualColu new Object[]{"Old Anatolian Turkish", "VIRGINIA"} ) ); - } + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + null, + new SelectorFilter("v0", "VIRGINIA"), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); + } @Test public void test_filterPushDown_factToRegionToCountryLeftFilterNormalizedAlreadyPushDownVariety() @@ -581,14 +595,34 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterNormalizedAlready regionToCountry(JoinType.LEFT) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup + ); + + JoinTestHelper.verifyCursors( + adapter.makeCursors( + originalFilter, + Intervals.ETERNITY, + VirtualColumns.EMPTY, + Granularities.ALL, + false, + null + ), + ImmutableList.of( + "page", + FACT_TO_REGION_PREFIX + "regionName", + REGION_TO_COUNTRY_PREFIX + "countryName" + ), + ImmutableList.of( + new Object[]{"Les Argonautes", "Quebec", "Canada"} + ) ); JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( @@ -655,27 +689,10 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterNormalizedAlready ), ImmutableSet.of() ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); Assert.assertEquals(expectedFilterSplit, actualFilterSplit); - - JoinTestHelper.verifyCursors( - adapter.makeCursors( - originalFilter, - Intervals.ETERNITY, - VirtualColumns.EMPTY, - Granularities.ALL, - false, - null - ), - ImmutableList.of( - "page", - FACT_TO_REGION_PREFIX + "regionName", - REGION_TO_COUNTRY_PREFIX + "countryName" - ), - ImmutableList.of( - new Object[]{"Les Argonautes", "Quebec", "Canada"} - ) - ); } @Test @@ -705,15 +722,35 @@ public void test_filterPushDown_factExpressionsToRegionToCountryLeftFilterOnChan new SelectorFilter("rtc.countryName", "States United") ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); - HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup + ); + + JoinTestHelper.verifyCursors( + adapter.makeCursors( + originalFilter, + Intervals.ETERNITY, + VirtualColumns.EMPTY, + Granularities.ALL, + false, + null + ), + ImmutableList.of( + "page", + FACT_TO_REGION_PREFIX + "regionName", + REGION_TO_COUNTRY_PREFIX + "countryName" + ), + ImmutableList.of( + new Object[]{"Old Anatolian Turkish", "Ainigriv", "States United"} + ) ); JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( @@ -726,6 +763,8 @@ public void test_filterPushDown_factExpressionsToRegionToCountryLeftFilterOnChan new SelectorFilter("rtc.countryName", "States United"), ImmutableSet.of() ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); ExpressionVirtualColumn expectedVirtualColumn = new ExpressionVirtualColumn( @@ -745,25 +784,6 @@ public void test_filterPushDown_factExpressionsToRegionToCountryLeftFilterOnChan ExpressionVirtualColumn actualVirtualColumn = (ExpressionVirtualColumn) actualFilterSplit.getPushDownVirtualColumns() .iterator().next(); compareExpressionVirtualColumns(expectedVirtualColumn, actualVirtualColumn); - - JoinTestHelper.verifyCursors( - adapter.makeCursors( - originalFilter, - Intervals.ETERNITY, - VirtualColumns.EMPTY, - Granularities.ALL, - false, - null - ), - ImmutableList.of( - "page", - FACT_TO_REGION_PREFIX + "regionName", - REGION_TO_COUNTRY_PREFIX + "countryName" - ), - ImmutableList.of( - new Object[]{"Old Anatolian Turkish", "Ainigriv", "States United"} - ) - ); } @Test @@ -795,9 +815,10 @@ public void test_filterPushDown_factToRegionToCountryNotEquiJoinLeftFilterOnChan ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Cannot build hash-join matcher on non-equi-join condition: \"r1.regionIsoCode\" == regionIsoCode && reverse(\"r1.countryIsoCode\") == countryIsoCode"); @@ -805,7 +826,7 @@ public void test_filterPushDown_factToRegionToCountryNotEquiJoinLeftFilterOnChan HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup ); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -846,20 +867,41 @@ public void test_filterPushDown_factToRegionToCountryLeftUnnormalizedFilter() ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup ); - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new AndFilter( - ImmutableList.of( - new OrFilter( + JoinTestHelper.verifyCursors( + adapter.makeCursors( + originalFilter, + Intervals.ETERNITY, + VirtualColumns.EMPTY, + Granularities.ALL, + false, + null + ), + ImmutableList.of( + "page", + FACT_TO_REGION_PREFIX + "regionName", + REGION_TO_COUNTRY_PREFIX + "countryName" + ), + ImmutableList.of( + new Object[]{"유희왕 GX", "Seoul", "Republic of Korea"}, + new Object[]{"Old Anatolian Turkish", "Virginia", "United States"} + ) + ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new AndFilter( + ImmutableList.of( + new OrFilter( ImmutableList.of( new SelectorFilter("channel", "#ko.wikipedia"), new InDimFilter("countryIsoCode", ImmutableSet.of("US"), null, null).toFilter() @@ -896,28 +938,11 @@ public void test_filterPushDown_factToRegionToCountryLeftUnnormalizedFilter() ), ImmutableSet.of() ); + + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); Assert.assertEquals(expectedFilterSplit, actualFilterSplit); - - JoinTestHelper.verifyCursors( - adapter.makeCursors( - originalFilter, - Intervals.ETERNITY, - VirtualColumns.EMPTY, - Granularities.ALL, - false, - null - ), - ImmutableList.of( - "page", - FACT_TO_REGION_PREFIX + "regionName", - REGION_TO_COUNTRY_PREFIX + "countryName" - ), - ImmutableList.of( - new Object[]{"유희왕 GX", "Seoul", "Republic of Korea"}, - new Object[]{"Old Anatolian Turkish", "Virginia", "United States"} - ) - ); } @Test @@ -946,15 +971,35 @@ public void test_filterPushDown_factConcatExpressionToCountryLeftFilterOnChannel ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + filter, joinableClauses, - filter + VirtualColumns.EMPTY ); - HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup + ); + + JoinTestHelper.verifyCursors( + adapter.makeCursors( + filter, + Intervals.ETERNITY, + VirtualColumns.EMPTY, + Granularities.ALL, + false, + null + ), + ImmutableList.of( + "page", + FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName" + ), + ImmutableList.of( + new Object[]{"President of India", "Usca"}, + new Object[]{"Otjiwarongo Airport", "Usca"}, + new Object[]{"Carlo Curti", "Usca"} + ) ); ExpressionVirtualColumn expectedVirtualColumn = new ExpressionVirtualColumn( @@ -975,6 +1020,8 @@ public void test_filterPushDown_factConcatExpressionToCountryLeftFilterOnChannel expectedVirtualColumn ) ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); Assert.assertEquals( expectedFilterSplit.getBaseTableFilter(), @@ -987,26 +1034,6 @@ public void test_filterPushDown_factConcatExpressionToCountryLeftFilterOnChannel ExpressionVirtualColumn actualVirtualColumn = (ExpressionVirtualColumn) actualFilterSplit.getPushDownVirtualColumns() .iterator().next(); compareExpressionVirtualColumns(expectedVirtualColumn, actualVirtualColumn); - - JoinTestHelper.verifyCursors( - adapter.makeCursors( - filter, - Intervals.ETERNITY, - VirtualColumns.EMPTY, - Granularities.ALL, - false, - null - ), - ImmutableList.of( - "page", - FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName" - ), - ImmutableList.of( - new Object[]{"President of India", "Usca"}, - new Object[]{"Otjiwarongo Airport", "Usca"}, - new Object[]{"Carlo Curti", "Usca"} - ) - ); } @Test @@ -1035,15 +1062,35 @@ public void test_filterPushDown_factConcatExpressionToCountryLeftFilterOnChannel ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + filter, joinableClauses, - filter + VirtualColumns.EMPTY ); - HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup + ); + + JoinTestHelper.verifyCursors( + adapter.makeCursors( + filter, + Intervals.ETERNITY, + VirtualColumns.EMPTY, + Granularities.ALL, + false, + null + ), + ImmutableList.of( + "page", + FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v" + ), + ImmutableList.of( + new Object[]{"President of India", "Usca"}, + new Object[]{"Otjiwarongo Airport", "Usca"}, + new Object[]{"Carlo Curti", "Usca"} + ) ); ExpressionVirtualColumn expectedVirtualColumn = new ExpressionVirtualColumn( @@ -1064,6 +1111,8 @@ public void test_filterPushDown_factConcatExpressionToCountryLeftFilterOnChannel expectedVirtualColumn ) ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); Assert.assertEquals( expectedFilterSplit.getBaseTableFilter(), @@ -1076,26 +1125,6 @@ public void test_filterPushDown_factConcatExpressionToCountryLeftFilterOnChannel ExpressionVirtualColumn actualVirtualColumn = (ExpressionVirtualColumn) actualFilterSplit.getPushDownVirtualColumns() .iterator().next(); compareExpressionVirtualColumns(expectedVirtualColumn, actualVirtualColumn); - - JoinTestHelper.verifyCursors( - adapter.makeCursors( - filter, - Intervals.ETERNITY, - VirtualColumns.EMPTY, - Granularities.ALL, - false, - null - ), - ImmutableList.of( - "page", - FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v" - ), - ImmutableList.of( - new Object[]{"President of India", "Usca"}, - new Object[]{"Otjiwarongo Airport", "Usca"}, - new Object[]{"Carlo Curti", "Usca"} - ) - ); } @Test @@ -1108,29 +1137,17 @@ public void test_filterPushDown_factToCountryRightWithFilterOnChannelAndJoinable new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", "Germany") ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup ); - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", "#de.wikipedia"), - new InDimFilter("countryIsoCode", ImmutableSet.of("DE"), null, null).toFilter() - ) - ), - new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", "Germany"), - ImmutableSet.of() - ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); - JoinTestHelper.verifyCursors( adapter.makeCursors( originalFilter, @@ -1152,6 +1169,21 @@ public void test_filterPushDown_factToCountryRightWithFilterOnChannelAndJoinable new Object[]{"Diskussion:Sebastian Schulz", "DE", 3L, "DE", "Germany", 3L} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", "#de.wikipedia"), + new InDimFilter("countryIsoCode", ImmutableSet.of("DE"), null, null).toFilter() + ) + ), + new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", "Germany"), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1164,28 +1196,16 @@ public void test_filterPushDown_factToCountryRightWithFilterOnChannelAndJoinable new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", "Germany") ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", "#de.wikipedia"), - new InDimFilter("countryIsoCode", ImmutableSet.of("DE"), null, null).toFilter() - ) - ), - new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", "Germany"), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -1207,6 +1227,21 @@ public void test_filterPushDown_factToCountryRightWithFilterOnChannelAndJoinable new Object[]{"Diskussion:Sebastian Schulz", "DE", 3L, "DE", "Germany"} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", "#de.wikipedia"), + new InDimFilter("countryIsoCode", ImmutableSet.of("DE"), null, null).toFilter() + ) + ), + new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", "Germany"), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1219,29 +1254,17 @@ public void test_filterPushDown_factToCountryRightWithFilterOnNullColumns() new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", null) ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup ); - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - null, - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", null), - new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", null) - ) - ), - ImmutableSet.of() - ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); - JoinTestHelper.verifyCursors( adapter.makeCursors( originalFilter, @@ -1261,6 +1284,21 @@ public void test_filterPushDown_factToCountryRightWithFilterOnNullColumns() ), ImmutableList.of() ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + null, + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", null), + new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", null) + ) + ), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1273,29 +1311,17 @@ public void test_filterPushDown_factToCountryRightWithFilterOnValueThatMatchesNo new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", "NO MATCH") ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup ); - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - FalseFilter.instance(), - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", null), - new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", "NO MATCH") - ) - ), - ImmutableSet.of() - ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); - JoinTestHelper.verifyCursors( adapter.makeCursors( originalFilter, @@ -1315,6 +1341,22 @@ public void test_filterPushDown_factToCountryRightWithFilterOnValueThatMatchesNo ), ImmutableList.of() ); + + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + FalseFilter.instance(), + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", null), + new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", "NO MATCH") + ) + ), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1327,28 +1369,16 @@ public void test_filterPushDown_factToCountryRightWithFilterOnNullColumnsUsingLo new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", null) ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - null, - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", null), - new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", null) - ) - ), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -1366,8 +1396,23 @@ public void test_filterPushDown_factToCountryRightWithFilterOnNullColumnsUsingLo FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "k", FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v" ), - ImmutableList.of() + ImmutableList.of() + ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + null, + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", null), + new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", null) + ) + ), + ImmutableSet.of() ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1380,28 +1425,16 @@ public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnChan new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "countryName", "Australia") ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", "#en.wikipedia"), - new InDimFilter("countryNumber", ImmutableSet.of("0"), null, null).toFilter() - ) - ), - new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "countryName", "Australia"), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); // In non-SQL-compatible mode, we get an extra row, since the 'null' countryNumber for "Talk:Oswald Tilghman" // is interpreted as 0 (a.k.a. Australia). @@ -1430,6 +1463,21 @@ public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnChan new Object[]{"Peremptory norm", "AU", "AU", "Australia", 0L} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", "#en.wikipedia"), + new InDimFilter("countryNumber", ImmutableSet.of("0"), null, null).toFilter() + ) + ), + new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "countryName", "Australia"), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1442,28 +1490,16 @@ public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnChan new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v", "Australia") ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", "#en.wikipedia"), - new InDimFilter("countryNumber", ImmutableSet.of("0"), null, null).toFilter() - ) - ), - new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v", "Australia"), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); // In non-SQL-compatible mode, we get an extra row, since the 'null' countryNumber for "Talk:Oswald Tilghman" // is interpreted as 0 (a.k.a. Australia). @@ -1491,6 +1527,21 @@ public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnChan new Object[]{"Peremptory norm", "AU", "0", "Australia"} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", "#en.wikipedia"), + new InDimFilter("countryNumber", ImmutableSet.of("0"), null, null).toFilter() + ) + ), + new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v", "Australia"), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1503,28 +1554,16 @@ public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnNull new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "countryName", null) ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - null, - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", null), - new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "countryName", null) - ) - ), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -1544,6 +1583,21 @@ public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnNull ), ImmutableList.of() ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + null, + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", null), + new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "countryName", null) + ) + ), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1556,28 +1610,16 @@ public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnNull new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v", null) ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - null, - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", null), - new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v", null) - ) - ), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -1596,6 +1638,21 @@ public void test_filterPushDown_factToCountryInnerUsingCountryNumberFilterOnNull ), ImmutableList.of() ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + null, + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", null), + new SelectorFilter(FACT_TO_COUNTRY_ON_NUMBER_PREFIX + "v", null) + ) + ), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1608,28 +1665,16 @@ public void test_filterPushDown_factToCountryFullWithFilterOnChannelAndCountryNa ) ); List joinableClauses = ImmutableList.of(factToCountryOnIsoCode(JoinType.FULL)); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + filter, joinableClauses, - filter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", "#es.wikipedia"), - new InDimFilter("countryIsoCode", ImmutableSet.of("SV"), null, null).toFilter() - ) - ), - new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", "El Salvador"), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -1652,6 +1697,21 @@ public void test_filterPushDown_factToCountryFullWithFilterOnChannelAndCountryNa new Object[]{"Wendigo", "SV", 12L, "SV", "El Salvador", 12L} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", "#es.wikipedia"), + new InDimFilter("countryIsoCode", ImmutableSet.of("SV"), null, null).toFilter() + ) + ), + new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", "El Salvador"), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1664,28 +1724,16 @@ public void test_filterPushDown_factToCountryFullWithFilterOnChannelAndCountryNa ) ); List joinableClauses = ImmutableList.of(factToCountryNameUsingIsoCodeLookup(JoinType.FULL)); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + filter, joinableClauses, - filter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", "#es.wikipedia"), - new InDimFilter("countryIsoCode", ImmutableSet.of("SV"), null, null).toFilter() - ) - ), - new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", "El Salvador"), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -1707,6 +1755,21 @@ public void test_filterPushDown_factToCountryFullWithFilterOnChannelAndCountryNa new Object[]{"Wendigo", "SV", 12L, "SV", "El Salvador"} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", "#es.wikipedia"), + new InDimFilter("countryIsoCode", ImmutableSet.of("SV"), null, null).toFilter() + ) + ), + new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", "El Salvador"), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1719,28 +1782,16 @@ public void test_filterPushDown_factToCountryFullWithFilterOnNulls() new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", null) ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - null, - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", null), - new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", null) - ) - ), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -1761,6 +1812,21 @@ public void test_filterPushDown_factToCountryFullWithFilterOnNulls() ), ImmutableList.of() ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + null, + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", null), + new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "countryName", null) + ) + ), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1773,28 +1839,16 @@ public void test_filterPushDown_factToCountryFullWithFilterOnNullsUsingLookup() new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", null) ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - null, - new AndFilter( - ImmutableList.of( - new SelectorFilter("channel", null), - new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", null) - ) - ), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -1814,6 +1868,21 @@ public void test_filterPushDown_factToCountryFullWithFilterOnNullsUsingLookup() ), ImmutableList.of() ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + null, + new AndFilter( + ImmutableList.of( + new SelectorFilter("channel", null), + new SelectorFilter(FACT_TO_COUNTRY_ON_ISO_CODE_PREFIX + "v", null) + ) + ), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1838,29 +1907,16 @@ public void test_filterPushDown_factToRegionTwoColumnsToOneRHSColumnAndFilterOnR ); Filter originalFilter = new SelectorFilter("r1.regionName", "Fourems Province"); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); - HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new AndFilter( - ImmutableList.of( - new InDimFilter("countryIsoCode", ImmutableSet.of("MMMM"), null, null).toFilter(), - new InDimFilter("regionIsoCode", ImmutableSet.of("MMMM"), null, null).toFilter() - ) - ), - new SelectorFilter("r1.regionName", "Fourems Province"), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -1879,6 +1935,22 @@ public void test_filterPushDown_factToRegionTwoColumnsToOneRHSColumnAndFilterOnR new Object[]{"History of Fourems", "Fourems Province"} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new AndFilter( + ImmutableList.of( + new InDimFilter("countryIsoCode", ImmutableSet.of("MMMM"), null, null).toFilter(), + new InDimFilter("regionIsoCode", ImmutableSet.of("MMMM"), null, null).toFilter() + ) + ), + new SelectorFilter("r1.regionName", "Fourems Province"), + ImmutableSet.of() + ); + + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -1908,28 +1980,16 @@ public void test_filterPushDown_factToRegionOneColumnToTwoRHSColumnsAndFilterOnR ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new OrFilter( - ImmutableList.of( - new InDimFilter("regionIsoCode", ImmutableSet.of("MMMM"), null, null).toFilter(), - new SelectorFilter("regionIsoCode", "AAAA") - ) - ), - originalFilter, - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -1948,6 +2008,22 @@ public void test_filterPushDown_factToRegionOneColumnToTwoRHSColumnsAndFilterOnR new Object[]{"History of Fourems", "Fourems Province"} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new OrFilter( + ImmutableList.of( + new InDimFilter("regionIsoCode", ImmutableSet.of("MMMM"), null, null).toFilter(), + new SelectorFilter("regionIsoCode", "AAAA") + ) + ), + originalFilter, + ImmutableSet.of() + ); + + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @@ -1979,28 +2055,16 @@ public void test_filterPushDown_factToRegionThreeRHSColumnsAllDirectAndFilterOnR ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - new OrFilter( - ImmutableList.of( - new SelectorFilter("user", "Fourems Province"), - new SelectorFilter("regionIsoCode", "AAAA") - ) - ), - null, - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); // This query doesn't execute because regionName is not a key column, but we can still check the // filter rewrites. @@ -2024,8 +2088,22 @@ public void test_filterPushDown_factToRegionThreeRHSColumnsAllDirectAndFilterOnR ), ImmutableList.of() ); - } + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + new OrFilter( + ImmutableList.of( + new SelectorFilter("user", "Fourems Province"), + new SelectorFilter("regionIsoCode", "AAAA") + ) + ), + null, + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); + } @Test public void test_filterPushDown_factToRegionToCountryLeftFilterOnPageDisablePushDown() @@ -2036,28 +2114,26 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnPageDisablePush )); Filter originalFilter = new SelectorFilter("page", "Peremptory norm"); - JoinFilterPreAnalysis joinFilterPreAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = new JoinFilterPreAnalysisGroup( + new JoinFilterRewriteConfig( + false, + true, + true, + QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + ), + true + ); + joinFilterPreAnalysisGroup.computeJoinFilterPreAnalysisIfAbsent( originalFilter, - false, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses.getJoinableClauses(), + VirtualColumns.EMPTY ); + HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses.getJoinableClauses(), - joinFilterPreAnalysis - ); - - JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( - null, - new SelectorFilter("page", "Peremptory norm"), - ImmutableSet.of() + joinFilterPreAnalysisGroup ); - JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); - Assert.assertEquals(expectedFilterSplit, actualFilterSplit); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -2077,6 +2153,16 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnPageDisablePush new Object[]{"Peremptory norm", "New South Wales", "Australia"} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + null, + new SelectorFilter("page", "Peremptory norm"), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -2108,19 +2194,48 @@ public void test_filterPushDown_factToRegionToCountryLeftEnablePushDownDisableRe )) ) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - joinableClauses, - VirtualColumns.EMPTY, + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = new JoinFilterPreAnalysisGroup( + new JoinFilterRewriteConfig( + true, + false, + true, + QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + ), + true + ); + joinFilterPreAnalysisGroup.computeJoinFilterPreAnalysisIfAbsent( originalFilter, - true, - false, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + joinableClauses.getJoinableClauses(), + VirtualColumns.EMPTY ); + HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses.getJoinableClauses(), - joinFilterPreAnalysis + joinFilterPreAnalysisGroup + ); + + JoinTestHelper.verifyCursors( + adapter.makeCursors( + originalFilter, + Intervals.ETERNITY, + VirtualColumns.EMPTY, + Granularities.ALL, + false, + null + ), + ImmutableList.of( + "page", + FACT_TO_REGION_PREFIX + "regionName", + REGION_TO_COUNTRY_PREFIX + "countryName" + ), + ImmutableList.of( + new Object[]{"President of India", "California", "United States"}, + new Object[]{"Otjiwarongo Airport", "California", "United States"}, + new Object[]{"DirecTV", "North Carolina", "United States"}, + new Object[]{"Carlo Curti", "California", "United States"}, + new Object[]{"Old Anatolian Turkish", "Virginia", "United States"} + ) ); JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( @@ -2152,31 +2267,10 @@ public void test_filterPushDown_factToRegionToCountryLeftEnablePushDownDisableRe ), ImmutableSet.of() ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); Assert.assertEquals(expectedFilterSplit, actualFilterSplit); - - JoinTestHelper.verifyCursors( - adapter.makeCursors( - originalFilter, - Intervals.ETERNITY, - VirtualColumns.EMPTY, - Granularities.ALL, - false, - null - ), - ImmutableList.of( - "page", - FACT_TO_REGION_PREFIX + "regionName", - REGION_TO_COUNTRY_PREFIX + "countryName" - ), - ImmutableList.of( - new Object[]{"President of India", "California", "United States"}, - new Object[]{"Otjiwarongo Airport", "California", "United States"}, - new Object[]{"DirecTV", "North Carolina", "United States"}, - new Object[]{"Carlo Curti", "California", "United States"}, - new Object[]{"Old Anatolian Turkish", "Virginia", "United States"} - ) - ); } @Test @@ -2284,15 +2378,15 @@ public boolean supportsRequiredColumnRewrite() regionToCountry(JoinType.LEFT) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); - HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup ); String rewrittenCountryIsoCodeColumnName = hasLhsExpressionInJoinCondition @@ -2324,6 +2418,25 @@ public boolean supportsRequiredColumnRewrite() expectedVirtualColumns = ImmutableSet.of(); } + JoinTestHelper.verifyCursors( + adapter.makeCursors( + originalFilter, + Intervals.ETERNITY, + VirtualColumns.EMPTY, + Granularities.ALL, + false, + null + ), + ImmutableList.of( + "page", + FACT_TO_REGION_PREFIX + "regionName", + REGION_TO_COUNTRY_PREFIX + "countryName" + ), + ImmutableList.of( + new Object[]{"Didier Leclair", "Ontario", "Canada"} + ) + ); + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( new AndFilter( ImmutableList.of( @@ -2401,27 +2514,10 @@ public boolean supportsRequiredColumnRewrite() ), expectedVirtualColumns ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); Assert.assertEquals(expectedFilterSplit, actualFilterSplit); - - JoinTestHelper.verifyCursors( - adapter.makeCursors( - originalFilter, - Intervals.ETERNITY, - VirtualColumns.EMPTY, - Granularities.ALL, - false, - null - ), - ImmutableList.of( - "page", - FACT_TO_REGION_PREFIX + "regionName", - REGION_TO_COUNTRY_PREFIX + "countryName" - ), - ImmutableList.of( - new Object[]{"Didier Leclair", "Ontario", "Canada"} - ) - ); } @Test @@ -2439,14 +2535,34 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnTwoRHSColumnsSa regionToCountry(JoinType.LEFT) ); - JoinFilterPreAnalysis joinFilterPreAnalysis = simplePreAnalysis( + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = makeDefaultConfigPreAnalysisGroup( + originalFilter, joinableClauses, - originalFilter + VirtualColumns.EMPTY ); + HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( factSegment.asStorageAdapter(), joinableClauses, - joinFilterPreAnalysis + joinFilterPreAnalysisGroup + ); + + JoinTestHelper.verifyCursors( + adapter.makeCursors( + originalFilter, + Intervals.ETERNITY, + VirtualColumns.EMPTY, + Granularities.ALL, + false, + null + ), + ImmutableList.of( + "page", + FACT_TO_REGION_PREFIX + "regionName", + REGION_TO_COUNTRY_PREFIX + "countryName" + ), + ImmutableList.of( + ) ); JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( @@ -2469,8 +2585,57 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnTwoRHSColumnsSa originalFilter, ImmutableSet.of() ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getPreAnalysisForSingleLevelMode(); + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); Assert.assertEquals(expectedFilterSplit, actualFilterSplit); + } + + + @Test + public void test_filterPushDown_factToRegionExprToCountryLeftFilterOnCountryNameWithMultiLevelMode() + { + Filter originalFilter = new SelectorFilter("rtc.countryName", "United States"); + JoinableClause regionExprToCountry = new JoinableClause( + REGION_TO_COUNTRY_PREFIX, + new IndexedTableJoinable(countriesTable), + JoinType.LEFT, + JoinConditionAnalysis.forExpression( + StringUtils.format( + "reverse(\"%scountryIsoCode\") == \"%scountryIsoCode\"", + FACT_TO_REGION_PREFIX, + REGION_TO_COUNTRY_PREFIX + ), + REGION_TO_COUNTRY_PREFIX, + ExprMacroTable.nil() + ) + ); + List joinableClauses = ImmutableList.of( + factToRegion(JoinType.LEFT), + regionExprToCountry + ); + + JoinFilterPreAnalysisGroup joinFilterPreAnalysisGroup = new JoinFilterPreAnalysisGroup( + new JoinFilterRewriteConfig( + true, + true, + true, + QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + ), + false + ); + + joinFilterPreAnalysisGroup.computeJoinFilterPreAnalysisIfAbsent( + originalFilter, + joinableClauses, + VirtualColumns.EMPTY + ); + + HashJoinSegmentStorageAdapter adapter = new HashJoinSegmentStorageAdapter( + factSegment.asStorageAdapter(), + joinableClauses, + joinFilterPreAnalysisGroup + ); JoinTestHelper.verifyCursors( adapter.makeCursors( @@ -2487,8 +2652,22 @@ public void test_filterPushDown_factToRegionToCountryLeftFilterOnTwoRHSColumnsSa REGION_TO_COUNTRY_PREFIX + "countryName" ), ImmutableList.of( + new Object[]{"Cream Soda", "Ainigriv", "United States"} ) ); + + JoinFilterSplit expectedFilterSplit = new JoinFilterSplit( + null, + new SelectorFilter("rtc.countryName", "United States"), + ImmutableSet.of() + ); + JoinFilterPreAnalysis joinFilterPreAnalysis = joinFilterPreAnalysisGroup.getAnalysis( + originalFilter, + VirtualColumns.EMPTY + ); + + JoinFilterSplit actualFilterSplit = JoinFilterAnalyzer.splitFilter(joinFilterPreAnalysis); + Assert.assertEquals(expectedFilterSplit, actualFilterSplit); } @Test @@ -2500,19 +2679,13 @@ public void test_JoinFilterSplit_equals() .verify(); } - private static JoinFilterPreAnalysis simplePreAnalysis( - List joinableClauses, - Filter originalFilter - ) + + @Test + public void test_JoinFilterPreAnalysisGroupKey_equals() { - return JoinFilterAnalyzer.computeJoinFilterPreAnalysis( - JoinableClauses.fromList(joinableClauses), - VirtualColumns.EMPTY, - originalFilter, - true, - true, - true, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE - ); + EqualsVerifier.forClass(JoinFilterPreAnalysisGroup.JoinFilterPreAnalysisGroupKey.class) + .usingGetClass() + .withNonnullFields("filter", "virtualColumns") + .verify(); } } diff --git a/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java b/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java index f7c4f4b415b8..c418f7fd45ef 100644 --- a/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java +++ b/processing/src/test/java/org/apache/druid/segment/join/JoinablesTest.java @@ -22,22 +22,34 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.apache.druid.java.util.common.IAE; +import org.apache.druid.java.util.common.Intervals; +import org.apache.druid.java.util.common.granularity.Granularities; import org.apache.druid.math.expr.ExprMacroTable; import org.apache.druid.query.DataSource; +import org.apache.druid.query.JoinDataSource; import org.apache.druid.query.LookupDataSource; +import org.apache.druid.query.Query; import org.apache.druid.query.QueryContexts; +import org.apache.druid.query.QueryDataSource; +import org.apache.druid.query.TableDataSource; +import org.apache.druid.query.TestQuery; +import org.apache.druid.query.expression.TestExprMacroTable; import org.apache.druid.query.extraction.MapLookupExtractor; +import org.apache.druid.query.groupby.GroupByQuery; import org.apache.druid.query.planning.PreJoinableClause; +import org.apache.druid.query.spec.MultipleIntervalSegmentSpec; import org.apache.druid.segment.SegmentReference; -import org.apache.druid.segment.VirtualColumns; import org.apache.druid.segment.column.ColumnHolder; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import org.apache.druid.segment.join.lookup.LookupJoinable; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; @@ -45,6 +57,13 @@ public class JoinablesTest { + private static final JoinFilterRewriteConfig DEFAULT_JOIN_FILTER_REWRITE_CONFIG = new JoinFilterRewriteConfig( + QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_PUSH_DOWN, + QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE, + QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_VALUE_COLUMN_FILTERS, + QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE + ); + @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -100,12 +119,8 @@ public void test_createSegmentMapFn_noClauses() ImmutableList.of(), NoopJoinableFactory.INSTANCE, new AtomicLong(), - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_PUSH_DOWN, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_VALUE_COLUMN_FILTERS, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE, - null, - VirtualColumns.EMPTY + DEFAULT_JOIN_FILTER_REWRITE_CONFIG, + null ); Assert.assertSame(Function.identity(), segmentMapFn); @@ -129,12 +144,8 @@ public void test_createSegmentMapFn_unusableClause() ImmutableList.of(clause), NoopJoinableFactory.INSTANCE, new AtomicLong(), - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_PUSH_DOWN, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_VALUE_COLUMN_FILTERS, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE, - null, - VirtualColumns.EMPTY + DEFAULT_JOIN_FILTER_REWRITE_CONFIG, + null ); } @@ -177,12 +188,13 @@ public Optional build(DataSource dataSource, JoinConditionAnalysis con } }, new AtomicLong(), - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_PUSH_DOWN, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_VALUE_COLUMN_FILTERS, - QueryContexts.DEFAULT_ENABLE_JOIN_FILTER_REWRITE_MAX_SIZE, - null, - VirtualColumns.EMPTY + DEFAULT_JOIN_FILTER_REWRITE_CONFIG, + new TestQuery( + new TableDataSource("test"), + new MultipleIntervalSegmentSpec(ImmutableList.of(Intervals.of("0/100"))), + false, + new HashMap() + ) ); Assert.assertNotSame(Function.identity(), segmentMapFn); @@ -236,4 +248,83 @@ public void test_checkClausePrefixesForDuplicatesAndShadowing_shadowing() Joinables.checkPrefixesForDuplicatesAndShadowing(prefixes); } + + @Test + public void test_gatherAllJoinQueryLevels() + { + Query query1 = new GroupByQuery.Builder() + .addDimension("dim1") + .setDataSource( + JoinDataSource.create( + new NoopDataSource(), + new NoopDataSource(), + "111", + "1", + JoinType.LEFT, + TestExprMacroTable.INSTANCE + ) + ) + .setInterval("1999/2000") + .setGranularity(Granularities.YEAR) + .build(); + + Query query2 = new GroupByQuery.Builder() + .addDimension("dim2") + .setDataSource( + JoinDataSource.create( + new NoopDataSource(), + new NoopDataSource(), + "222", + "1", + JoinType.LEFT, + TestExprMacroTable.INSTANCE + ) + ) + .setInterval("1999/2000") + .setGranularity(Granularities.YEAR) + .build(); + + QueryDataSource queryDataSource1 = new QueryDataSource(query1); + QueryDataSource queryDataSource2 = new QueryDataSource(query2); + + Query query3 = new GroupByQuery.Builder() + .addDimension("dim2") + .setDataSource( + JoinDataSource.create( + JoinDataSource.create( + queryDataSource1, + new NoopDataSource(), + "444", + "4", + JoinType.LEFT, + TestExprMacroTable.INSTANCE + ), + queryDataSource2, + "333", + "3", + JoinType.LEFT, + TestExprMacroTable.INSTANCE + ) + ) + .setInterval("1999/2000") + .setGranularity(Granularities.YEAR) + .build(); + + Query queryOuter = new GroupByQuery.Builder() + .addDimension("dim") + .setDataSource( + new QueryDataSource(query3) + ) + .setInterval("1999/2000") + .setGranularity(Granularities.YEAR) + .build(); + + List joinQueryLevels = new ArrayList<>(); + Joinables.gatherAllJoinQueryLevels(queryOuter, joinQueryLevels); + + Assert.assertEquals( + ImmutableList.of(query3, query1, query2), + joinQueryLevels + ); + } } diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java index 4527287b4f66..b46bd7431800 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java @@ -59,6 +59,7 @@ import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.join.JoinableFactory; import org.apache.druid.segment.join.Joinables; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import org.apache.druid.segment.realtime.FireHydrant; import org.apache.druid.segment.realtime.plumber.Sink; import org.apache.druid.timeline.SegmentId; @@ -169,17 +170,20 @@ public QueryRunner getQueryRunnerForSegments(final Query query, final throw new ISE("Cannot handle subquery: %s", analysis.getDataSource()); } + final JoinFilterRewriteConfig joinFilterRewriteConfig = new JoinFilterRewriteConfig( + QueryContexts.getEnableJoinFilterPushDown(query), + QueryContexts.getEnableJoinFilterRewrite(query), + QueryContexts.getEnableJoinFilterRewriteValueColumnFilters(query), + QueryContexts.getJoinFilterRewriteMaxSize(query) + ); + // segmentMapFn maps each base Segment into a joined Segment if necessary. final Function segmentMapFn = Joinables.createSegmentMapFn( analysis.getPreJoinableClauses(), joinableFactory, cpuTimeAccumulator, - QueryContexts.getEnableJoinFilterPushDown(query), - QueryContexts.getEnableJoinFilterRewrite(query), - QueryContexts.getEnableJoinFilterRewriteValueColumnFilters(query), - QueryContexts.getJoinFilterRewriteMaxSize(query), - query.getFilter() == null ? null : query.getFilter().toFilter(), - query.getVirtualColumns() + joinFilterRewriteConfig, + query ); Iterable> perSegmentRunners = Iterables.transform( diff --git a/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java b/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java index 8283524040f4..dbd043f52bf2 100644 --- a/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java +++ b/server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java @@ -42,6 +42,7 @@ import org.apache.druid.segment.SegmentWrangler; import org.apache.druid.segment.join.JoinableFactory; import org.apache.druid.segment.join.Joinables; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import org.joda.time.Interval; import java.util.HashSet; @@ -100,16 +101,18 @@ public QueryRunner getQueryRunnerForIntervals(final Query query, final final Query prioritizedAndLaned = prioritizeAndLaneQuery(query, segments); final AtomicLong cpuAccumulator = new AtomicLong(0L); + final JoinFilterRewriteConfig joinFilterRewriteConfig = new JoinFilterRewriteConfig( + QueryContexts.getEnableJoinFilterPushDown(prioritizedAndLaned), + QueryContexts.getEnableJoinFilterRewrite(prioritizedAndLaned), + QueryContexts.getEnableJoinFilterRewriteValueColumnFilters(prioritizedAndLaned), + QueryContexts.getJoinFilterRewriteMaxSize(prioritizedAndLaned) + ); final Function segmentMapFn = Joinables.createSegmentMapFn( analysis.getPreJoinableClauses(), joinableFactory, cpuAccumulator, - QueryContexts.getEnableJoinFilterPushDown(prioritizedAndLaned), - QueryContexts.getEnableJoinFilterRewrite(prioritizedAndLaned), - QueryContexts.getEnableJoinFilterRewriteValueColumnFilters(prioritizedAndLaned), - QueryContexts.getJoinFilterRewriteMaxSize(prioritizedAndLaned), - prioritizedAndLaned.getFilter() == null ? null : prioritizedAndLaned.getFilter().toFilter(), - prioritizedAndLaned.getVirtualColumns() + joinFilterRewriteConfig, + query ); final QueryRunnerFactory> queryRunnerFactory = conglomerate.findFactory(prioritizedAndLaned); diff --git a/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java b/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java index 086673fabc13..b2bef3302623 100644 --- a/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java +++ b/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java @@ -57,6 +57,7 @@ import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.join.JoinableFactory; import org.apache.druid.segment.join.Joinables; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import org.apache.druid.server.SegmentManager; import org.apache.druid.server.SetAndVerifyContextQueryRunner; import org.apache.druid.server.initialization.ServerConfig; @@ -192,17 +193,20 @@ public QueryRunner getQueryRunnerForSegments(Query query, Iterable(); } + final JoinFilterRewriteConfig joinFilterRewriteConfig = new JoinFilterRewriteConfig( + QueryContexts.getEnableJoinFilterPushDown(query), + QueryContexts.getEnableJoinFilterRewrite(query), + QueryContexts.getEnableJoinFilterRewriteValueColumnFilters(query), + QueryContexts.getJoinFilterRewriteMaxSize(query) + ); + // segmentMapFn maps each base Segment into a joined Segment if necessary. final Function segmentMapFn = Joinables.createSegmentMapFn( analysis.getPreJoinableClauses(), joinableFactory, cpuTimeAccumulator, - QueryContexts.getEnableJoinFilterPushDown(query), - QueryContexts.getEnableJoinFilterRewrite(query), - QueryContexts.getEnableJoinFilterRewriteValueColumnFilters(query), - QueryContexts.getJoinFilterRewriteMaxSize(query), - query.getFilter() == null ? null : query.getFilter().toFilter(), - query.getVirtualColumns() + joinFilterRewriteConfig, + query ); FunctionalIterable> queryRunners = FunctionalIterable diff --git a/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java b/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java index e41ad268d160..c11bb8179163 100644 --- a/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java +++ b/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java @@ -48,6 +48,7 @@ import org.apache.druid.segment.SegmentReference; import org.apache.druid.segment.join.JoinableFactory; import org.apache.druid.segment.join.Joinables; +import org.apache.druid.segment.join.filter.rewrite.JoinFilterRewriteConfig; import org.apache.druid.timeline.TimelineObjectHolder; import org.apache.druid.timeline.VersionedIntervalTimeline; import org.apache.druid.timeline.partition.PartitionChunk; @@ -139,17 +140,19 @@ public QueryRunner getQueryRunnerForSegments(final Query query, final && !toolChest.canPerformSubquery(((QueryDataSource) analysis.getDataSource()).getQuery())) { throw new ISE("Cannot handle subquery: %s", analysis.getDataSource()); } + final JoinFilterRewriteConfig joinFilterRewriteConfig = new JoinFilterRewriteConfig( + QueryContexts.getEnableJoinFilterPushDown(query), + QueryContexts.getEnableJoinFilterRewrite(query), + QueryContexts.getEnableJoinFilterRewriteValueColumnFilters(query), + QueryContexts.getJoinFilterRewriteMaxSize(query) + ); final Function segmentMapFn = Joinables.createSegmentMapFn( analysis.getPreJoinableClauses(), joinableFactory, new AtomicLong(), - QueryContexts.getEnableJoinFilterPushDown(query), - QueryContexts.getEnableJoinFilterRewrite(query), - QueryContexts.getEnableJoinFilterRewriteValueColumnFilters(query), - QueryContexts.getJoinFilterRewriteMaxSize(query), - query.getFilter() == null ? null : query.getFilter().toFilter(), - query.getVirtualColumns() + joinFilterRewriteConfig, + query ); final QueryRunner baseRunner = new FinalizeResultsQueryRunner<>( diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index 62c1519473fe..1606ef7e7b92 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -31,7 +31,6 @@ import org.apache.druid.common.config.NullHandling; import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.IAE; -import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.JodaUtils; import org.apache.druid.java.util.common.StringUtils; @@ -11917,79 +11916,75 @@ public void testTimeExtractWithTooFewArguments() throws Exception @Parameters(source = QueryContextForJoinProvider.class) public void testNestedGroupByOnInlineDataSourceWithFilter(Map queryContext) throws Exception { - try { - testQuery( - "with abc as" - + "(" - + " SELECT dim1, m2 from druid.foo where \"__time\" >= '2001-01-02'" - + ")" - + ", def as" - + "(" - + " SELECT t1.dim1, SUM(t2.m2) as \"metricSum\" " - + " from abc as t1 inner join abc as t2 on t1.dim1 = t2.dim1" - + " where t1.dim1='def'" - + " group by 1" - + ")" - + "SELECT count(*) from def", - queryContext, - ImmutableList.of( - GroupByQuery - .builder() - .setDataSource( - GroupByQuery - .builder() - .setDataSource( - join( - new QueryDataSource( - newScanQueryBuilder() - .dataSource(CalciteTests.DATASOURCE1) - .intervals(querySegmentSpec(Intervals.of("2001-01-02T00:00:00.000Z/146140482-04-24T15:36:27.903Z"))) - .columns("dim1", "m2") - .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) - .context(queryContext) - .build() - ), - new QueryDataSource( - newScanQueryBuilder() - .dataSource(CalciteTests.DATASOURCE1) - .intervals(querySegmentSpec(Intervals.of("2001-01-02T00:00:00.000Z/146140482-04-24T15:36:27.903Z"))) - .columns("dim1", "m2") - .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) - .context(queryContext) - .build() - ), - "j0", - equalsCondition( - DruidExpression.fromColumn("dim1"), - DruidExpression.fromColumn("j0.dim1") - ), - JoinType.INNER - ) - ) - .setGranularity(Granularities.ALL) - .setInterval(querySegmentSpec(Filtration.eternity())) - .build() - ) - .setGranularity(Granularities.ALL) - .setInterval(querySegmentSpec(Filtration.eternity())) - .build() - ), - ImmutableList.of(new Object[] {1}) - ); - Assert.fail("Expected an ISE to be thrown"); - } - catch (RuntimeException e) { - Throwable cause = e.getCause(); - boolean foundISE = false; - while (cause != null) { - if (cause instanceof ISE) { - foundISE = true; - break; - } - cause = cause.getCause(); - } - Assert.assertTrue(foundISE); - } + // Cannot vectorize due to virtual columns. + cannotVectorize(); + + testQuery( + "with abc as" + + "(" + + " SELECT dim1, m2 from druid.foo where \"__time\" >= '2001-01-02'" + + ")" + + ", def as" + + "(" + + " SELECT t1.dim1, SUM(t2.m2) as \"metricSum\" " + + " from abc as t1 inner join abc as t2 on t1.dim1 = t2.dim1" + + " where t1.dim1='def'" + + " group by 1" + + ")" + + "SELECT count(*) from def", + queryContext, + ImmutableList.of( + GroupByQuery + .builder() + .setDataSource( + GroupByQuery + .builder() + .setDataSource( + join( + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Intervals.of("2001-01-02T00:00:00.000Z/146140482-04-24T15:36:27.903Z"))) + .columns("dim1") + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + new QueryDataSource( + newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Intervals.of("2001-01-02T00:00:00.000Z/146140482-04-24T15:36:27.903Z"))) + .columns("dim1", "m2") + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .context(queryContext) + .build() + ), + "j0.", + equalsCondition( + DruidExpression.fromColumn("dim1"), + DruidExpression.fromColumn("j0.dim1") + ), + JoinType.INNER + ) + ) + .setGranularity(Granularities.ALL) + .setInterval(querySegmentSpec(Filtration.eternity())) + .setDimFilter(selector("dim1", "def", null)) + .setDimensions( + dimensions( + new DefaultDimensionSpec("v0", "d0") + ) + ) + .setVirtualColumns(expressionVirtualColumn("v0", "'def'", ValueType.STRING)) + .build() + ) + .setAggregatorSpecs(aggregators(new CountAggregatorFactory("a0"))) + .setGranularity(Granularities.ALL) + .setInterval(querySegmentSpec(Filtration.eternity())) + .build() + ), + ImmutableList.of(new Object[] {1L}) + ); } @Test From 7496d37612d159783ffb90613e9c2d6bc3aa7356 Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Fri, 19 Jun 2020 11:35:18 -0700 Subject: [PATCH 098/107] fix topn on string columns with non-sorted or non-unique dictionaries (#10053) * fix topn on string columns with non-sorted or non-unique dictionaries * fix metadata tests * refactor, clarify comments and code, fix ci failures --- .../benchmark/TopNTypeInterfaceBenchmark.java | 4 +- .../druid/query/topn/TopNQueryEngine.java | 88 ++++++++---- .../StringTopNColumnAggregatesProcessor.java | 2 +- .../apache/druid/server/QueryStackTests.java | 29 ++++ .../sql/avatica/DruidAvaticaHandlerTest.java | 12 ++ .../sql/calcite/BaseCalciteQueryTest.java | 5 +- .../druid/sql/calcite/CalciteQueryTest.java | 43 ++++++ .../druid/sql/calcite/util/CalciteTests.java | 132 ++++++++++++++++-- .../SpecificSegmentsQuerySegmentWalker.java | 10 +- 9 files changed, 276 insertions(+), 49 deletions(-) diff --git a/benchmarks/src/test/java/org/apache/druid/benchmark/TopNTypeInterfaceBenchmark.java b/benchmarks/src/test/java/org/apache/druid/benchmark/TopNTypeInterfaceBenchmark.java index 241bd80ff721..bf7733db180e 100644 --- a/benchmarks/src/test/java/org/apache/druid/benchmark/TopNTypeInterfaceBenchmark.java +++ b/benchmarks/src/test/java/org/apache/druid/benchmark/TopNTypeInterfaceBenchmark.java @@ -165,7 +165,7 @@ private void setupQueries() queryAggs.add(new DoubleMinAggregatorFactory("minFloatZipf", "minFloatZipf")); queryAggs.add(new HyperUniquesAggregatorFactory("hyperUniquesMet", "hyper")); - // Use an IdentityExtractionFn to force usage of DimExtractionTopNAlgorithm + // Use an IdentityExtractionFn to force usage of HeapBasedTopNAlgorithm TopNQueryBuilder queryBuilderString = new TopNQueryBuilder() .dataSource("blah") .granularity(Granularities.ALL) @@ -174,7 +174,7 @@ private void setupQueries() .intervals(intervalSpec) .aggregators(queryAggs); - // DimExtractionTopNAlgorithm is always used for numeric columns + // HeapBasedTopNAlgorithm is always used for numeric columns TopNQueryBuilder queryBuilderLong = new TopNQueryBuilder() .dataSource("blah") .granularity(Granularities.ALL) diff --git a/processing/src/main/java/org/apache/druid/query/topn/TopNQueryEngine.java b/processing/src/main/java/org/apache/druid/query/topn/TopNQueryEngine.java index 1d1bccdd7b1b..f1eab7fe2686 100644 --- a/processing/src/main/java/org/apache/druid/query/topn/TopNQueryEngine.java +++ b/processing/src/main/java/org/apache/druid/query/topn/TopNQueryEngine.java @@ -120,36 +120,34 @@ private TopNMapFn getMapFn( final TopNAlgorithm topNAlgorithm; - if ( - selector.isHasExtractionFn() && + if (requiresHeapAlgorithm(selector, query, columnCapabilities)) { + // heap based algorithm selection + if (selector.isHasExtractionFn() && dimension.equals(ColumnHolder.TIME_COLUMN_NAME)) { // TimeExtractionTopNAlgorithm can work on any single-value dimension of type long. - // Once we have arbitrary dimension types following check should be replaced by checking - // that the column is of type long and single-value. - dimension.equals(ColumnHolder.TIME_COLUMN_NAME) - ) { - // A special TimeExtractionTopNAlgorithm is required, since DimExtractionTopNAlgorithm - // currently relies on the dimension cardinality to support lexicographic sorting - topNAlgorithm = new TimeExtractionTopNAlgorithm(adapter, query); - } else if (selector.isHasExtractionFn()) { - topNAlgorithm = new HeapBasedTopNAlgorithm(adapter, query); - } else if (columnCapabilities == null || !(columnCapabilities.getType() == ValueType.STRING - && columnCapabilities.isDictionaryEncoded())) { - // Use HeapBasedTopNAlgorithm for non-Strings and for non-dictionary-encoded Strings, and for things we don't know - // which can happen for 'inline' data sources when this is run on the broker - topNAlgorithm = new HeapBasedTopNAlgorithm(adapter, query); - } else if (query.getDimensionSpec().getOutputType() != ValueType.STRING) { - // Use HeapBasedTopNAlgorithm when the dimension output type is a non-String. (It's like an extractionFn: there can be - // a many-to-one mapping, since numeric types can't represent all possible values of other types.) - topNAlgorithm = new HeapBasedTopNAlgorithm(adapter, query); - } else if (selector.isAggregateAllMetrics()) { - // sorted by dimension - topNAlgorithm = new PooledTopNAlgorithm(adapter, query, bufferPool); - } else if (selector.isAggregateTopNMetricFirst() || query.getContextBoolean("doAggregateTopNMetricFirst", false)) { - // high cardinality dimensions with larger result sets - topNAlgorithm = new AggregateTopNMetricFirstAlgorithm(adapter, query, bufferPool); + // We might be able to use this for any long column with an extraction function, that is + // ValueType.LONG.equals(columnCapabilities.getType()) + // but this needs investigation to ensure that it is an improvement over HeapBasedTopNAlgorithm + + // A special TimeExtractionTopNAlgorithm is required since DimExtractionTopNAlgorithm + // currently relies on the dimension cardinality to support lexicographic sorting + topNAlgorithm = new TimeExtractionTopNAlgorithm(adapter, query); + } else { + topNAlgorithm = new HeapBasedTopNAlgorithm(adapter, query); + } } else { - // anything else - topNAlgorithm = new PooledTopNAlgorithm(adapter, query, bufferPool); + // pool based algorithm selection + if (selector.isAggregateAllMetrics()) { + // if sorted by dimension we should aggregate all metrics in a single pass, use the regular pooled algorithm for + // this + topNAlgorithm = new PooledTopNAlgorithm(adapter, query, bufferPool); + } else if (selector.isAggregateTopNMetricFirst() || query.getContextBoolean("doAggregateTopNMetricFirst", false)) { + // for high cardinality dimensions with larger result sets we aggregate with only the ordering aggregation to + // compute the first 'n' values, and then for the rest of the metrics but for only the 'n' values + topNAlgorithm = new AggregateTopNMetricFirstAlgorithm(adapter, query, bufferPool); + } else { + // anything else, use the regular pooled algorithm + topNAlgorithm = new PooledTopNAlgorithm(adapter, query, bufferPool); + } } if (queryMetrics != null) { queryMetrics.algorithm(topNAlgorithm); @@ -158,6 +156,40 @@ private TopNMapFn getMapFn( return new TopNMapFn(query, topNAlgorithm); } + /** + * {@link PooledTopNAlgorithm} (and {@link AggregateTopNMetricFirstAlgorithm} which utilizes the pooled + * algorithm) are optimized off-heap algorithms for aggregating dictionary encoded string columns. These algorithms + * rely on dictionary ids being unique so to aggregate on the dictionary ids directly and defer + * {@link org.apache.druid.segment.DimensionSelector#lookupName(int)} until as late as possible in query processing. + * + * When these conditions are not true, we have an on-heap fall-back algorithm, the {@link HeapBasedTopNAlgorithm} + * (and {@link TimeExtractionTopNAlgorithm} for a specialized form for long columns) which aggregates on values of + * selectors. + */ + private static boolean requiresHeapAlgorithm( + final TopNAlgorithmSelector selector, + final TopNQuery query, + final ColumnCapabilities capabilities + ) + { + if (selector.isHasExtractionFn()) { + // extraction functions can have a many to one mapping, and should use a heap algorithm + return true; + } + + if (query.getDimensionSpec().getOutputType() != ValueType.STRING) { + // non-string output cannot use the pooled algorith, even if the underlying selector supports it + return true; + } + if (capabilities != null && capabilities.getType() == ValueType.STRING) { + // string columns must use the on heap algorithm unless they have the following capabilites + return !(capabilities.isDictionaryEncoded() && capabilities.areDictionaryValuesUnique().isTrue()); + } else { + // non-strings are not eligible to use the pooled algorithm, and should use a heap algorithm + return true; + } + } + public static boolean canApplyExtractionInPost(TopNQuery query) { return query.getDimensionSpec() != null diff --git a/processing/src/main/java/org/apache/druid/query/topn/types/StringTopNColumnAggregatesProcessor.java b/processing/src/main/java/org/apache/druid/query/topn/types/StringTopNColumnAggregatesProcessor.java index cea7c775a758..92b2e8a3fe9c 100644 --- a/processing/src/main/java/org/apache/druid/query/topn/types/StringTopNColumnAggregatesProcessor.java +++ b/processing/src/main/java/org/apache/druid/query/topn/types/StringTopNColumnAggregatesProcessor.java @@ -59,7 +59,7 @@ public Aggregator[][] getRowSelector(TopNQuery query, TopNParams params, Storage throw new UnsupportedOperationException("Cannot operate on a dimension with unknown cardinality"); } - // This method is used for the DimExtractionTopNAlgorithm only. + // This method is used for the HeapBasedTopNAlgorithm only. // Unlike regular topN we cannot rely on ordering to optimize. // Optimization possibly requires a reverse lookup from value to ID, which is // not possible when applying an extraction function diff --git a/server/src/test/java/org/apache/druid/server/QueryStackTests.java b/server/src/test/java/org/apache/druid/server/QueryStackTests.java index 445d8d64495c..8a468d2edc38 100644 --- a/server/src/test/java/org/apache/druid/server/QueryStackTests.java +++ b/server/src/test/java/org/apache/druid/server/QueryStackTests.java @@ -25,9 +25,12 @@ import org.apache.druid.java.util.common.Pair; import org.apache.druid.java.util.common.io.Closer; import org.apache.druid.java.util.emitter.service.ServiceEmitter; +import org.apache.druid.query.DataSource; import org.apache.druid.query.DefaultGenericQueryMetricsFactory; import org.apache.druid.query.DefaultQueryRunnerFactoryConglomerate; import org.apache.druid.query.DruidProcessingConfig; +import org.apache.druid.query.InlineDataSource; +import org.apache.druid.query.LookupDataSource; import org.apache.druid.query.Query; import org.apache.druid.query.QueryRunnerFactory; import org.apache.druid.query.QueryRunnerFactoryConglomerate; @@ -41,6 +44,7 @@ import org.apache.druid.query.groupby.GroupByQueryRunnerFactory; import org.apache.druid.query.groupby.GroupByQueryRunnerTest; import org.apache.druid.query.groupby.strategy.GroupByStrategySelector; +import org.apache.druid.query.lookup.LookupExtractorFactoryContainerProvider; import org.apache.druid.query.metadata.SegmentMetadataQueryConfig; import org.apache.druid.query.metadata.SegmentMetadataQueryQueryToolChest; import org.apache.druid.query.metadata.SegmentMetadataQueryRunnerFactory; @@ -61,7 +65,10 @@ import org.apache.druid.segment.ReferenceCountingSegment; import org.apache.druid.segment.SegmentWrangler; import org.apache.druid.segment.TestHelper; +import org.apache.druid.segment.join.InlineJoinableFactory; import org.apache.druid.segment.join.JoinableFactory; +import org.apache.druid.segment.join.LookupJoinableFactory; +import org.apache.druid.segment.join.MapJoinableFactoryTest; import org.apache.druid.server.initialization.ServerConfig; import org.apache.druid.server.metrics.NoopServiceEmitter; import org.apache.druid.server.scheduling.ManualQueryPrioritizationStrategy; @@ -273,4 +280,26 @@ public int getNumMergeBuffers() return conglomerate; } + public static JoinableFactory makeJoinableFactoryForLookup( + LookupExtractorFactoryContainerProvider lookupProvider + ) + { + return makeJoinableFactoryFromDefault(lookupProvider, null); + } + + public static JoinableFactory makeJoinableFactoryFromDefault( + @Nullable LookupExtractorFactoryContainerProvider lookupProvider, + @Nullable Map, JoinableFactory> custom + ) + { + ImmutableMap.Builder, JoinableFactory> builder = ImmutableMap.builder(); + builder.put(InlineDataSource.class, new InlineJoinableFactory()); + if (lookupProvider != null) { + builder.put(LookupDataSource.class, new LookupJoinableFactory(lookupProvider)); + } + if (custom != null) { + builder.putAll(custom); + } + return MapJoinableFactoryTest.fromMap(builder.build()); + } } diff --git a/sql/src/test/java/org/apache/druid/sql/avatica/DruidAvaticaHandlerTest.java b/sql/src/test/java/org/apache/druid/sql/avatica/DruidAvaticaHandlerTest.java index 6b836b83ea4d..3f43f76c1757 100644 --- a/sql/src/test/java/org/apache/druid/sql/avatica/DruidAvaticaHandlerTest.java +++ b/sql/src/test/java/org/apache/druid/sql/avatica/DruidAvaticaHandlerTest.java @@ -384,6 +384,12 @@ public void testDatabaseMetaDataTables() throws Exception final DatabaseMetaData metaData = client.getMetaData(); Assert.assertEquals( ImmutableList.of( + row( + Pair.of("TABLE_CAT", "druid"), + Pair.of("TABLE_NAME", CalciteTests.BROADCAST_DATASOURCE), + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_TYPE", "TABLE") + ), row( Pair.of("TABLE_CAT", "druid"), Pair.of("TABLE_NAME", CalciteTests.DATASOURCE1), @@ -441,6 +447,12 @@ public void testDatabaseMetaDataTablesAsSuperuser() throws Exception final DatabaseMetaData metaData = superuserClient.getMetaData(); Assert.assertEquals( ImmutableList.of( + row( + Pair.of("TABLE_CAT", "druid"), + Pair.of("TABLE_NAME", CalciteTests.BROADCAST_DATASOURCE), + Pair.of("TABLE_SCHEM", "druid"), + Pair.of("TABLE_TYPE", "TABLE") + ), row( Pair.of("TABLE_CAT", "druid"), Pair.of("TABLE_NAME", CalciteTests.DATASOURCE1), diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java index 952b3cfe2f71..7c475e5fe037 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/BaseCalciteQueryTest.java @@ -422,7 +422,10 @@ public QueryLogHook getQueryLogHook() @Before public void setUp() throws Exception { - walker = CalciteTests.createMockWalker(conglomerate, temporaryFolder.newFolder()); + walker = CalciteTests.createMockWalker( + conglomerate, + temporaryFolder.newFolder() + ); } @After diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index 1606ef7e7b92..622842fd32eb 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -38,6 +38,7 @@ import org.apache.druid.java.util.common.granularity.PeriodGranularity; import org.apache.druid.math.expr.ExprMacroTable; import org.apache.druid.query.Druids; +import org.apache.druid.query.GlobalTableDataSource; import org.apache.druid.query.LookupDataSource; import org.apache.druid.query.QueryContexts; import org.apache.druid.query.QueryDataSource; @@ -712,6 +713,7 @@ public void testInformationSchemaTables() throws Exception + "WHERE TABLE_TYPE IN ('SYSTEM_TABLE', 'TABLE', 'VIEW')", ImmutableList.of(), ImmutableList.builder() + .add(new Object[]{"druid", CalciteTests.BROADCAST_DATASOURCE, "TABLE", "YES", "YES"}) .add(new Object[]{"druid", CalciteTests.DATASOURCE1, "TABLE", "NO", "NO"}) .add(new Object[]{"druid", CalciteTests.DATASOURCE2, "TABLE", "NO", "NO"}) .add(new Object[]{"druid", CalciteTests.DATASOURCE4, "TABLE", "NO", "NO"}) @@ -741,6 +743,7 @@ public void testInformationSchemaTables() throws Exception CalciteTests.SUPER_USER_AUTH_RESULT, ImmutableList.of(), ImmutableList.builder() + .add(new Object[]{"druid", CalciteTests.BROADCAST_DATASOURCE, "TABLE", "YES", "YES"}) .add(new Object[]{"druid", CalciteTests.DATASOURCE1, "TABLE", "NO", "NO"}) .add(new Object[]{"druid", CalciteTests.DATASOURCE2, "TABLE", "NO", "NO"}) .add(new Object[]{"druid", CalciteTests.DATASOURCE4, "TABLE", "NO", "NO"}) @@ -14997,6 +15000,46 @@ public void testValidationErrorWrongTypeLiteral() throws Exception ); } + @Test + @Parameters(source = QueryContextForJoinProvider.class) + public void testTopNOnStringWithNonSortedOrUniqueDictionary(Map queryContext) throws Exception + { + testQuery( + "SELECT druid.broadcast.dim4, COUNT(*)\n" + + "FROM druid.numfoo\n" + + "INNER JOIN druid.broadcast ON numfoo.dim4 = broadcast.dim4\n" + + "GROUP BY 1 ORDER BY 2 LIMIT 4", + queryContext, + ImmutableList.of( + new TopNQueryBuilder() + .dataSource( + join( + new TableDataSource(CalciteTests.DATASOURCE3), + new GlobalTableDataSource(CalciteTests.BROADCAST_DATASOURCE), + "j0.", + equalsCondition( + DruidExpression.fromColumn("dim4"), + DruidExpression.fromColumn("j0.dim4") + ), + JoinType.INNER + + ) + ) + .intervals(querySegmentSpec(Filtration.eternity())) + .dimension(new DefaultDimensionSpec("j0.dim4", "_d0", ValueType.STRING)) + .threshold(4) + .aggregators(aggregators(new CountAggregatorFactory("a0"))) + .context(queryContext) + .metric(new InvertedTopNMetricSpec(new NumericTopNMetricSpec("a0"))) + .build() + ), + ImmutableList.of( + new Object[]{"a", 9L}, + new Object[]{"b", 9L} + ) + ); + } + /** * This is a provider of query contexts that should be used by join tests. * It tests various configs that can be passed to join queries. All the configs provided by this provider should diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java index ca683a243aea..dfee466bcef3 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java @@ -50,13 +50,17 @@ import org.apache.druid.discovery.NodeRole; import org.apache.druid.guice.ExpressionModule; import org.apache.druid.guice.annotations.Json; +import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.emitter.core.NoopEmitter; import org.apache.druid.java.util.emitter.service.ServiceEmitter; import org.apache.druid.java.util.http.client.HttpClient; import org.apache.druid.java.util.http.client.Request; import org.apache.druid.java.util.http.client.response.HttpResponseHandler; import org.apache.druid.math.expr.ExprMacroTable; +import org.apache.druid.query.DataSource; import org.apache.druid.query.DefaultGenericQueryMetricsFactory; +import org.apache.druid.query.GlobalTableDataSource; +import org.apache.druid.query.InlineDataSource; import org.apache.druid.query.Query; import org.apache.druid.query.QueryRunnerFactoryConglomerate; import org.apache.druid.query.QuerySegmentWalker; @@ -72,8 +76,14 @@ import org.apache.druid.segment.IndexBuilder; import org.apache.druid.segment.QueryableIndex; import org.apache.druid.segment.TestHelper; +import org.apache.druid.segment.column.RowSignature; +import org.apache.druid.segment.column.ValueType; import org.apache.druid.segment.incremental.IncrementalIndexSchema; -import org.apache.druid.segment.join.MapJoinableFactory; +import org.apache.druid.segment.join.JoinConditionAnalysis; +import org.apache.druid.segment.join.Joinable; +import org.apache.druid.segment.join.JoinableFactory; +import org.apache.druid.segment.join.table.IndexedTableJoinable; +import org.apache.druid.segment.join.table.RowBasedIndexedTable; import org.apache.druid.segment.loading.SegmentLoader; import org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory; import org.apache.druid.server.DruidNode; @@ -125,6 +135,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.Executor; import java.util.function.BooleanSupplier; @@ -140,6 +151,7 @@ public class CalciteTests public static final String DATASOURCE3 = "numfoo"; public static final String DATASOURCE4 = "foo4"; public static final String DATASOURCE5 = "lotsocolumns"; + public static final String BROADCAST_DATASOURCE = "broadcast"; public static final String FORBIDDEN_DATASOURCE = "forbiddenDatasource"; public static final String SOME_DATASOURCE = "some_datasource"; public static final String SOME_DATSOURCE_ESCAPED = "some\\_datasource"; @@ -214,7 +226,7 @@ public AuthenticationResult createEscalatedAuthenticationResult() private static final String TIMESTAMP_COLUMN = "t"; - private static final Injector INJECTOR = Guice.createInjector( + public static final Injector INJECTOR = Guice.createInjector( binder -> { binder.bind(Key.get(ObjectMapper.class, Json.class)).toInstance(TestHelper.makeJsonMapper()); @@ -605,6 +617,68 @@ public AuthenticationResult createEscalatedAuthenticationResult() ) ); + private static final InlineDataSource JOINABLE_BACKING_DATA = InlineDataSource.fromIterable( + RAW_ROWS1_WITH_NUMERIC_DIMS.stream().map(x -> new Object[]{ + x.get("dim1"), + x.get("dim2"), + x.get("dim3"), + x.get("dim4"), + x.get("dim5"), + x.get("d1"), + x.get("d2"), + x.get("f1"), + x.get("f2"), + x.get("l1"), + x.get("l2") + }).collect(Collectors.toList()), + RowSignature.builder() + .add("dim1", ValueType.STRING) + .add("dim2", ValueType.STRING) + .add("dim3", ValueType.STRING) + .add("dim4", ValueType.STRING) + .add("dim5", ValueType.STRING) + .add("d1", ValueType.DOUBLE) + .add("d2", ValueType.DOUBLE) + .add("f1", ValueType.FLOAT) + .add("f2", ValueType.FLOAT) + .add("l1", ValueType.LONG) + .add("l2", ValueType.LONG) + .build() + ); + + private static final Set KEY_COLUMNS = ImmutableSet.of("dim4"); + + private static final RowBasedIndexedTable JOINABLE_TABLE = new RowBasedIndexedTable( + JOINABLE_BACKING_DATA.getRowsAsList(), + JOINABLE_BACKING_DATA.rowAdapter(), + JOINABLE_BACKING_DATA.getRowSignature(), + KEY_COLUMNS, + DateTimes.nowUtc().toString() + ); + + public static GlobalTableDataSource CUSTOM_TABLE = new GlobalTableDataSource(BROADCAST_DATASOURCE); + + public static JoinableFactory CUSTOM_ROW_TABLE_JOINABLE = new JoinableFactory() + { + @Override + public boolean isDirectlyJoinable(DataSource dataSource) + { + return CUSTOM_TABLE.equals(dataSource); + } + + @Override + public Optional build( + DataSource dataSource, + JoinConditionAnalysis condition + ) + { + if (dataSource instanceof GlobalTableDataSource) { + return Optional.of(new IndexedTableJoinable(JOINABLE_TABLE)); + } + return Optional.empty(); + } + }; + private CalciteTests() { // No instantiation. @@ -649,12 +723,28 @@ public static ObjectMapper getJsonMapper() return INJECTOR.getInstance(Key.get(ObjectMapper.class, Json.class)); } + public static JoinableFactory createDefaultJoinableFactory() + { + return QueryStackTests.makeJoinableFactoryFromDefault( + INJECTOR.getInstance(LookupExtractorFactoryContainerProvider.class), + ImmutableMap.of( + GlobalTableDataSource.class, + CUSTOM_ROW_TABLE_JOINABLE + ) + ); + } + public static SpecificSegmentsQuerySegmentWalker createMockWalker( final QueryRunnerFactoryConglomerate conglomerate, final File tmpDir ) { - return createMockWalker(conglomerate, tmpDir, QueryStackTests.DEFAULT_NOOP_SCHEDULER); + return createMockWalker( + conglomerate, + tmpDir, + QueryStackTests.DEFAULT_NOOP_SCHEDULER, + createDefaultJoinableFactory() + ); } public static SpecificSegmentsQuerySegmentWalker createMockWalker( @@ -662,6 +752,16 @@ public static SpecificSegmentsQuerySegmentWalker createMockWalker( final File tmpDir, final QueryScheduler scheduler ) + { + return createMockWalker(conglomerate, tmpDir, scheduler, null); + } + + public static SpecificSegmentsQuerySegmentWalker createMockWalker( + final QueryRunnerFactoryConglomerate conglomerate, + final File tmpDir, + final QueryScheduler scheduler, + final JoinableFactory joinableFactory + ) { final QueryableIndex index1 = IndexBuilder .create() @@ -713,7 +813,7 @@ public static SpecificSegmentsQuerySegmentWalker createMockWalker( final QueryableIndex someDatasourceIndex = IndexBuilder .create() - .tmpDir(new File(tmpDir, "1")) + .tmpDir(new File(tmpDir, "6")) .segmentWriteOutMediumFactory(OffHeapMemorySegmentWriteOutMediumFactory.instance()) .schema(INDEX_SCHEMA) .rows(ROWS1) @@ -721,7 +821,7 @@ public static SpecificSegmentsQuerySegmentWalker createMockWalker( final QueryableIndex someXDatasourceIndex = IndexBuilder .create() - .tmpDir(new File(tmpDir, "1")) + .tmpDir(new File(tmpDir, "7")) .segmentWriteOutMediumFactory(OffHeapMemorySegmentWriteOutMediumFactory.instance()) .schema(INDEX_SCHEMA_WITH_X_COLUMNS) .rows(RAW_ROWS1_X) @@ -731,7 +831,7 @@ public static SpecificSegmentsQuerySegmentWalker createMockWalker( return new SpecificSegmentsQuerySegmentWalker( conglomerate, INJECTOR.getInstance(LookupExtractorFactoryContainerProvider.class), - null, + joinableFactory, scheduler ).add( DataSegment.builder() @@ -805,6 +905,15 @@ public static SpecificSegmentsQuerySegmentWalker createMockWalker( .size(0) .build(), someXDatasourceIndex + ).add( + DataSegment.builder() + .dataSource(BROADCAST_DATASOURCE) + .interval(indexNumericDims.getDataInterval()) + .version("1") + .shardSpec(new LinearShardSpec(0)) + .size(0) + .build(), + indexNumericDims ); } @@ -979,8 +1088,15 @@ private static DruidSchema createMockSchema( final DruidSchema schema = new DruidSchema( CalciteTests.createMockQueryLifecycleFactory(walker, conglomerate), new TestServerInventoryView(walker.getSegments()), - new SegmentManager(EasyMock.createMock(SegmentLoader.class)), - new MapJoinableFactory(ImmutableMap.of()), + new SegmentManager(EasyMock.createMock(SegmentLoader.class)) + { + @Override + public Set getDataSourceNames() + { + return ImmutableSet.of(BROADCAST_DATASOURCE); + } + }, + createDefaultJoinableFactory(), plannerConfig, viewManager, TEST_AUTHENTICATOR_ESCALATOR diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/util/SpecificSegmentsQuerySegmentWalker.java b/sql/src/test/java/org/apache/druid/sql/calcite/util/SpecificSegmentsQuerySegmentWalker.java index 0490420cdd73..7fde642ca8c0 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/util/SpecificSegmentsQuerySegmentWalker.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/util/SpecificSegmentsQuerySegmentWalker.java @@ -40,10 +40,7 @@ import org.apache.druid.segment.ReferenceCountingSegment; import org.apache.druid.segment.Segment; import org.apache.druid.segment.SegmentWrangler; -import org.apache.druid.segment.join.InlineJoinableFactory; import org.apache.druid.segment.join.JoinableFactory; -import org.apache.druid.segment.join.LookupJoinableFactory; -import org.apache.druid.segment.join.MapJoinableFactoryTest; import org.apache.druid.server.ClientQuerySegmentWalker; import org.apache.druid.server.QueryScheduler; import org.apache.druid.server.QueryStackTests; @@ -91,12 +88,7 @@ public SpecificSegmentsQuerySegmentWalker( final JoinableFactory joinableFactoryToUse; if (joinableFactory == null) { - joinableFactoryToUse = MapJoinableFactoryTest.fromMap( - ImmutableMap., JoinableFactory>builder() - .put(InlineDataSource.class, new InlineJoinableFactory()) - .put(LookupDataSource.class, new LookupJoinableFactory(lookupProvider)) - .build() - ); + joinableFactoryToUse = QueryStackTests.makeJoinableFactoryForLookup(lookupProvider); } else { joinableFactoryToUse = joinableFactory; } From 05c2278e3629253d4d37d77b499d38fdb614c2b3 Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn Date: Fri, 19 Jun 2020 17:18:56 -1000 Subject: [PATCH 099/107] Add safeguard to make sure new Rules added are aware of Rule usage in loadstatus API (#10054) * Add safeguard to make sure new Rules added are aware of Rule usuage in loadstatus API * address comments * address comments * add tests --- .../server/coordinator/DruidCoordinator.java | 42 ++++--------------- .../coordinator/SegmentReplicantLookup.java | 28 +++++++++++-- .../server/coordinator/ServerHolder.java | 8 +++- .../rules/BroadcastDistributionRule.java | 35 ++++++++++++++++ .../server/coordinator/rules/DropRule.java | 6 +++ .../server/coordinator/rules/LoadRule.java | 29 +++++++++++++ .../druid/server/coordinator/rules/Rule.java | 26 ++++++++++++ .../server/coordinator/ServerHolderTest.java | 22 +++++++++- 8 files changed, 156 insertions(+), 40 deletions(-) diff --git a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java index 0dcf636b7442..64168e11d76b 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java @@ -29,7 +29,6 @@ import it.unimi.dsi.fastutil.objects.Object2IntMaps; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2LongMap; -import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.utils.ZKPaths; import org.apache.druid.client.DataSourcesSnapshot; @@ -69,7 +68,6 @@ import org.apache.druid.server.coordinator.duty.MarkAsUnusedOvershadowedSegments; import org.apache.druid.server.coordinator.duty.RunRules; import org.apache.druid.server.coordinator.duty.UnloadUnusedSegments; -import org.apache.druid.server.coordinator.rules.BroadcastDistributionRule; import org.apache.druid.server.coordinator.rules.LoadRule; import org.apache.druid.server.coordinator.rules.Rule; import org.apache.druid.server.initialization.ZkPathsConfig; @@ -275,13 +273,6 @@ public Map> computeUnderReplicationCountsPerDataS ) { final Map> underReplicationCountsPerDataSourcePerTier = new HashMap<>(); - final Set decommissioningServers = getDynamicConfigs().getDecommissioningNodes(); - final List broadcastTargetServers = serverInventoryView - .getInventory() - .stream() - .filter(druidServer -> druidServer.isSegmentBroadcastTarget() && !decommissioningServers.contains(druidServer.getHost())) - .map(DruidServer::toImmutableDruidServer) - .collect(Collectors.toList()); if (segmentReplicantLookup == null) { return underReplicationCountsPerDataSourcePerTier; @@ -294,36 +285,19 @@ public Map> computeUnderReplicationCountsPerDataS for (final Rule rule : rules) { if (!rule.appliesTo(segment, now)) { + // Rule did not match. Continue to the next Rule. continue; } - - if (rule instanceof LoadRule) { - ((LoadRule) rule) - .getTieredReplicants() - .forEach((final String tier, final Integer ruleReplicants) -> { - int currentReplicants = segmentReplicantLookup.getLoadedReplicants(segment.getId(), tier); - Object2LongMap underReplicationPerDataSource = underReplicationCountsPerDataSourcePerTier - .computeIfAbsent(tier, ignored -> new Object2LongOpenHashMap<>()); - ((Object2LongOpenHashMap) underReplicationPerDataSource) - .addTo(segment.getDataSource(), Math.max(ruleReplicants - currentReplicants, 0)); - }); + if (!rule.canLoadSegments()) { + // Rule matched but rule does not and cannot load segments. + // Hence, there is no need to update underReplicationCountsPerDataSourcePerTier map + break; } - if (rule instanceof BroadcastDistributionRule) { - for (ImmutableDruidServer server : broadcastTargetServers) { - Object2LongMap underReplicationPerDataSource = underReplicationCountsPerDataSourcePerTier - .computeIfAbsent(server.getTier(), ignored -> new Object2LongOpenHashMap<>()); - if (server.getSegment(segment.getId()) == null) { - ((Object2LongOpenHashMap) underReplicationPerDataSource) - .addTo(segment.getDataSource(), 1); - } else { - // This make sure that every datasource has a entry even if the all segments are loaded - underReplicationPerDataSource.putIfAbsent(segment.getDataSource(), 0); - } - } - } + rule.updateUnderReplicated(underReplicationCountsPerDataSourcePerTier, segmentReplicantLookup, segment); - // only the first matching rule applies + // Only the first matching rule applies. This is because the Coordinator cycle through all used segments + // and match each segment with the first rule that applies. Each segment may only match a single rule. break; } } diff --git a/server/src/main/java/org/apache/druid/server/coordinator/SegmentReplicantLookup.java b/server/src/main/java/org/apache/druid/server/coordinator/SegmentReplicantLookup.java index cd04bfdcb8a5..b86ca0106d39 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/SegmentReplicantLookup.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/SegmentReplicantLookup.java @@ -21,6 +21,8 @@ import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import org.apache.druid.client.ImmutableDruidServer; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.SegmentId; @@ -62,19 +64,22 @@ public static SegmentReplicantLookup make(DruidCluster cluster) } } - return new SegmentReplicantLookup(segmentsInCluster, loadingSegments); + return new SegmentReplicantLookup(segmentsInCluster, loadingSegments, cluster); } private final Table segmentsInCluster; private final Table loadingSegments; + private final DruidCluster cluster; private SegmentReplicantLookup( Table segmentsInCluster, - Table loadingSegments + Table loadingSegments, + DruidCluster cluster ) { this.segmentsInCluster = segmentsInCluster; this.loadingSegments = loadingSegments; + this.cluster = cluster; } public Map getClusterTiers(SegmentId segmentId) @@ -93,7 +98,7 @@ int getLoadedReplicants(SegmentId segmentId) return retVal; } - int getLoadedReplicants(SegmentId segmentId, String tier) + public int getLoadedReplicants(SegmentId segmentId, String tier) { Integer retVal = segmentsInCluster.get(segmentId, tier); return (retVal == null) ? 0 : retVal; @@ -124,4 +129,21 @@ public int getTotalReplicants(SegmentId segmentId, String tier) { return getLoadedReplicants(segmentId, tier) + getLoadingReplicants(segmentId, tier); } + + public Object2LongMap getBroadcastUnderReplication(SegmentId segmentId) + { + Object2LongOpenHashMap perTier = new Object2LongOpenHashMap<>(); + for (ServerHolder holder : cluster.getAllServers()) { + // Only record tier entry for server that is segment broadcast target + if (holder.getServer().getType().isSegmentBroadcastTarget()) { + // Every broadcast target server should be serving 1 replica of the segment + if (!holder.isServingSegment(segmentId)) { + perTier.addTo(holder.getServer().getTier(), 1L); + } else { + perTier.putIfAbsent(holder.getServer().getTier(), 0); + } + } + } + return perTier; + } } diff --git a/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java b/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java index 26fa9a54c7ff..43fdaaef1d1d 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java @@ -22,6 +22,7 @@ import org.apache.druid.client.ImmutableDruidServer; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.SegmentId; import java.util.Objects; @@ -114,7 +115,7 @@ public long getAvailableSize() public boolean isServingSegment(DataSegment segment) { - return server.getSegment(segment.getId()) != null; + return isServingSegment(segment.getId()); } public boolean isLoadingSegment(DataSegment segment) @@ -132,6 +133,11 @@ public int getNumberOfSegmentsInQueue() return peon.getNumberOfSegmentsInQueue(); } + public boolean isServingSegment(SegmentId segmentId) + { + return server.getSegment(segmentId) != null; + } + @Override public int compareTo(ServerHolder serverHolder) { diff --git a/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java b/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java index 35ff39ea6505..0b8c37b79318 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java @@ -19,15 +19,19 @@ package org.apache.druid.server.coordinator.rules; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import org.apache.druid.java.util.emitter.EmittingLogger; import org.apache.druid.server.coordination.ServerType; import org.apache.druid.server.coordinator.CoordinatorStats; import org.apache.druid.server.coordinator.DruidCoordinator; import org.apache.druid.server.coordinator.DruidCoordinatorRuntimeParams; +import org.apache.druid.server.coordinator.SegmentReplicantLookup; import org.apache.druid.server.coordinator.ServerHolder; import org.apache.druid.timeline.DataSegment; import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -71,6 +75,37 @@ public CoordinatorStats run(DruidCoordinator coordinator, DruidCoordinatorRuntim .accumulate(drop(dropServerHolders, segment)); } + @Override + public boolean canLoadSegments() + { + return true; + } + + @Override + public void updateUnderReplicated( + Map> underReplicatedPerTier, + SegmentReplicantLookup segmentReplicantLookup, + DataSegment segment + ) + { + Object2LongMap underReplicatedBroadcastTiers = segmentReplicantLookup.getBroadcastUnderReplication(segment.getId()); + for (final Object2LongMap.Entry entry : underReplicatedBroadcastTiers.object2LongEntrySet()) { + final String tier = entry.getKey(); + final long underReplicatedCount = entry.getLongValue(); + underReplicatedPerTier.compute(tier, (_tier, existing) -> { + Object2LongMap underReplicationPerDataSource = existing; + if (existing == null) { + underReplicationPerDataSource = new Object2LongOpenHashMap<>(); + } + underReplicationPerDataSource.compute( + segment.getDataSource(), + (_datasource, count) -> count != null ? count + underReplicatedCount : underReplicatedCount + ); + return underReplicationPerDataSource; + }); + } + } + private CoordinatorStats assign( final Set serverHolders, final DataSegment segment diff --git a/server/src/main/java/org/apache/druid/server/coordinator/rules/DropRule.java b/server/src/main/java/org/apache/druid/server/coordinator/rules/DropRule.java index c565df9b58be..7ffc7a211551 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/rules/DropRule.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/rules/DropRule.java @@ -37,4 +37,10 @@ public CoordinatorStats run(DruidCoordinator coordinator, DruidCoordinatorRuntim stats.addToGlobalStat("deletedCount", 1); return stats; } + + @Override + public boolean canLoadSegments() + { + return false; + } } diff --git a/server/src/main/java/org/apache/druid/server/coordinator/rules/LoadRule.java b/server/src/main/java/org/apache/druid/server/coordinator/rules/LoadRule.java index 80912544a4e9..d0bf5b8ad56b 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/rules/LoadRule.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/rules/LoadRule.java @@ -21,6 +21,8 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.java.util.emitter.EmittingLogger; @@ -30,6 +32,7 @@ import org.apache.druid.server.coordinator.DruidCoordinator; import org.apache.druid.server.coordinator.DruidCoordinatorRuntimeParams; import org.apache.druid.server.coordinator.ReplicationThrottler; +import org.apache.druid.server.coordinator.SegmentReplicantLookup; import org.apache.druid.server.coordinator.ServerHolder; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.SegmentId; @@ -90,6 +93,32 @@ public CoordinatorStats run( } } + @Override + public boolean canLoadSegments() + { + return true; + } + + @Override + public void updateUnderReplicated( + Map> underReplicatedPerTier, + SegmentReplicantLookup segmentReplicantLookup, + DataSegment segment + ) + { + getTieredReplicants().forEach((final String tier, final Integer ruleReplicants) -> { + int currentReplicants = segmentReplicantLookup.getLoadedReplicants(segment.getId(), tier); + Object2LongMap underReplicationPerDataSource = underReplicatedPerTier.computeIfAbsent( + tier, + ignored -> new Object2LongOpenHashMap<>() + ); + ((Object2LongOpenHashMap) underReplicationPerDataSource).addTo( + segment.getDataSource(), + Math.max(ruleReplicants - currentReplicants, 0) + ); + }); + } + /** * @param stats {@link CoordinatorStats} to accumulate assignment statistics. */ diff --git a/server/src/main/java/org/apache/druid/server/coordinator/rules/Rule.java b/server/src/main/java/org/apache/druid/server/coordinator/rules/Rule.java index d475f67d4c1b..02c552f639cc 100644 --- a/server/src/main/java/org/apache/druid/server/coordinator/rules/Rule.java +++ b/server/src/main/java/org/apache/druid/server/coordinator/rules/Rule.java @@ -21,13 +21,18 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.google.common.base.Preconditions; +import it.unimi.dsi.fastutil.objects.Object2LongMap; import org.apache.druid.server.coordinator.CoordinatorStats; import org.apache.druid.server.coordinator.DruidCoordinator; import org.apache.druid.server.coordinator.DruidCoordinatorRuntimeParams; +import org.apache.druid.server.coordinator.SegmentReplicantLookup; import org.apache.druid.timeline.DataSegment; import org.joda.time.DateTime; import org.joda.time.Interval; +import java.util.Map; + /** */ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @@ -51,6 +56,27 @@ public interface Rule boolean appliesTo(Interval interval, DateTime referenceTimestamp); + /** + * Return true if this Rule can load segment onto one or more type of Druid node, otherwise return false. + * Any Rule that returns true for this method should implement logic for calculating segment under replicated + * in {@link Rule#updateUnderReplicated} + */ + boolean canLoadSegments(); + + /** + * This method should update the {@param underReplicatedPerTier} with the replication count of the + * {@param segment}. Rule that returns true for {@link Rule#canLoadSegments()} must override this method. + * Note that {@param underReplicatedPerTier} is a map of tier -> { dataSource -> underReplicationCount } + */ + default void updateUnderReplicated( + Map> underReplicatedPerTier, + SegmentReplicantLookup segmentReplicantLookup, + DataSegment segment + ) + { + Preconditions.checkArgument(!canLoadSegments()); + } + /** * {@link DruidCoordinatorRuntimeParams#getUsedSegments()} must not be called in Rule's code, because the used * segments are not specified for the {@link DruidCoordinatorRuntimeParams} passed into Rule's code. This is because diff --git a/server/src/test/java/org/apache/druid/server/coordinator/ServerHolderTest.java b/server/src/test/java/org/apache/druid/server/coordinator/ServerHolderTest.java index fcebbdee8209..cb1ee0425b61 100644 --- a/server/src/test/java/org/apache/druid/server/coordinator/ServerHolderTest.java +++ b/server/src/test/java/org/apache/druid/server/coordinator/ServerHolderTest.java @@ -39,7 +39,7 @@ public class ServerHolderTest { private static final List SEGMENTS = ImmutableList.of( new DataSegment( - "test", + "src1", Intervals.of("2015-04-12/2015-04-13"), "1", ImmutableMap.of("containerName", "container1", "blobPath", "blobPath1"), @@ -50,7 +50,7 @@ public class ServerHolderTest 1 ), new DataSegment( - "test", + "src2", Intervals.of("2015-04-12/2015-04-13"), "1", ImmutableMap.of("containerName", "container2", "blobPath", "blobPath2"), @@ -177,4 +177,22 @@ public void testEquals() Assert.assertNotEquals(h1, h4); Assert.assertNotEquals(h1, h5); } + + @Test + public void testIsServingSegment() + { + final ServerHolder h1 = new ServerHolder( + new ImmutableDruidServer( + new DruidServerMetadata("name1", "host1", null, 100L, ServerType.HISTORICAL, "tier1", 0), + 0L, + ImmutableMap.of("src1", DATA_SOURCES.get("src1")), + 1 + ), + new LoadQueuePeonTester() + ); + Assert.assertTrue(h1.isServingSegment(SEGMENTS.get(0))); + Assert.assertFalse(h1.isServingSegment(SEGMENTS.get(1))); + Assert.assertTrue(h1.isServingSegment(SEGMENTS.get(0).getId())); + Assert.assertFalse(h1.isServingSegment(SEGMENTS.get(1).getId())); + } } From 64062dcb8661460554e48ae07664452ab4da4568 Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn Date: Fri, 19 Jun 2020 17:29:25 -1000 Subject: [PATCH 100/107] SketchAggregator.updateUnion should handle null inside List update object (#10055) --- .../datasketches/theta/SketchAggregator.java | 4 +++- .../theta/SketchAggregationTest.java | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/SketchAggregator.java b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/SketchAggregator.java index 6b73390c192f..b7df576e9498 100644 --- a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/SketchAggregator.java +++ b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/theta/SketchAggregator.java @@ -122,7 +122,9 @@ static void updateUnion(Union union, Object update) union.update((long[]) update); } else if (update instanceof List) { for (Object entry : (List) update) { - union.update(entry.toString()); + if (entry != null) { + union.update(entry.toString()); + } } } else { throw new ISE("Illegal type received while theta sketch merging [%s]", update.getClass()); diff --git a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/SketchAggregationTest.java b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/SketchAggregationTest.java index 19716d968e6e..3e7d47d8b07d 100644 --- a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/SketchAggregationTest.java +++ b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/theta/SketchAggregationTest.java @@ -35,8 +35,10 @@ import org.apache.druid.java.util.common.guava.Sequence; import org.apache.druid.query.Query; import org.apache.druid.query.aggregation.AggregationTestHelper; +import org.apache.druid.query.aggregation.Aggregator; import org.apache.druid.query.aggregation.AggregatorFactory; import org.apache.druid.query.aggregation.PostAggregator; +import org.apache.druid.query.aggregation.TestObjectColumnSelector; import org.apache.druid.query.aggregation.post.FieldAccessPostAggregator; import org.apache.druid.query.groupby.GroupByQuery; import org.apache.druid.query.groupby.GroupByQueryConfig; @@ -493,6 +495,25 @@ public void testRelocation() Assert.assertEquals(holders[0].getEstimate(), holders[1].getEstimate(), 0); } + @Test + public void testUpdateUnionWithNullInList() + { + List value = new ArrayList<>(); + value.add("foo"); + value.add(null); + value.add("bar"); + List[] columnValues = new List[]{value}; + final TestObjectColumnSelector selector = new TestObjectColumnSelector(columnValues); + final Aggregator agg = new SketchAggregator(selector, 4096); + agg.aggregate(); + Assert.assertFalse(agg.isNull()); + Assert.assertNotNull(agg.get()); + Assert.assertTrue(agg.get() instanceof SketchHolder); + Assert.assertEquals(2, ((SketchHolder) agg.get()).getEstimate(), 0); + Assert.assertNotNull(((SketchHolder) agg.get()).getSketch()); + Assert.assertEquals(2, ((SketchHolder) agg.get()).getSketch().getEstimate(), 0); + } + private void assertPostAggregatorSerde(PostAggregator agg) throws Exception { Assert.assertEquals( From 51a9ef72f5eb6d3da87adc446d81c2fa5d566332 Mon Sep 17 00:00:00 2001 From: Jianhuan Liu Date: Sat, 20 Jun 2020 17:14:54 +0800 Subject: [PATCH 101/107] fix docs error in hadoop-based part (#9907) * fix docs error: google to azure and hdfs to http * fix docs error: indexSpecForIntermediatePersists of tuningConfig in hadoop-based batch part * fix docs error: logParseExceptions of tuningConfig in hadoop-based batch part * fix docs error: maxParseExceptions of tuningConfig in hadoop-based batch part --- docs/ingestion/hadoop.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ingestion/hadoop.md b/docs/ingestion/hadoop.md index 2101c1b9fc9a..0f5db6a33b78 100644 --- a/docs/ingestion/hadoop.md +++ b/docs/ingestion/hadoop.md @@ -329,12 +329,12 @@ The tuningConfig is optional and default parameters will be used if no tuningCon |useCombiner|Boolean|Use Hadoop combiner to merge rows at mapper if possible.|no (default == false)| |jobProperties|Object|A map of properties to add to the Hadoop job configuration, see below for details.|no (default == null)| |indexSpec|Object|Tune how data is indexed. See [`indexSpec`](index.md#indexspec) on the main ingestion page for more information.|no| -|indexSpecForIntermediatePersists|defines segment storage format options to be used at indexing time for intermediate persisted temporary segments. this can be used to disable dimension/metric compression on intermediate segments to reduce memory required for final merging. however, disabling compression on intermediate segments might increase page cache use while they are used before getting merged into final segment published, see [`indexSpec`](index.md#indexspec) for possible values.|no (default = same as indexSpec)| +|indexSpecForIntermediatePersists|Object|defines segment storage format options to be used at indexing time for intermediate persisted temporary segments. this can be used to disable dimension/metric compression on intermediate segments to reduce memory required for final merging. however, disabling compression on intermediate segments might increase page cache use while they are used before getting merged into final segment published, see [`indexSpec`](index.md#indexspec) for possible values.|no (default = same as indexSpec)| |numBackgroundPersistThreads|Integer|The number of new background threads to use for incremental persists. Using this feature causes a notable increase in memory pressure and CPU usage but will make the job finish more quickly. If changing from the default of 0 (use current thread for persists), we recommend setting it to 1.|no (default == 0)| |forceExtendableShardSpecs|Boolean|Forces use of extendable shardSpecs. Hash-based partitioning always uses an extendable shardSpec. For single-dimension partitioning, this option should be set to true to use an extendable shardSpec. For partitioning, please check [Partitioning specification](#partitionsspec). This option can be useful when you need to append more data to existing dataSource.|no (default = false)| |useExplicitVersion|Boolean|Forces HadoopIndexTask to use version.|no (default = false)| -|logParseExceptions|Boolean|If true, log an error message when a parsing exception occurs, containing information about the row where the error occurred.|false|no| -|maxParseExceptions|Integer|The maximum number of parse exceptions that can occur before the task halts ingestion and fails. Overrides `ignoreInvalidRows` if `maxParseExceptions` is defined.|unlimited|no| +|logParseExceptions|Boolean|If true, log an error message when a parsing exception occurs, containing information about the row where the error occurred.|no(default = false)| +|maxParseExceptions|Integer|The maximum number of parse exceptions that can occur before the task halts ingestion and fails. Overrides `ignoreInvalidRows` if `maxParseExceptions` is defined.|no(default = unlimited)| |useYarnRMJobStatusFallback|Boolean|If the Hadoop jobs created by the indexing task are unable to retrieve their completion status from the JobHistory server, and this parameter is true, the indexing task will try to fetch the application status from `http:///ws/v1/cluster/apps/`, where `` is the value of `yarn.resourcemanager.webapp.address` in your Hadoop configuration. This flag is intended as a fallback for cases where an indexing task's jobs succeed, but the JobHistory server is unavailable, causing the indexing task to fail because it cannot determine the job statuses.|no (default = true)| ### `jobProperties` From db9ab6441c2e3c2e7696e3f89a32f62fecfd465b Mon Sep 17 00:00:00 2001 From: Clint Wylie Date: Mon, 22 Jun 2020 09:08:50 -0700 Subject: [PATCH 102/107] minor rework of topn algorithm selection for clarity and more javadocs (#10058) * minor refactor of topn engine algorithm selection for clarity * adjust * more javadoc --- .../AggregateTopNMetricFirstAlgorithm.java | 7 +++ .../druid/query/topn/PooledTopNAlgorithm.java | 12 ++++ .../druid/query/topn/TopNQueryEngine.java | 63 ++++++++++++------- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/processing/src/main/java/org/apache/druid/query/topn/AggregateTopNMetricFirstAlgorithm.java b/processing/src/main/java/org/apache/druid/query/topn/AggregateTopNMetricFirstAlgorithm.java index 270ec1640866..ab5eef290851 100644 --- a/processing/src/main/java/org/apache/druid/query/topn/AggregateTopNMetricFirstAlgorithm.java +++ b/processing/src/main/java/org/apache/druid/query/topn/AggregateTopNMetricFirstAlgorithm.java @@ -36,6 +36,13 @@ import java.util.List; /** + * This {@link TopNAlgorithm} is tailored to processing aggregates on high cardility columns which are likely to have + * larger result sets. Internally it uses a 2 phase approach to compute the top-n result using the + * {@link PooledTopNAlgorithm} for each phase. The first phase is to process the segment with only the order-by + * aggregator to compute which values constitute the top 'n' results. With this information, a actual result set + * is computed by a second run of the {@link PooledTopNAlgorithm}, this time with all aggregators, but only considering + * the values from the 'n' results to avoid performing any aggregations that would have been thrown away for results + * that didn't make the top-n. */ public class AggregateTopNMetricFirstAlgorithm implements TopNAlgorithm { diff --git a/processing/src/main/java/org/apache/druid/query/topn/PooledTopNAlgorithm.java b/processing/src/main/java/org/apache/druid/query/topn/PooledTopNAlgorithm.java index 7cd93401b1c0..19ebb543578a 100644 --- a/processing/src/main/java/org/apache/druid/query/topn/PooledTopNAlgorithm.java +++ b/processing/src/main/java/org/apache/druid/query/topn/PooledTopNAlgorithm.java @@ -36,6 +36,7 @@ import org.apache.druid.segment.DimensionSelector; import org.apache.druid.segment.FilteredOffset; import org.apache.druid.segment.StorageAdapter; +import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.data.IndexedInts; import org.apache.druid.segment.data.Offset; import org.apache.druid.segment.historical.HistoricalColumnSelector; @@ -49,7 +50,18 @@ import java.util.List; /** + * This {@link TopNAlgorithm} is highly specialized for processing aggregates on string columns that are + * {@link ColumnCapabilities#isDictionaryEncoded()} and {@link ColumnCapabilities#areDictionaryValuesUnique()}. This + * algorithm is built around using a direct {@link ByteBuffer} from the 'processing pool' of intermediary results + * buffers, to aggregate using the dictionary id directly as the key, to defer looking up the value until is necessary. * + * At runtime, this implementation is specialized with wizardry to optimize for processing common top-n query shapes, + * see {@link #computeSpecializedScanAndAggregateImplementations}, + * {@link Generic1AggPooledTopNScanner} and {@link Generic1AggPooledTopNScannerPrototype}, + * {@link Generic2AggPooledTopNScanner} and {@link Generic2AggPooledTopNScannerPrototype}, + * {@link org.apache.druid.query.monomorphicprocessing.CalledFromHotLoop}, + * {@link org.apache.druid.query.monomorphicprocessing.HotLoopCallee}, + * {@link org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector} for more details. */ public class PooledTopNAlgorithm extends BaseTopNAlgorithm diff --git a/processing/src/main/java/org/apache/druid/query/topn/TopNQueryEngine.java b/processing/src/main/java/org/apache/druid/query/topn/TopNQueryEngine.java index f1eab7fe2686..eb22dc9b4ae6 100644 --- a/processing/src/main/java/org/apache/druid/query/topn/TopNQueryEngine.java +++ b/processing/src/main/java/org/apache/druid/query/topn/TopNQueryEngine.java @@ -53,6 +53,12 @@ public TopNQueryEngine(NonBlockingPool bufferPool) this.bufferPool = bufferPool; } + /** + * Do the thing - process a {@link StorageAdapter} into a {@link Sequence} of {@link TopNResultValue}, with one of the + * fine {@link TopNAlgorithm} available chosen based on the type of column being aggregated. The algorithm provides a + * mapping function to process rows from the adapter {@link org.apache.druid.segment.Cursor} to apply + * {@link AggregatorFactory} and create or update {@link TopNResultValue} + */ public Sequence> query( final TopNQuery query, final StorageAdapter adapter, @@ -71,7 +77,9 @@ public Sequence> query( final TopNMapFn mapFn = getMapFn(query, adapter, queryMetrics); Preconditions.checkArgument( - queryIntervals.size() == 1, "Can only handle a single interval, got[%s]", queryIntervals + queryIntervals.size() == 1, + "Can only handle a single interval, got[%s]", + queryIntervals ); return Sequences.filter( @@ -95,6 +103,9 @@ public Sequence> query( ); } + /** + * Choose the best {@link TopNAlgorithm} for the given query. + */ private TopNMapFn getMapFn( final TopNQuery query, final StorageAdapter adapter, @@ -120,22 +131,8 @@ private TopNMapFn getMapFn( final TopNAlgorithm topNAlgorithm; - if (requiresHeapAlgorithm(selector, query, columnCapabilities)) { - // heap based algorithm selection - if (selector.isHasExtractionFn() && dimension.equals(ColumnHolder.TIME_COLUMN_NAME)) { - // TimeExtractionTopNAlgorithm can work on any single-value dimension of type long. - // We might be able to use this for any long column with an extraction function, that is - // ValueType.LONG.equals(columnCapabilities.getType()) - // but this needs investigation to ensure that it is an improvement over HeapBasedTopNAlgorithm - - // A special TimeExtractionTopNAlgorithm is required since DimExtractionTopNAlgorithm - // currently relies on the dimension cardinality to support lexicographic sorting - topNAlgorithm = new TimeExtractionTopNAlgorithm(adapter, query); - } else { - topNAlgorithm = new HeapBasedTopNAlgorithm(adapter, query); - } - } else { - // pool based algorithm selection + if (canUsePooledAlgorithm(selector, query, columnCapabilities)) { + // pool based algorithm selection, if we can if (selector.isAggregateAllMetrics()) { // if sorted by dimension we should aggregate all metrics in a single pass, use the regular pooled algorithm for // this @@ -148,6 +145,20 @@ private TopNMapFn getMapFn( // anything else, use the regular pooled algorithm topNAlgorithm = new PooledTopNAlgorithm(adapter, query, bufferPool); } + } else { + // heap based algorithm selection, if we must + if (selector.isHasExtractionFn() && dimension.equals(ColumnHolder.TIME_COLUMN_NAME)) { + // TimeExtractionTopNAlgorithm can work on any single-value dimension of type long. + // We might be able to use this for any long column with an extraction function, that is + // ValueType.LONG.equals(columnCapabilities.getType()) + // but this needs investigation to ensure that it is an improvement over HeapBasedTopNAlgorithm + + // A special TimeExtractionTopNAlgorithm is required since HeapBasedTopNAlgorithm + // currently relies on the dimension cardinality to support lexicographic sorting + topNAlgorithm = new TimeExtractionTopNAlgorithm(adapter, query); + } else { + topNAlgorithm = new HeapBasedTopNAlgorithm(adapter, query); + } } if (queryMetrics != null) { queryMetrics.algorithm(topNAlgorithm); @@ -166,7 +177,7 @@ private TopNMapFn getMapFn( * (and {@link TimeExtractionTopNAlgorithm} for a specialized form for long columns) which aggregates on values of * selectors. */ - private static boolean requiresHeapAlgorithm( + private static boolean canUsePooledAlgorithm( final TopNAlgorithmSelector selector, final TopNQuery query, final ColumnCapabilities capabilities @@ -174,22 +185,30 @@ private static boolean requiresHeapAlgorithm( { if (selector.isHasExtractionFn()) { // extraction functions can have a many to one mapping, and should use a heap algorithm - return true; + return false; } if (query.getDimensionSpec().getOutputType() != ValueType.STRING) { // non-string output cannot use the pooled algorith, even if the underlying selector supports it - return true; + return false; } if (capabilities != null && capabilities.getType() == ValueType.STRING) { // string columns must use the on heap algorithm unless they have the following capabilites - return !(capabilities.isDictionaryEncoded() && capabilities.areDictionaryValuesUnique().isTrue()); + return capabilities.isDictionaryEncoded() && capabilities.areDictionaryValuesUnique().isTrue(); } else { // non-strings are not eligible to use the pooled algorithm, and should use a heap algorithm - return true; + return false; } } + /** + * {@link ExtractionFn} which are one to one may have their execution deferred until as late as possible, since the + * which value is used as the grouping key itself doesn't particularly matter. For top-n, this method allows the + * query to be transformed in {@link TopNQueryQueryToolChest#preMergeQueryDecoration} to strip off the + * {@link ExtractionFn} on the broker, so that a more optimized algorithm (e.g. {@link PooledTopNAlgorithm}) can be + * chosen for processing segments, and then added back and evaluated against the final merged result sets on the + * broker via {@link TopNQueryQueryToolChest#postMergeQueryDecoration}. + */ public static boolean canApplyExtractionInPost(TopNQuery query) { return query.getDimensionSpec() != null From fc264b5d1da1beac3f6623f321fc55af1e9c3f31 Mon Sep 17 00:00:00 2001 From: Dylan Wylie Date: Tue, 23 Jun 2020 21:56:44 +0100 Subject: [PATCH 103/107] change default number of segment loading threads (#9856) * change default number of segment loading threads * fix docs * missed file * min -> max for segment loading threads Co-authored-by: Dylan --- docs/configuration/index.md | 2 +- .../org/apache/druid/segment/loading/SegmentLoaderConfig.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/index.md b/docs/configuration/index.md index 34b458a49b2b..b17b74dbd80d 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -1383,7 +1383,7 @@ These Historical configurations can be defined in the `historical/runtime.proper |`druid.segmentCache.dropSegmentDelayMillis`|How long a process delays before completely dropping segment.|30000 (30 seconds)| |`druid.segmentCache.infoDir`|Historical processes keep track of the segments they are serving so that when the process is restarted they can reload the same segments without waiting for the Coordinator to reassign. This path defines where this metadata is kept. Directory will be created if needed.|${first_location}/info_dir| |`druid.segmentCache.announceIntervalMillis`|How frequently to announce segments while segments are loading from cache. Set this value to zero to wait for all segments to be loaded before announcing.|5000 (5 seconds)| -|`druid.segmentCache.numLoadingThreads`|How many segments to drop or load concurrently from deep storage. Note that the work of loading segments involves downloading segments from deep storage, decompressing them and loading them to a memory mapped location. So the work is not all I/O Bound. Depending on CPU and network load, one could possibly increase this config to a higher value.|Number of cores| +|`druid.segmentCache.numLoadingThreads`|How many segments to drop or load concurrently from deep storage. Note that the work of loading segments involves downloading segments from deep storage, decompressing them and loading them to a memory mapped location. So the work is not all I/O Bound. Depending on CPU and network load, one could possibly increase this config to a higher value.|max(1,Number of cores / 6)| |`druid.segmentCache.numBootstrapThreads`|How many segments to load concurrently during historical startup.|`druid.segmentCache.numLoadingThreads`| |`druid.segmentCache.lazyLoadOnStart`|Whether or not to load segment columns metadata lazily during historical startup. When set to true, Historical startup time will be dramatically improved by deferring segment loading until the first time that segment takes part in a query, which will incur this cost instead. One catch is that if historical crashes while in the process of downloading and creating segment files, it is possible to end up with a corrupted segment on disk, this requires manual intervention to delete corrupted files. When the flag is set to true, historical startup would complete successfully and queries using this segment would fail at runtime.|false| |`druid.coordinator.loadqueuepeon.curator.numCallbackThreads`|Number of threads for executing callback actions associated with loading or dropping of segments. One might want to increase this number when noticing clusters are lagging behind w.r.t. balancing segments across historical nodes.|2| diff --git a/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderConfig.java b/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderConfig.java index 39b3bde3129d..883ac5b0211e 100644 --- a/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderConfig.java +++ b/server/src/main/java/org/apache/druid/segment/loading/SegmentLoaderConfig.java @@ -49,7 +49,7 @@ public class SegmentLoaderConfig private int announceIntervalMillis = 0; // do not background announce @JsonProperty("numLoadingThreads") - private int numLoadingThreads = JvmUtils.getRuntimeInfo().getAvailableProcessors(); + private int numLoadingThreads = Math.max(1, JvmUtils.getRuntimeInfo().getAvailableProcessors() / 6); @JsonProperty("numBootstrapThreads") private Integer numBootstrapThreads = null; From e6c13be2f83eab8d4a2702dae23c3826e78e9a96 Mon Sep 17 00:00:00 2001 From: Harshpreet Singh Date: Tue, 23 Jun 2020 15:49:34 -0700 Subject: [PATCH 104/107] retry 500 and 503 errors against kinesis (#10059) * retry 500 and 503 errors against kinesis * add test that exercises retry logic * more branch coverage * retry 500 and 503 on getRecords request when fetching sequence numberu Co-authored-by: Harshpreet Singh --- .../kinesis/KinesisRecordSupplier.java | 7 +- .../kinesis/KinesisRecordSupplierTest.java | 89 +++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplier.java b/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplier.java index 65ae1f62ba07..23e5bec573f1 100644 --- a/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplier.java +++ b/extensions-core/kinesis-indexing-service/src/main/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplier.java @@ -110,7 +110,8 @@ private static boolean isServiceExceptionRecoverable(AmazonServiceException ex) { final boolean isIOException = ex.getCause() instanceof IOException; final boolean isTimeout = "RequestTimeout".equals(ex.getErrorCode()); - return isIOException || isTimeout; + final boolean isInternalError = ex.getStatusCode() == 500 || ex.getStatusCode() == 503; + return isIOException || isTimeout || isInternalError; } /** @@ -809,6 +810,10 @@ private String getSequenceNumber(StreamPartition partition, ShardIterato ); return true; } + if (throwable instanceof AmazonServiceException) { + AmazonServiceException ase = (AmazonServiceException) throwable; + return isServiceExceptionRecoverable(ase); + } return false; }, GET_SEQUENCE_NUMBER_RETRY_COUNT diff --git a/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplierTest.java b/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplierTest.java index 6b2f32f6cdda..881750de2a79 100644 --- a/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplierTest.java +++ b/extensions-core/kinesis-indexing-service/src/test/java/org/apache/druid/indexing/kinesis/KinesisRecordSupplierTest.java @@ -19,6 +19,7 @@ package org.apache.druid.indexing.kinesis; +import com.amazonaws.AmazonServiceException; import com.amazonaws.services.kinesis.AmazonKinesis; import com.amazonaws.services.kinesis.AmazonKinesisClient; import com.amazonaws.services.kinesis.model.DescribeStreamRequest; @@ -316,6 +317,94 @@ public void testPoll() throws InterruptedException Assert.assertEquals(SHARDS_LAG_MILLIS, recordSupplier.getPartitionResourcesTimeLag()); } + @Test + public void testPollWithKinesisInternalFailure() throws InterruptedException + { + recordsPerFetch = 100; + + EasyMock.expect(kinesis.getShardIterator( + EasyMock.anyObject(), + EasyMock.eq(SHARD_ID0), + EasyMock.anyString(), + EasyMock.anyString() + )).andReturn( + getShardIteratorResult0).anyTimes(); + + EasyMock.expect(kinesis.getShardIterator( + EasyMock.anyObject(), + EasyMock.eq(SHARD_ID1), + EasyMock.anyString(), + EasyMock.anyString() + )).andReturn( + getShardIteratorResult1).anyTimes(); + + EasyMock.expect(getShardIteratorResult0.getShardIterator()).andReturn(SHARD0_ITERATOR).anyTimes(); + EasyMock.expect(getShardIteratorResult1.getShardIterator()).andReturn(SHARD1_ITERATOR).anyTimes(); + EasyMock.expect(kinesis.getRecords(generateGetRecordsReq(SHARD0_ITERATOR, recordsPerFetch))) + .andReturn(getRecordsResult0) + .anyTimes(); + EasyMock.expect(kinesis.getRecords(generateGetRecordsReq(SHARD1_ITERATOR, recordsPerFetch))) + .andReturn(getRecordsResult1) + .anyTimes(); + AmazonServiceException getException = new AmazonServiceException("InternalFailure"); + getException.setErrorCode("InternalFailure"); + getException.setStatusCode(500); + getException.setServiceName("AmazonKinesis"); + EasyMock.expect(getRecordsResult0.getRecords()).andThrow(getException).once(); + EasyMock.expect(getRecordsResult0.getRecords()).andReturn(SHARD0_RECORDS).once(); + AmazonServiceException getException2 = new AmazonServiceException("InternalFailure"); + getException2.setErrorCode("InternalFailure"); + getException2.setStatusCode(503); + getException2.setServiceName("AmazonKinesis"); + EasyMock.expect(getRecordsResult1.getRecords()).andThrow(getException2).once(); + EasyMock.expect(getRecordsResult1.getRecords()).andReturn(SHARD1_RECORDS).once(); + EasyMock.expect(getRecordsResult0.getNextShardIterator()).andReturn(null).anyTimes(); + EasyMock.expect(getRecordsResult1.getNextShardIterator()).andReturn(null).anyTimes(); + EasyMock.expect(getRecordsResult0.getMillisBehindLatest()).andReturn(SHARD0_LAG_MILLIS).once(); + EasyMock.expect(getRecordsResult0.getMillisBehindLatest()).andReturn(SHARD0_LAG_MILLIS).once(); + EasyMock.expect(getRecordsResult1.getMillisBehindLatest()).andReturn(SHARD1_LAG_MILLIS).once(); + EasyMock.expect(getRecordsResult1.getMillisBehindLatest()).andReturn(SHARD1_LAG_MILLIS).once(); + + replayAll(); + + Set> partitions = ImmutableSet.of( + StreamPartition.of(STREAM, SHARD_ID0), + StreamPartition.of(STREAM, SHARD_ID1) + ); + + + recordSupplier = new KinesisRecordSupplier( + kinesis, + recordsPerFetch, + 0, + 2, + false, + 100, + 5000, + 5000, + 60000, + 100, + true + ); + + recordSupplier.assign(partitions); + recordSupplier.seekToEarliest(partitions); + recordSupplier.start(); + + while (recordSupplier.bufferSize() < 14) { + Thread.sleep(100); + } + + List> polledRecords = cleanRecords(recordSupplier.poll( + POLL_TIMEOUT_MILLIS)); + + verifyAll(); + + Assert.assertEquals(partitions, recordSupplier.getAssignment()); + Assert.assertTrue(polledRecords.containsAll(ALL_RECORDS)); + Assert.assertEquals(SHARDS_LAG_MILLIS, recordSupplier.getPartitionResourcesTimeLag()); + } + @Test public void testSeek() throws InterruptedException From 94e2a0c5d0a0d4c936885263c32411b3763a4c74 Mon Sep 17 00:00:00 2001 From: sthetland Date: Tue, 23 Jun 2020 17:39:48 -0700 Subject: [PATCH 105/107] Druid user permissions (#10047) * Druid user permissions apply in the console * Update index.md * noting user warning in console page; some minor shuffling * noting user warning in console page; some minor shuffling 1 * touchups * link checking fixes * Updated per suggestions --- docs/design/processes.md | 2 +- docs/design/router.md | 4 ++-- docs/operations/druid-console.md | 9 +++++++-- docs/operations/management-uis.md | 21 +++------------------ docs/tutorials/index.md | 4 ++++ website/i18n/en.json | 2 +- website/sidebars.json | 6 +++--- 7 files changed, 21 insertions(+), 27 deletions(-) diff --git a/docs/design/processes.md b/docs/design/processes.md index bbcd917145dd..4c1e46a46a77 100644 --- a/docs/design/processes.md +++ b/docs/design/processes.md @@ -78,7 +78,7 @@ caller. End users typically query Brokers rather than querying Historicals or Mi Overlords, and Coordinators. They are optional since you can also simply contact the Druid Brokers, Overlords, and Coordinators directly. -The Router also runs the [Druid Console](../operations/management-uis.html#druid-console), a management UI for datasources, segments, tasks, data processes (Historicals and MiddleManagers), and coordinator dynamic configuration. The user can also run SQL and native Druid queries within the console. +The Router also runs the [Druid Console](../operations/druid-console.md), a management UI for datasources, segments, tasks, data processes (Historicals and MiddleManagers), and coordinator dynamic configuration. The user can also run SQL and native Druid queries within the console. ### Data server diff --git a/docs/design/router.md b/docs/design/router.md index c5f73084a873..cc037e229da8 100644 --- a/docs/design/router.md +++ b/docs/design/router.md @@ -24,13 +24,13 @@ title: "Router Process" > The Router is an optional and [experimental](../development/experimental.md) feature due to the fact that its recommended place in the Druid cluster architecture is still evolving. -> However, it has been battle-tested in production, and it hosts the powerful [Druid Console](../operations/management-uis.html#druid-console), so you should feel safe deploying it. +> However, it has been battle-tested in production, and it hosts the powerful [Druid Console](../operations/druid-console.md), so you should feel safe deploying it. The Apache Druid Router process can be used to route queries to different Broker processes. By default, the broker routes queries based on how [Rules](../operations/rule-configuration.md) are set up. For example, if 1 month of recent data is loaded into a `hot` cluster, queries that fall within the recent month can be routed to a dedicated set of brokers. Queries outside this range are routed to another set of brokers. This set up provides query isolation such that queries for more important data are not impacted by queries for less important data. For query routing purposes, you should only ever need the Router process if you have a Druid cluster well into the terabyte range. -In addition to query routing, the Router also runs the [Druid Console](../operations/management-uis.html#druid-console), a management UI for datasources, segments, tasks, data processes (Historicals and MiddleManagers), and coordinator dynamic configuration. The user can also run SQL and native Druid queries within the console. +In addition to query routing, the Router also runs the [Druid Console](../operations/druid-console.md), a management UI for datasources, segments, tasks, data processes (Historicals and MiddleManagers), and coordinator dynamic configuration. The user can also run SQL and native Druid queries within the console. ### Configuration diff --git a/docs/operations/druid-console.md b/docs/operations/druid-console.md index 24856e7afdd6..2bda0e92250a 100644 --- a/docs/operations/druid-console.md +++ b/docs/operations/druid-console.md @@ -22,20 +22,25 @@ title: "Web console" ~ under the License. --> +Druid include a console for managing datasources, segments, tasks, data processes (Historicals and MiddleManagers), and coordinator dynamic configuration. Users can also run SQL and native Druid queries in the console. The Druid Console is hosted by the [Router](../design/router.md) process. -In addition, the following cluster settings must be enabled: +The following cluster settings must be enabled, as they are by default: - the Router's [management proxy](../design/router.html#enabling-the-management-proxy) must be enabled. - the Broker processes in the cluster must have [Druid SQL](../querying/sql.md) enabled. -After enabling Druid SQL on the Brokers and deploying a Router with the management proxy enabled, the Druid console can be accessed at: +The Druid console can be accessed at: ``` http://: ``` +> It is important to note that any Druid console user will have, effectively, the same file permissions as the user under which Druid runs. One way these permissions are surfaced is in the file browser dialog. The dialog +will show console users the files that the underlying user has permissions to. In general, avoid running Druid as +root user. Consider creating a dedicated user account for running Druid. + Below is a description of the high-level features and functionality of the Druid Console ## Home diff --git a/docs/operations/management-uis.md b/docs/operations/management-uis.md index bb3e80bf5e88..2b1d23470351 100644 --- a/docs/operations/management-uis.md +++ b/docs/operations/management-uis.md @@ -1,6 +1,6 @@ --- id: management-uis -title: "Management UIs" +title: "Legacy Management UIs" --- -## Druid console +## Legacy consoles Druid provides a console for managing datasources, segments, tasks, data processes (Historicals and MiddleManagers), and coordinator dynamic configuration. The user can also run SQL and native Druid queries within the console. -The Druid Console is hosted by the [Router](../design/router.md) process. We recommend running the Router process on your [Query server](../design/processes.md). - -In addition, the following cluster settings must be enabled: - -- the Router's [management proxy](../design/router.html#enabling-the-management-proxy) must be enabled. -- the Broker processes in the cluster must have [Druid SQL](../querying/sql.md) enabled. - -After enabling Druid SQL on the Brokers and deploying a Router with the management proxy enabled, the Druid console can be accessed at: - -``` -http://: -``` +For more information on the Druid Console, have a look at the [Druid Console overview](./druid-console.md) The Druid Console contains all of the functionality provided by the older consoles described below, which are still available if needed. The legacy consoles may be replaced by the Druid Console in the future. -For more information on the features of the Druid Console have a look at the [Druid Console overview](./druid-console.md) - -## Legacy consoles - These older consoles provide a subset of the functionality of the Druid Console. We recommend using the Druid Console if possible. ### Coordinator consoles diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md index 2ee1b42eeb6e..5c8bdd422cab 100644 --- a/docs/tutorials/index.md +++ b/docs/tutorials/index.md @@ -54,6 +54,10 @@ The software requirements for the installation machine are: `DRUID_JAVA_HOME` if there is more than one instance of Java. To verify Java requirements for your environment, run the `bin/verify-java` script. +Before installing a production Druid instance, be sure to consider the user account on the operating system under +which Druid will run. This is important because any Druid console user will have, effectively, the same permissions as +that user. So, for example, the file browser UI will show console users the files that the underlying user can +access. In general, avoid running Druid as root user. Consider creating a dedicated user account for running Druid. ## Step 1. Install Druid diff --git a/website/i18n/en.json b/website/i18n/en.json index 9561136a5e59..6c40a6157082 100644 --- a/website/i18n/en.json +++ b/website/i18n/en.json @@ -334,7 +334,7 @@ "title": "kubernetes" }, "operations/management-uis": { - "title": "Management UIs" + "title": "Legacy Management UIs" }, "operations/metadata-migration": { "title": "Metadata Migration" diff --git a/website/sidebars.json b/website/sidebars.json index da8a73458417..019f8eeef89e 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -111,7 +111,8 @@ "configuration/logging" ], "Operations": [ - "operations/management-uis", + "operations/druid-console", + "operations/getting-started", "operations/basic-cluster-tuning", "operations/api-reference", "operations/high-availability", @@ -131,10 +132,9 @@ "type": "subcategory", "label": "Misc", "ids": [ + "operations/management-uis", "operations/deep-storage-migration", - "operations/druid-console", "operations/export-metadata", - "operations/getting-started", "operations/metadata-migration", "operations/segment-optimization", "operations/use_sbt_to_build_fat_jar" From 90735f1ec04a56769f04fb85833b3af2c0e559af Mon Sep 17 00:00:00 2001 From: Maytas Monsereenusorn Date: Tue, 23 Jun 2020 15:54:37 -1000 Subject: [PATCH 106/107] Fix HyperUniquesAggregatorFactory.estimateCardinality null handling to respect output type (#10063) * fix return type from HyperUniquesAggregator/HyperUniquesVectorAggregator * address comments * address comments --- .../HyperUniquesAggregatorFactory.java | 10 +++----- .../druid/query/SchemaEvolutionTest.java | 2 +- .../HyperUniquesAggregatorFactoryTest.java | 25 +++++++++++++++++++ .../timeseries/TimeseriesQueryRunnerTest.java | 4 +-- .../druid/query/topn/TopNQueryRunnerTest.java | 6 ++--- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/processing/src/main/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactory.java b/processing/src/main/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactory.java index ceb82d5247e5..e0b7686ed1f5 100644 --- a/processing/src/main/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactory.java +++ b/processing/src/main/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactory.java @@ -58,17 +58,13 @@ public class HyperUniquesAggregatorFactory extends AggregatorFactory { public static Object estimateCardinality(@Nullable Object object, boolean round) { - if (object == null) { - return 0; - } - final HyperLogLogCollector collector = (HyperLogLogCollector) object; - // Avoid ternary, it causes estimateCardinalityRound to be cast to double. + // Avoid ternary for round check as it causes estimateCardinalityRound to be cast to double. if (round) { - return collector.estimateCardinalityRound(); + return collector == null ? 0L : collector.estimateCardinalityRound(); } else { - return collector.estimateCardinality(); + return collector == null ? 0d : collector.estimateCardinality(); } } diff --git a/processing/src/test/java/org/apache/druid/query/SchemaEvolutionTest.java b/processing/src/test/java/org/apache/druid/query/SchemaEvolutionTest.java index 7e829e00a89a..ce17b38ba46b 100644 --- a/processing/src/test/java/org/apache/druid/query/SchemaEvolutionTest.java +++ b/processing/src/test/java/org/apache/druid/query/SchemaEvolutionTest.java @@ -241,7 +241,7 @@ public void testHyperUniqueEvolutionTimeseries(boolean doVectorize) // index1 has no "uniques" column Assert.assertEquals( - timeseriesResult(ImmutableMap.of("uniques", 0)), + timeseriesResult(ImmutableMap.of("uniques", 0d)), runQuery(query, factory, ImmutableList.of(index1)) ); diff --git a/processing/src/test/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactoryTest.java b/processing/src/test/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactoryTest.java index f9151923413d..421a457999d9 100644 --- a/processing/src/test/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactoryTest.java +++ b/processing/src/test/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesAggregatorFactoryTest.java @@ -30,6 +30,7 @@ import org.junit.Assert; import org.junit.Test; +import java.nio.ByteBuffer; import java.util.Comparator; import java.util.Random; @@ -173,6 +174,30 @@ public void testCompareToShouldBehaveConsistentlyWithEstimatedCardinalitiesEvenI } } + @Test + public void testEstimateCardinalityForZeroCardinality() + { + HyperLogLogCollector emptyHyperLogLogCollector = HyperUniquesBufferAggregator.doGet( + ByteBuffer.allocate(HyperLogLogCollector.getLatestNumBytesForDenseStorage()), + 0 + ); + + Assert.assertEquals(0L, HyperUniquesAggregatorFactory.estimateCardinality(null, true)); + Assert.assertEquals(0d, HyperUniquesAggregatorFactory.estimateCardinality(null, false)); + + Assert.assertEquals(0L, HyperUniquesAggregatorFactory.estimateCardinality(emptyHyperLogLogCollector, true)); + Assert.assertEquals(0d, HyperUniquesAggregatorFactory.estimateCardinality(emptyHyperLogLogCollector, false)); + + Assert.assertEquals( + HyperUniquesAggregatorFactory.estimateCardinality(emptyHyperLogLogCollector, true).getClass(), + HyperUniquesAggregatorFactory.estimateCardinality(null, true).getClass() + ); + Assert.assertEquals( + HyperUniquesAggregatorFactory.estimateCardinality(emptyHyperLogLogCollector, false).getClass(), + HyperUniquesAggregatorFactory.estimateCardinality(null, false).getClass() + ); + } + @Test public void testSerde() throws Exception { diff --git a/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java b/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java index 5d5af015b5c8..99989c01d8b4 100644 --- a/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java +++ b/processing/src/test/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerTest.java @@ -2568,7 +2568,7 @@ public void testTimeseriesWithTimestampResultFieldContextForArrayResponse() ); Assert.assertEquals( 0.0D, - NullHandling.sqlCompatible() ? (Double) result[4] : (Integer) result[4], + (Double) result[4], 0.02 ); Assert.assertEquals( @@ -2581,7 +2581,7 @@ public void testTimeseriesWithTimestampResultFieldContextForArrayResponse() result[3] ); Assert.assertEquals( - (Integer) result[4], + (Double) result[4], 0.0, 0.02 ); diff --git a/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java b/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java index 3f5b41db0a15..f98160a1a390 100644 --- a/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java +++ b/processing/src/test/java/org/apache/druid/query/topn/TopNQueryRunnerTest.java @@ -647,15 +647,15 @@ public void testTopNOverMissingUniques() Arrays.>asList( ImmutableMap.builder() .put("market", "spot") - .put("uniques", 0) + .put("uniques", 0d) .build(), ImmutableMap.builder() .put("market", "total_market") - .put("uniques", 0) + .put("uniques", 0d) .build(), ImmutableMap.builder() .put("market", "upfront") - .put("uniques", 0) + .put("uniques", 0d) .build() ) ) From 5127f81b825387eca2e6df47baa0e369660f45b2 Mon Sep 17 00:00:00 2001 From: Tianxin Zhao Date: Tue, 23 Jun 2020 21:03:45 -0700 Subject: [PATCH 107/107] Remove deprecated metric --- extensions-contrib/prometheus-emitter/pom.xml | 2 +- .../prometheus-emitter/src/main/resources/defaultMetrics.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions-contrib/prometheus-emitter/pom.xml b/extensions-contrib/prometheus-emitter/pom.xml index 6a38f7e9c6c2..ea6864398e5b 100644 --- a/extensions-contrib/prometheus-emitter/pom.xml +++ b/extensions-contrib/prometheus-emitter/pom.xml @@ -23,7 +23,7 @@ druid org.apache.druid - 0.19.0-SNAPSHOT + 0.18.0-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json index 90770d7b4fc9..1c2063d44333 100644 --- a/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json +++ b/extensions-contrib/prometheus-emitter/src/main/resources/defaultMetrics.json @@ -5,7 +5,6 @@ "query/node/ttfb" : { "dimensions" : ["server"], "type" : "timer", "help": "Time to first byte. Seconds elapsed until Broker starts receiving the response from individual historical/realtime processes."}, "query/node/bytes" : { "dimensions" : ["server"], "type" : "count", "help": "Number of bytes returned from querying individual historical/realtime processes."}, "query/node/backpressure": { "dimensions" : ["server"], "type" : "timer", "help": "Seconds that the channel to this process has spent suspended due to backpressure."}, - "query/intervalChunk/time" : { "dimensions" : [], "type" : "timer", "conversionFactor": 1000.0, "help": "Only emitted if interval chunking is enabled. Milliseconds required to query an interval chunk. This metric is deprecated and will be removed in the future because interval chunking is deprecated."}, "query/segment/time" : { "dimensions" : [], "type" : "timer", "conversionFactor": 1000.0, "help": "Seconds taken to query individual segment. Includes time to page in the segment from disk."}, "query/wait/time" : { "dimensions" : [], "type" : "timer", "conversionFactor": 1000.0, "help": "Seconds spent waiting for a segment to be scanned."},

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 GdF4NAYaZ4B diff --git a/docs/assets/tutorial-query-02.png b/docs/assets/tutorial-query-02.png index cf407e3c631eca2a357768857e6ba0fdf6bf52f2..f459bd69961fc624228b9aa8edf4b54ef53261d5 100644 GIT binary patch literal 155423 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