You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by ma...@apache.org on 2021/08/24 07:30:55 UTC

[druid] branch master updated: Add sleep function for testing (#11626)

This is an automated email from the ASF dual-hosted git repository.

maytasm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 78b4be4  Add sleep function for testing (#11626)
78b4be4 is described below

commit 78b4be467eb5c93dc63e171b9ed758140c40b84b
Author: Jihoon Son <ji...@apache.org>
AuthorDate: Tue Aug 24 00:30:31 2021 -0700

    Add sleep function for testing (#11626)
    
    * Add sleep function for testing
    
    * sql function
    
    * javadoc
---
 .../java/org/apache/druid/math/expr/Function.java  | 55 +++++++++++++++++++
 .../org/apache/druid/math/expr/FunctionTest.java   | 31 ++++++++++-
 .../builtin/SleepOperatorConversion.java           | 62 ++++++++++++++++++++++
 .../sql/calcite/planner/DruidOperatorTable.java    |  2 +
 .../apache/druid/sql/calcite/CalciteQueryTest.java | 30 +++++++++++
 5 files changed, 179 insertions(+), 1 deletion(-)

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 9e4a241..cd677b2 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
@@ -19,6 +19,7 @@
 
 package org.apache.druid.math.expr;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableSet;
 import org.apache.druid.common.config.NullHandling;
 import org.apache.druid.java.util.common.DateTimes;
@@ -27,6 +28,8 @@ import org.apache.druid.java.util.common.IAE;
 import org.apache.druid.java.util.common.RE;
 import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.java.util.common.UOE;
+import org.apache.druid.math.expr.Expr.InputBindingInspector;
+import org.apache.druid.math.expr.Expr.ObjectBinding;
 import org.apache.druid.math.expr.vector.CastToTypeVectorProcessor;
 import org.apache.druid.math.expr.vector.ExprVectorProcessor;
 import org.apache.druid.math.expr.vector.VectorMathProcessors;
@@ -3639,4 +3642,56 @@ public interface Function
       return HumanReadableBytes.UnitSystem.DECIMAL;
     }
   }
+
+  /**
+   * This function makes the current thread sleep for the given amount of seconds.
+   * Fractional-second delays can be specified.
+   *
+   * This function is applied per row. The actual query time can vary depending on how much parallelism is used
+   * for the query. As it does not provide consistent sleep time, this function should be used only for testing
+   * when you want to keep a certain query running during the test.
+   */
+  @VisibleForTesting
+  class Sleep implements Function
+  {
+    @Override
+    public String name()
+    {
+      return "sleep";
+    }
+
+    @Override
+    public ExprEval apply(List<Expr> args, ObjectBinding bindings)
+    {
+      ExprEval eval = args.get(0).eval(bindings);
+      try {
+        if (!eval.isNumericNull()) {
+          double seconds = eval.asDouble();
+          if (seconds > 0) {
+            Thread.sleep((long) (seconds * 1000));
+          }
+        }
+        return ExprEval.of(null);
+      }
+      catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+        throw new RuntimeException(e);
+      }
+    }
+
+    @Override
+    public void validateArguments(List<Expr> args)
+    {
+      if (args.size() != 1) {
+        throw new IAE("Function[%s] needs 1 argument", name());
+      }
+    }
+
+    @Nullable
+    @Override
+    public ExprType getOutputType(InputBindingInspector inspector, List<Expr> args)
+    {
+      return ExprType.STRING;
+    }
+  }
 }
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 43ff070..99d4753 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
@@ -783,6 +783,36 @@ public class FunctionTest extends InitializedNullHandlingTest
     assertExpr("repeat(nonexistent, 10)", null);
   }
 
+  @Test
+  public void testSleep()
+  {
+    assertExpr("sleep(1)", null);
+    assertExpr("sleep(0.5)", null);
+    assertExpr("sleep(null)", null);
+    assertExpr("sleep(0)", null);
+    assertExpr("sleep(-1)", null);
+
+    assertTimeElapsed("sleep(1)", 1000);
+    assertTimeElapsed("sleep(0.5)", 500);
+    assertTimeElapsed("sleep(null)", 0);
+    assertTimeElapsed("sleep(0)", 0);
+    assertTimeElapsed("sleep(-1)", 0);
+  }
+
+  private void assertTimeElapsed(String expression, long expectedTimeElapsedMs)
+  {
+    final long detla = 50;
+    final long before = System.currentTimeMillis();
+    final Expr expr = Parser.parse(expression, ExprMacroTable.nil());
+    expr.eval(bindings).value();
+    final long after = System.currentTimeMillis();
+    final long elapsed = after - before;
+    Assert.assertTrue(
+        StringUtils.format("Expected [%s], but actual elapsed was [%s]", expectedTimeElapsedMs, elapsed),
+        elapsed >= expectedTimeElapsedMs
+        && elapsed < expectedTimeElapsedMs + detla
+    );
+  }
 
   private void assertExpr(final String expression, @Nullable final Object expectedResult)
   {
@@ -793,7 +823,6 @@ public class FunctionTest extends InitializedNullHandlingTest
     final Expr roundTrip = Parser.parse(exprNoFlatten.stringify(), ExprMacroTable.nil());
     Assert.assertEquals(expr.stringify(), expectedResult, roundTrip.eval(bindings).value());
 
-
     final Expr roundTripFlatten = Parser.parse(expr.stringify(), ExprMacroTable.nil());
     Assert.assertEquals(expr.stringify(), expectedResult, roundTripFlatten.eval(bindings).value());
 
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/SleepOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/SleepOperatorConversion.java
new file mode 100644
index 0000000..de9d186
--- /dev/null
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/SleepOperatorConversion.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.sql.calcite.expression.builtin;
+
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.SqlFunction;
+import org.apache.calcite.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.druid.segment.column.RowSignature;
+import org.apache.druid.sql.calcite.expression.DruidExpression;
+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 javax.annotation.Nullable;
+
+/**
+ * A SQL operator conversion for the {@link org.apache.druid.math.expr.Function.Sleep} expression.
+ * The expression is currently evaluated during the query planning when the given argument is a number literal.
+ */
+public class SleepOperatorConversion implements SqlOperatorConversion
+{
+  private static final SqlFunction SQL_FUNCTION = OperatorConversions
+      .operatorBuilder("SLEEP")
+      .operandTypes(SqlTypeFamily.NUMERIC)
+      .requiredOperands(1)
+      .returnTypeNullable(SqlTypeName.VARCHAR) // always null
+      .functionCategory(SqlFunctionCategory.TIMEDATE)
+      .build();
+
+  @Override
+  public SqlOperator calciteOperator()
+  {
+    return SQL_FUNCTION;
+  }
+
+  @Nullable
+  @Override
+  public DruidExpression toDruidExpression(PlannerContext plannerContext, RowSignature rowSignature, RexNode rexNode)
+  {
+    return OperatorConversions.convertCall(plannerContext, rowSignature, rexNode, "sleep");
+  }
+}
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 0f52c34..40f7a58 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
@@ -97,6 +97,7 @@ 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;
 import org.apache.druid.sql.calcite.expression.builtin.RoundOperatorConversion;
+import org.apache.druid.sql.calcite.expression.builtin.SleepOperatorConversion;
 import org.apache.druid.sql.calcite.expression.builtin.StringFormatOperatorConversion;
 import org.apache.druid.sql.calcite.expression.builtin.StringToArrayOperatorConversion;
 import org.apache.druid.sql.calcite.expression.builtin.StrposOperatorConversion;
@@ -168,6 +169,7 @@ public class DruidOperatorTable implements SqlOperatorTable
                    .add(new TimeParseOperatorConversion())
                    .add(new TimeShiftOperatorConversion())
                    .add(new TimestampToMillisOperatorConversion())
+                   .add(new SleepOperatorConversion())
                    .build();
 
   private static final List<SqlOperatorConversion> STRING_OPERATOR_CONVERSIONS =
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 3afbbb7..102e2b2 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
@@ -18858,4 +18858,34 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
         ImmutableList.of()
     );
   }
+
+  @Test
+  public void testSleepFunction() throws Exception
+  {
+    testQuery(
+        "SELECT sleep(m1) from foo where m1 < 2.0",
+        ImmutableList.of(
+            Druids.newScanQueryBuilder()
+                  .dataSource(new TableDataSource("foo"))
+                  .intervals(querySegmentSpec(Filtration.eternity()))
+                  .virtualColumns(
+                      new ExpressionVirtualColumn(
+                          "v0",
+                          "sleep(\"m1\")",
+                          ValueType.STRING,
+                          ExprMacroTable.nil()
+                      )
+                  )
+                  .columns("v0")
+                  .filters(new BoundDimFilter("m1", null, "2.0", null, true, null, null, StringComparators.NUMERIC))
+                  .resultFormat(ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
+                  .legacy(false)
+                  .context(QUERY_CONTEXT_DEFAULT)
+                  .build()
+        ),
+        ImmutableList.of(
+            new Object[]{NullHandling.replaceWithDefault() ? "" : null}
+        )
+    );
+  }
 }

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org