This is an automated email from the ASF dual-hosted git repository. jackie pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git
The following commit(s) were added to refs/heads/master by this push: new 5390698 Fix the failure caused by Reflections in FunctionRegistry (#5531) 5390698 is described below commit 53906986e376e7b454f1d23599794b07683293b0 Author: Xiaotian (Jackie) Jiang <17555551+jackie-ji...@users.noreply.github.com> AuthorDate: Tue Jun 9 19:07:53 2020 -0700 Fix the failure caused by Reflections in FunctionRegistry (#5531) The new FunctionRegistry change of using reflection to plug in methods (introduced in #5440) is causing failure in query compilation. The problem is caused by Reflections library not being thread-safe when multiple threads are accessing the same jar file. We have multiple libraries (jersey, swagger) using reflection to load classes. The FunctionRegistry uses reflection in the static block, which happens when the class is loaded. If the first query arrives (first usage of FunctionRegistry which runs the static block) during the setup of the jersey server, Reflections will throw exception. Using reflection in the static block can also cause the first query to block on reflection scanning the classes, which can potentially take seconds. Read more about the thread-safety issue here: ronmamo/reflections#81 Users are reporting the same issue even with the latest Reflections version 0.9.12. A common solution is to downgrade Reflections to 0.9.9, but we have other dependencies rely on 0.9.11, so downgrading might cause other issues. The solution here is to introduce a no-op init() method to trigger the static block so that we can control when to scan the files to avoid the thread-safety issue as well as blocking the first query. Another benefit of using an init() method is that the exception won't be swallowed (the exception in static block will be swallowed and query engine will start getting ClassNotFoundException) This PR also introduces a convention for the plugin methods using reflection, where the class must includes ".function." in its class path. This can significantly reduce the time of class scanning (reduced from 4 seconds to 200 ms locally) --- .../broker/broker/helix/HelixBrokerStarter.java | 8 +- .../pinot/common/function/FunctionRegistry.java | 94 ++++++++++++---------- .../apache/pinot/sql/parsers/CalciteSqlParser.java | 31 +++---- .../apache/pinot/controller/ControllerStarter.java | 4 + .../data/function/InbuiltFunctionEvaluator.java | 8 +- .../function/TransformFunctionFactory.java | 40 ++++----- .../function/InbuiltFunctionEvaluatorTest.java | 42 +++++----- .../core/data/function/InbuiltFunctionsTest.java | 15 ++-- .../pinot/server/starter/ServerInstance.java | 3 + 9 files changed, 127 insertions(+), 118 deletions(-) diff --git a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/HelixBrokerStarter.java b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/HelixBrokerStarter.java index 50acf54..96681b2 100644 --- a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/HelixBrokerStarter.java +++ b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/HelixBrokerStarter.java @@ -49,6 +49,7 @@ import org.apache.pinot.broker.requesthandler.BrokerRequestHandler; import org.apache.pinot.broker.requesthandler.SingleConnectionBrokerRequestHandler; import org.apache.pinot.broker.routing.RoutingManager; import org.apache.pinot.common.Utils; +import org.apache.pinot.common.function.FunctionRegistry; import org.apache.pinot.common.metadata.ZKMetadataProvider; import org.apache.pinot.common.metrics.BrokerMeter; import org.apache.pinot.common.metrics.BrokerMetrics; @@ -210,6 +211,8 @@ public class HelixBrokerStarter implements ServiceStartable { HelixExternalViewBasedQueryQuotaManager queryQuotaManager = new HelixExternalViewBasedQueryQuotaManager(_brokerMetrics); queryQuotaManager.init(_spectatorHelixManager); + // Initialize FunctionRegistry before starting the broker request handler + FunctionRegistry.init(); _brokerRequestHandler = new SingleConnectionBrokerRequestHandler(_brokerConf, _routingManager, _accessControlFactory, queryQuotaManager, _brokerMetrics, _propertyStore); @@ -291,8 +294,8 @@ public class HelixBrokerStarter implements ServiceStartable { Broker.DEFAULT_BROKER_MIN_RESOURCE_PERCENT_FOR_START); LOGGER.info("Registering service status handler"); - ServiceStatus.setServiceStatusCallback(_brokerId, new ServiceStatus.MultipleCallbackServiceStatusCallback(ImmutableList - .of(new ServiceStatus.IdealStateAndCurrentStateMatchServiceStatusCallback(_participantHelixManager, + ServiceStatus.setServiceStatusCallback(_brokerId, new ServiceStatus.MultipleCallbackServiceStatusCallback( + ImmutableList.of(new ServiceStatus.IdealStateAndCurrentStateMatchServiceStatusCallback(_participantHelixManager, _clusterName, _brokerId, resourcesToMonitor, minResourcePercentForStartup), new ServiceStatus.IdealStateAndExternalViewMatchServiceStatusCallback(_participantHelixManager, _clusterName, _brokerId, resourcesToMonitor, minResourcePercentForStartup)))); @@ -345,7 +348,6 @@ public class HelixBrokerStarter implements ServiceStartable { LOGGER.info("Shutdown Broker Metrics Registry"); _metricsRegistry.shutdown(); LOGGER.info("Finish shutting down Pinot broker for {}", _brokerId); - } public HelixManager getSpectatorHelixManager() { diff --git a/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionRegistry.java b/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionRegistry.java index 479a67d..c8b26cf 100644 --- a/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionRegistry.java +++ b/pinot-common/src/main/java/org/apache/pinot/common/function/FunctionRegistry.java @@ -18,18 +18,17 @@ */ package org.apache.pinot.common.function; -import com.google.common.base.Preconditions; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Set; +import javax.annotation.Nullable; import org.apache.pinot.common.function.annotations.ScalarFunction; import org.reflections.Reflections; import org.reflections.scanners.MethodAnnotationsScanner; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; +import org.reflections.util.FilterBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,63 +37,72 @@ import org.slf4j.LoggerFactory; * Registry for in-built Pinot functions */ public class FunctionRegistry { + private FunctionRegistry() { + } + private static final Logger LOGGER = LoggerFactory.getLogger(FunctionRegistry.class); - private static final Map<String, FunctionInfo> _functionInfoMap = new HashMap<>(); + private static final Map<String, FunctionInfo> FUNCTION_INFO_MAP = new HashMap<>(); /** - * Given a function name, asserts that a corresponding function was registered during construction and returns it + * Registers the scalar functions via reflection. + * NOTE: In order to plugin methods using reflection, the methods should be inside a class that includes ".function." + * in its class path. This convention can significantly reduce the time of class scanning. */ - public static FunctionInfo getFunctionByName(String functionName) { - Preconditions.checkArgument(_functionInfoMap.containsKey(functionName.toLowerCase())); - return _functionInfoMap.get(functionName.toLowerCase()); + static { + long startTimeMs = System.currentTimeMillis(); + Reflections reflections = new Reflections( + new ConfigurationBuilder().setUrls(ClasspathHelper.forPackage("org.apache.pinot")) + .filterInputsBy(new FilterBuilder.Include(".*\\.function\\..*")) + .setScanners(new MethodAnnotationsScanner())); + Set<Method> methodSet = reflections.getMethodsAnnotatedWith(ScalarFunction.class); + for (Method method : methodSet) { + ScalarFunction scalarFunction = method.getAnnotation(ScalarFunction.class); + if (scalarFunction.enabled()) { + if (!scalarFunction.name().isEmpty()) { + FunctionRegistry.registerFunction(scalarFunction.name(), method); + } else { + FunctionRegistry.registerFunction(method); + } + } + } + LOGGER.info("Initialized FunctionRegistry with {} functions: {} in {}ms", FUNCTION_INFO_MAP.size(), + FUNCTION_INFO_MAP.keySet(), System.currentTimeMillis() - startTimeMs); } /** - * Given a function name and a set of argument types, asserts that a corresponding function - * was registered during construction and returns it + * Initializes the FunctionRegistry. + * NOTE: This method itself is a NO-OP, but can be used to explicitly trigger the static block of registering the + * scalar functions via reflection. */ - public static FunctionInfo getFunctionByNameWithApplicableArgumentTypes(String functionName, - Class<?>[] argumentTypes) { - FunctionInfo functionInfo = getFunctionByName(functionName); - Preconditions.checkArgument(functionInfo.isApplicable(argumentTypes)); - return functionInfo; + public static void init() { } + /** + * Registers a method with the name of the method. + */ public static void registerFunction(Method method) { - registerFunction(method.getName().toLowerCase(), method); + registerFunction(method.getName(), method); } - public static void registerFunction(String name, Method method) { + /** + * Registers a method with the given function name. + */ + public static void registerFunction(String functionName, Method method) { FunctionInfo functionInfo = new FunctionInfo(method, method.getDeclaringClass()); - _functionInfoMap.put(name, functionInfo); + FUNCTION_INFO_MAP.put(canonicalize(functionName), functionInfo); } - public static boolean containsFunctionByName(String funcName) { - return _functionInfoMap.containsKey(funcName.toLowerCase()); + /** + * Returns the {@link FunctionInfo} associated with the given function name, or {@code null} if there is no method + * registered under the name. This method should be called after the FunctionRegistry is initialized and all methods + * are already registered. + */ + @Nullable + public static FunctionInfo getFunctionByName(String functionName) { + return FUNCTION_INFO_MAP.get(canonicalize(functionName)); } - static { - try { - - Reflections reflections = new Reflections( - new ConfigurationBuilder().setUrls(ClasspathHelper.forPackage("org.apache.pinot")) - .setScanners(new MethodAnnotationsScanner())); - - Set<Method> methodSet = reflections.getMethodsAnnotatedWith(ScalarFunction.class); - for (Method method : methodSet) { - ScalarFunction scalarFunction = method.getAnnotation(ScalarFunction.class); - if (scalarFunction.enabled()) { - if (!scalarFunction.name().isEmpty()) { - FunctionRegistry.registerFunction(scalarFunction.name(), method); - } else { - FunctionRegistry.registerFunction(method); - } - } - } - - } catch (Exception e) { - LOGGER.error("Caught exception when registering function", e); - throw new IllegalStateException(e); - } + private static String canonicalize(String functionName) { + return functionName.toLowerCase(); } } diff --git a/pinot-common/src/main/java/org/apache/pinot/sql/parsers/CalciteSqlParser.java b/pinot-common/src/main/java/org/apache/pinot/sql/parsers/CalciteSqlParser.java index 2aec8ec..de05274 100644 --- a/pinot-common/src/main/java/org/apache/pinot/sql/parsers/CalciteSqlParser.java +++ b/pinot-common/src/main/java/org/apache/pinot/sql/parsers/CalciteSqlParser.java @@ -628,8 +628,7 @@ public class CalciteSqlParser { funcName = funcSqlNode.getOperator().getName(); } if (funcName.equalsIgnoreCase(SqlKind.COUNT.toString()) && (funcSqlNode.getFunctionQuantifier() != null) - && funcSqlNode.getFunctionQuantifier().toValue() - .equalsIgnoreCase(AggregationFunctionType.DISTINCT.getName())) { + && funcSqlNode.getFunctionQuantifier().toValue().equalsIgnoreCase(AggregationFunctionType.DISTINCT.getName())) { funcName = AggregationFunctionType.DISTINCTCOUNT.getName(); } return funcName; @@ -667,21 +666,23 @@ public class CalciteSqlParser { function.getOperands().set(i, operand); } String funcName = function.getOperator(); - if (FunctionRegistry.containsFunctionByName(funcName) && compilable) { + if (compilable) { FunctionInfo functionInfo = FunctionRegistry.getFunctionByName(funcName); - Object[] arguments = new Object[functionOperandsLength]; - for (int i = 0; i < functionOperandsLength; i++) { - arguments[i] = function.getOperands().get(i).getLiteral().getFieldValue(); - } - try { - FunctionInvoker invoker = new FunctionInvoker(functionInfo); - Object result = invoker.process(arguments); - if (result instanceof String) { - result = String.format("'%s'", result); + if (functionInfo != null) { + Object[] arguments = new Object[functionOperandsLength]; + for (int i = 0; i < functionOperandsLength; i++) { + arguments[i] = function.getOperands().get(i).getLiteral().getFieldValue(); + } + try { + FunctionInvoker invoker = new FunctionInvoker(functionInfo); + Object result = invoker.process(arguments); + if (result instanceof String) { + result = String.format("'%s'", result); + } + return RequestUtils.getLiteralExpression(result); + } catch (Exception e) { + throw new SqlCompilationException(new IllegalArgumentException("Unsupported function - " + funcName, e)); } - return RequestUtils.getLiteralExpression(result); - } catch (Exception e) { - throw new SqlCompilationException(new IllegalArgumentException("Unsupported function - " + funcName, e)); } } return funcExpr; diff --git a/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerStarter.java b/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerStarter.java index 75e766e..1eab79a 100644 --- a/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerStarter.java +++ b/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerStarter.java @@ -44,6 +44,7 @@ import org.apache.helix.api.listeners.ControllerChangeListener; import org.apache.helix.model.MasterSlaveSMD; import org.apache.helix.task.TaskDriver; import org.apache.pinot.common.Utils; +import org.apache.pinot.common.function.FunctionRegistry; import org.apache.pinot.common.metrics.ControllerMeter; import org.apache.pinot.common.metrics.ControllerMetrics; import org.apache.pinot.common.metrics.MetricsHelper; @@ -151,6 +152,9 @@ public class ControllerStarter implements ServiceStartable { _helixResourceManager = null; _executorService = null; } else { + // Initialize FunctionRegistry before starting the admin application (PinotQueryResource requires it to compile + // queries) + FunctionRegistry.init(); _adminApp = new ControllerAdminApiApplication(_config.getQueryConsoleWebappPath(), _config.getQueryConsoleUseHttps()); // Do not use this before the invocation of {@link PinotHelixResourceManager::start()}, which happens in {@link ControllerStarter::start()} diff --git a/pinot-core/src/main/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluator.java b/pinot-core/src/main/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluator.java index e49d6c4..e427355 100644 --- a/pinot-core/src/main/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluator.java +++ b/pinot-core/src/main/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluator.java @@ -20,6 +20,7 @@ package org.apache.pinot.core.data.function; import com.google.common.base.Preconditions; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.apache.commons.lang3.math.NumberUtils; import org.apache.pinot.common.function.FunctionInfo; @@ -86,8 +87,9 @@ public class InbuiltFunctionEvaluator implements FunctionEvaluator { argumentTypes[i] = childNode.getReturnType(); } - FunctionInfo functionInfo = - FunctionRegistry.getFunctionByNameWithApplicableArgumentTypes(transformName, argumentTypes); + FunctionInfo functionInfo = FunctionRegistry.getFunctionByName(transformName); + Preconditions.checkState(functionInfo != null && functionInfo.isApplicable(argumentTypes), + "Failed to find function of name: %s with argument types: %s", transformName, Arrays.toString(argumentTypes)); return new FunctionExecutionNode(functionInfo, childNodes); } @@ -114,8 +116,6 @@ public class InbuiltFunctionEvaluator implements FunctionEvaluator { public FunctionExecutionNode(FunctionInfo functionInfo, ExecutableNode[] argumentProviders) throws Exception { - Preconditions.checkNotNull(functionInfo); - Preconditions.checkNotNull(argumentProviders); _functionInvoker = new FunctionInvoker(functionInfo); _argumentProviders = argumentProviders; _argInputs = new Object[_argumentProviders.length]; diff --git a/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TransformFunctionFactory.java b/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TransformFunctionFactory.java index 02b41bc..7084c61 100644 --- a/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TransformFunctionFactory.java +++ b/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TransformFunctionFactory.java @@ -111,30 +111,30 @@ public class TransformFunctionFactory { * @return Transform function */ public static TransformFunction get(TransformExpressionTree expression, Map<String, DataSource> dataSourceMap) { - TransformFunction transformFunction; switch (expression.getExpressionType()) { case FUNCTION: + TransformFunction transformFunction; String functionName = expression.getValue(); - Class<? extends TransformFunction> transformFunctionClass; - FunctionInfo functionInfo = null; - if (FunctionRegistry.containsFunctionByName(functionName)) { - transformFunctionClass = ScalarTransformFunctionWrapper.class; - functionInfo = FunctionRegistry.getFunctionByName(functionName); + Class<? extends TransformFunction> transformFunctionClass = TRANSFORM_FUNCTION_MAP.get(functionName); + if (transformFunctionClass != null) { + // Transform function + try { + transformFunction = transformFunctionClass.newInstance(); + } catch (Exception e) { + throw new RuntimeException("Caught exception while constructing transform function: " + functionName, e); + } } else { - transformFunctionClass = TRANSFORM_FUNCTION_MAP.get(functionName); - } - - if (transformFunctionClass == null) { - throw new BadQueryRequestException("Unsupported transform function: " + functionName); - } - try { - if (functionInfo != null) { + // Scalar function + FunctionInfo functionInfo = FunctionRegistry.getFunctionByName(functionName); + if (functionInfo == null) { + throw new BadQueryRequestException("Unsupported transform function: " + functionName); + } + try { transformFunction = new ScalarTransformFunctionWrapper(functionName, functionInfo); - } else { - transformFunction = transformFunctionClass.newInstance(); + } catch (Exception e) { + throw new RuntimeException("Caught exception while constructing scalar transform function: " + functionName, + e); } - } catch (Exception e) { - throw new RuntimeException("Caught exception while instantiating transform function: " + functionName, e); } List<TransformExpressionTree> children = expression.getChildren(); List<TransformFunction> arguments = new ArrayList<>(children.size()); @@ -144,8 +144,8 @@ public class TransformFunctionFactory { try { transformFunction.init(arguments, dataSourceMap); } catch (Exception e) { - throw new BadQueryRequestException( - "Caught exception while initializing transform function: " + transformFunction.getName(), e); + throw new BadQueryRequestException("Caught exception while initializing transform function: " + functionName, + e); } return transformFunction; case IDENTIFIER: diff --git a/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluatorTest.java b/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluatorTest.java index b89f1e2..e5f34b4 100644 --- a/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluatorTest.java +++ b/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionEvaluatorTest.java @@ -19,7 +19,6 @@ package org.apache.pinot.core.data.function; import com.google.common.collect.Lists; -import org.apache.pinot.common.function.FunctionInfo; import org.apache.pinot.common.function.FunctionRegistry; import org.apache.pinot.spi.data.readers.GenericRow; import org.joda.time.DateTime; @@ -37,11 +36,7 @@ public class InbuiltFunctionEvaluatorTest { throws Exception { MyFunc myFunc = new MyFunc(); FunctionRegistry.registerFunction(myFunc.getClass().getDeclaredMethod("reverseString", String.class)); - FunctionInfo functionInfo = FunctionRegistry - .getFunctionByNameWithApplicableArgumentTypes("reverseString", new Class<?>[]{Object.class}); - System.out.println(functionInfo); String expression = "reverseString(testColumn)"; - InbuiltFunctionEvaluator evaluator = new InbuiltFunctionEvaluator(expression); Assert.assertEquals(evaluator.getArguments(), Lists.newArrayList("testColumn")); GenericRow row = new GenericRow(); @@ -91,8 +86,7 @@ public class InbuiltFunctionEvaluatorTest { public void testStateSharedBetweenRowsForExecution() throws Exception { MyFunc myFunc = new MyFunc(); - FunctionRegistry - .registerFunction(myFunc.getClass().getDeclaredMethod("appendToStringAndReturn", String.class)); + FunctionRegistry.registerFunction(myFunc.getClass().getDeclaredMethod("appendToStringAndReturn", String.class)); String expression = String.format("appendToStringAndReturn('%s')", "test "); InbuiltFunctionEvaluator evaluator = new InbuiltFunctionEvaluator(expression); Assert.assertTrue(evaluator.getArguments().isEmpty()); @@ -101,28 +95,28 @@ public class InbuiltFunctionEvaluatorTest { Assert.assertEquals(evaluator.evaluate(row), "test test "); Assert.assertEquals(evaluator.evaluate(row), "test test test "); } -} -class MyFunc { - String reverseString(String input) { - return new StringBuilder(input).reverse().toString(); - } + private static class MyFunc { + String reverseString(String input) { + return new StringBuilder(input).reverse().toString(); + } - MutableDateTime EPOCH_START = new MutableDateTime(); + MutableDateTime EPOCH_START = new MutableDateTime(); - public MyFunc() { - EPOCH_START.setDate(0L); - } + public MyFunc() { + EPOCH_START.setDate(0L); + } - int daysSinceEpoch(String input, String format) { - DateTime dateTime = DateTimeFormat.forPattern(format).parseDateTime(input); - return Days.daysBetween(EPOCH_START, dateTime).getDays(); - } + int daysSinceEpoch(String input, String format) { + DateTime dateTime = DateTimeFormat.forPattern(format).parseDateTime(input); + return Days.daysBetween(EPOCH_START, dateTime).getDays(); + } - private String baseString = ""; + private String baseString = ""; - String appendToStringAndReturn(String addedString) { - baseString += addedString; - return baseString; + String appendToStringAndReturn(String addedString) { + baseString += addedString; + return baseString; + } } } diff --git a/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionsTest.java b/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionsTest.java index 9532ea8..a156089 100644 --- a/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionsTest.java +++ b/pinot-core/src/test/java/org/apache/pinot/core/data/function/InbuiltFunctionsTest.java @@ -230,8 +230,7 @@ public class InbuiltFunctionsTest { GenericRow row1 = new GenericRow(); jsonStr = "{\"k3\":{\"sub1\":10,\"sub2\":1.0},\"k4\":\"baz\",\"k5\":[1,2,3]}"; - row1.putValue("jsonMap", JsonUtils - .stringToObject(jsonStr, Map.class)); + row1.putValue("jsonMap", JsonUtils.stringToObject(jsonStr, Map.class)); inputs.add(new Object[]{"toJsonMapStr(jsonMap)", Lists.newArrayList("jsonMap"), row1, jsonStr}); GenericRow row2 = new GenericRow(); @@ -241,20 +240,18 @@ public class InbuiltFunctionsTest { GenericRow row3 = new GenericRow(); jsonStr = "{\"k3\":{\"sub1\":10,\"sub2\":1.0},\"k4\":\"baz\",\"k5\":[1,2,3]}"; - row3.putValue("jsonMap", JsonUtils - .stringToObject(jsonStr, Map.class)); + row3.putValue("jsonMap", JsonUtils.stringToObject(jsonStr, Map.class)); inputs.add(new Object[]{"json_format(jsonMap)", Lists.newArrayList("jsonMap"), row3, jsonStr}); GenericRow row4 = new GenericRow(); jsonStr = "[{\"one\":1,\"two\":\"too\"},{\"one\":11,\"two\":\"roo\"}]"; - row4.putValue("jsonMap", JsonUtils - .stringToObject(jsonStr, List.class)); + row4.putValue("jsonMap", JsonUtils.stringToObject(jsonStr, List.class)); inputs.add(new Object[]{"json_format(jsonMap)", Lists.newArrayList("jsonMap"), row4, jsonStr}); GenericRow row5 = new GenericRow(); - jsonStr = "[{\"one\":1,\"two\":{\"sub1\":1.1,\"sub2\":1.2},\"three\":[\"a\",\"b\"]},{\"one\":11,\"two\":{\"sub1\":11.1,\"sub2\":11.2},\"three\":[\"aa\",\"bb\"]}]"; - row5.putValue("jsonMap", JsonUtils - .stringToObject(jsonStr, List.class)); + jsonStr = + "[{\"one\":1,\"two\":{\"sub1\":1.1,\"sub2\":1.2},\"three\":[\"a\",\"b\"]},{\"one\":11,\"two\":{\"sub1\":11.1,\"sub2\":11.2},\"three\":[\"aa\",\"bb\"]}]"; + row5.putValue("jsonMap", JsonUtils.stringToObject(jsonStr, List.class)); inputs.add(new Object[]{"json_format(jsonMap)", Lists.newArrayList("jsonMap"), row5, jsonStr}); return inputs.toArray(new Object[0][]); diff --git a/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java b/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java index 7a11728..ae448ad 100644 --- a/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java +++ b/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerInstance.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.LongAccumulator; import org.apache.helix.HelixManager; +import org.apache.pinot.common.function.FunctionRegistry; import org.apache.pinot.common.metrics.MetricsHelper; import org.apache.pinot.common.metrics.ServerMetrics; import org.apache.pinot.core.data.manager.InstanceDataManager; @@ -71,6 +72,8 @@ public class ServerInstance { _instanceDataManager = (InstanceDataManager) Class.forName(instanceDataManagerClassName).newInstance(); _instanceDataManager.init(serverConf.getInstanceDataManagerConfig(), helixManager, _serverMetrics); + // Initialize FunctionRegistry before starting the query executor + FunctionRegistry.init(); String queryExecutorClassName = serverConf.getQueryExecutorClassName(); LOGGER.info("Initializing query executor of class: {}", queryExecutorClassName); _queryExecutor = (QueryExecutor) Class.forName(queryExecutorClassName).newInstance(); --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org For additional commands, e-mail: commits-h...@pinot.apache.org