From b7e07f8aa56fe176d85d00395edfbb4c2853f505 Mon Sep 17 00:00:00 2001 From: morningman Date: Mon, 2 Sep 2024 18:07:45 +0800 Subject: [PATCH 1/5] [sf] refactor udf cache --- .../common/classloader/ScannerLoader.java | 9 +- .../doris/common/jni/utils/UdfClassCache.java | 19 +++ .../org/apache/doris/udf/UdfExecutor.java | 119 ++++++++++++++---- fe/fe-common/pom.xml | 4 + 4 files changed, 123 insertions(+), 28 deletions(-) create mode 100644 fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfClassCache.java diff --git a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/classloader/ScannerLoader.java b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/classloader/ScannerLoader.java index 2bbbd21838c9f2..f5a987a3ebb376 100644 --- a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/classloader/ScannerLoader.java +++ b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/classloader/ScannerLoader.java @@ -18,6 +18,7 @@ package org.apache.doris.common.classloader; import org.apache.doris.common.jni.utils.ExpiringMap; +import org.apache.doris.common.jni.utils.UdfClassCache; import com.google.common.collect.Streams; import org.apache.log4j.Logger; @@ -45,7 +46,7 @@ public class ScannerLoader { public static final Logger LOG = Logger.getLogger(ScannerLoader.class); private static final Map> loadedClasses = new HashMap<>(); - private static final ExpiringMap udfLoadedClasses = new ExpiringMap(); + private static final ExpiringMap udfLoadedClasses = new ExpiringMap<>(); private static final String CLASS_SUFFIX = ".class"; private static final String LOAD_PACKAGE = "org.apache.doris"; @@ -65,14 +66,14 @@ public void loadAllScannerJars() { }); } - public static ClassLoader getUdfClassLoader(String functionSignature) { + public static UdfClassCache getUdfClassLoader(String functionSignature) { return udfLoadedClasses.get(functionSignature); } - public static synchronized void cacheClassLoader(String functionSignature, ClassLoader classLoader, + public static synchronized void cacheClassLoader(String functionSignature, UdfClassCache classCache, long expirationTime) { LOG.info("cacheClassLoader for: " + functionSignature); - udfLoadedClasses.put(functionSignature, classLoader, expirationTime * 60 * 1000L); + udfLoadedClasses.put(functionSignature, classCache, expirationTime * 60 * 1000L); } public synchronized void cleanUdfClassLoader(String functionSignature) { diff --git a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfClassCache.java b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfClassCache.java new file mode 100644 index 00000000000000..a2ac1015b0b6ec --- /dev/null +++ b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfClassCache.java @@ -0,0 +1,19 @@ +package org.apache.doris.common.jni.utils; + +import org.apache.doris.thrift.TFunction; + +import com.esotericsoftware.reflectasm.MethodAccess; + +import java.lang.reflect.Method; + +public class UdfClassCache { + public Class c; + public MethodAccess methodAccess; + public int evaluateIndex; + public JavaUdfDataType[] argTypes; + public JavaUdfDataType retType; + public Class[] argClass; + public TFunction fn; + public Method method; + public Method prepareMethod; +} diff --git a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java index ef120b2b913442..51bb1b59bc81f3 100644 --- a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java +++ b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java @@ -20,8 +20,10 @@ import org.apache.doris.catalog.Type; import org.apache.doris.common.Pair; import org.apache.doris.common.classloader.ScannerLoader; +import org.apache.doris.common.exception.InternalException; import org.apache.doris.common.exception.UdfRuntimeException; import org.apache.doris.common.jni.utils.JavaUdfDataType; +import org.apache.doris.common.jni.utils.UdfClassCache; import org.apache.doris.common.jni.utils.UdfUtils; import org.apache.doris.common.jni.vec.ColumnValueConverter; import org.apache.doris.common.jni.vec.VectorTable; @@ -140,26 +142,96 @@ private Method findPrepareMethod(Method[] methods) { return null; // Method not found } - public ClassLoader getClassLoader(String jarPath, String signature, long expirationTime) - throws MalformedURLException, FileNotFoundException { - ClassLoader loader = null; + public UdfClassCache getClassCache(String className, String jarPath, String signature, long expirationTime, + Type funcRetType, Type... parameterTypes) + throws MalformedURLException, FileNotFoundException, ClassNotFoundException, InternalException, + UdfRuntimeException { + UdfClassCache cache = null; if (jarPath == null) { // for test - loader = ClassLoader.getSystemClassLoader(); + // cache = ClassLoader.getSystemClassLoader(); } else { if (isStaticLoad) { - loader = ScannerLoader.getUdfClassLoader(signature); + cache = ScannerLoader.getUdfClassLoader(signature); } - if (loader == null) { + if (cache == null) { ClassLoader parent = getClass().getClassLoader(); classLoader = UdfUtils.getClassLoader(jarPath, parent); - loader = classLoader; + ClassLoader loader = classLoader; + cache = new UdfClassCache(); + cache.c = Class.forName(className, true, loader); + cache.methodAccess = MethodAccess.get(cache.c); + checkUdfClass(className, cache, funcRetType, parameterTypes); if (isStaticLoad) { - ScannerLoader.cacheClassLoader(signature, loader, expirationTime); + ScannerLoader.cacheClassLoader(signature, cache, expirationTime); } } } - return loader; + return cache; + } + + private void checkUdfClass(String className, UdfClassCache cache, Type funcRetType, Type... parameterTypes) + throws InternalException, UdfRuntimeException { + ArrayList signatures = Lists.newArrayList(); + Class c = cache.c; + Method[] methods = c.getMethods(); + Method prepareMethod = findPrepareMethod(methods); + if (prepareMethod != null) { + cache.prepareMethod = prepareMethod; + } + for (Method m : methods) { + // By convention, the udf must contain the function "evaluate" + if (!m.getName().equals(UDF_FUNCTION_NAME)) { + continue; + } + signatures.add(m.toGenericString()); + cache.argClass = m.getParameterTypes(); + + // Try to match the arguments + if (cache.argClass.length != parameterTypes.length) { + continue; + } + cache.method = m; + cache.evaluateIndex = cache.methodAccess.getIndex(UDF_FUNCTION_NAME, cache.argClass); + Pair returnType; + if (cache.argClass.length == 0 && parameterTypes.length == 0) { + // Special case where the UDF doesn't take any input args + returnType = UdfUtils.setReturnType(funcRetType, m.getReturnType()); + if (!returnType.first) { + continue; + } else { + cache.retType = returnType.second; + } + cache.argTypes = new JavaUdfDataType[0]; + return; + } + returnType = UdfUtils.setReturnType(funcRetType, m.getReturnType()); + if (!returnType.first) { + continue; + } else { + cache.retType = returnType.second; + } + Type keyType = cache.retType.getKeyType(); + Type valueType = cache.retType.getValueType(); + Pair inputType = UdfUtils.setArgTypes(parameterTypes, cache.argClass, false); + if (!inputType.first) { + continue; + } else { + cache.argTypes = inputType.second; + } + cache.retType.setKeyType(keyType); + cache.retType.setValueType(valueType); + return; + } + StringBuilder sb = new StringBuilder(); + sb.append("Unable to find evaluate function with the correct signature: ") + .append(className) + .append(".evaluate(") + .append(Joiner.on(", ").join(parameterTypes)) + .append(")\n") + .append("UDF contains: \n ") + .append(Joiner.on("\n ").join(signatures)); + throw new UdfRuntimeException(sb.toString()); } // Preallocate the input objects that will be passed to the underlying UDF. @@ -168,7 +240,6 @@ public ClassLoader getClassLoader(String jarPath, String signature, long expirat protected void init(TJavaUdfExecutorCtorParams request, String jarPath, Type funcRetType, Type... parameterTypes) throws UdfRuntimeException { String className = request.fn.scalar_fn.symbol; - ArrayList signatures = Lists.newArrayList(); try { if (LOG.isDebugEnabled()) { LOG.debug("Loading UDF '" + className + "' from " + jarPath); @@ -178,16 +249,17 @@ protected void init(TJavaUdfExecutorCtorParams request, String jarPath, Type fun if (request.getFn().isSetExpirationTime()) { expirationTime = request.getFn().getExpirationTime(); } - ClassLoader loader = getClassLoader(jarPath, request.getFn().getSignature(), expirationTime); - Class c = Class.forName(className, true, loader); - methodAccess = MethodAccess.get(c); + UdfClassCache cache = getClassCache(className, jarPath, request.getFn().getSignature(), expirationTime, + funcRetType, parameterTypes); + Class c = cache.c; + methodAccess = cache.methodAccess; Constructor ctor = c.getConstructor(); udf = ctor.newInstance(); - Method[] methods = c.getMethods(); - Method prepareMethod = findPrepareMethod(methods); + Method prepareMethod = cache.prepareMethod; if (prepareMethod != null) { prepareMethod.invoke(udf); } +<<<<<<< HEAD for (Method m : methods) { // By convention, the udf must contain the function "evaluate" if (!m.getName().equals(UDF_FUNCTION_NAME)) { @@ -228,16 +300,14 @@ protected void init(TJavaUdfExecutorCtorParams request, String jarPath, Type fun } return; } +======= +>>>>>>> 631326901a ([sf] refactor udf cache) - StringBuilder sb = new StringBuilder(); - sb.append("Unable to find evaluate function with the correct signature: ") - .append(className) - .append(".evaluate(") - .append(Joiner.on(", ").join(parameterTypes)) - .append(")\n") - .append("UDF contains: \n ") - .append(Joiner.on("\n ").join(signatures)); - throw new UdfRuntimeException(sb.toString()); + argClass = cache.argClass; + method = cache.method; + evaluateIndex = cache.evaluateIndex; + retType = cache.retType; + argTypes = cache.argTypes; } catch (MalformedURLException e) { throw new UdfRuntimeException("Unable to load jar.", e); } catch (SecurityException e) { @@ -255,3 +325,4 @@ protected void init(TJavaUdfExecutorCtorParams request, String jarPath, Type fun } } } + diff --git a/fe/fe-common/pom.xml b/fe/fe-common/pom.xml index 458cb6ff9e4064..eb6b673238ed52 100644 --- a/fe/fe-common/pom.xml +++ b/fe/fe-common/pom.xml @@ -132,6 +132,10 @@ under the License. org.apache.logging.log4j log4j-core + + com.esotericsoftware + kryo-shaded + doris-fe-common From e4648d8c0d94389510424a40a6455a7b352c3b44 Mon Sep 17 00:00:00 2001 From: morningman Date: Wed, 4 Sep 2024 23:52:37 +0800 Subject: [PATCH 2/5] 1 --- .../doris/common/jni/utils/UdfClassCache.java | 34 +++++++++-- .../org/apache/doris/udf/UdfExecutor.java | 56 ++----------------- 2 files changed, 34 insertions(+), 56 deletions(-) diff --git a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfClassCache.java b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfClassCache.java index a2ac1015b0b6ec..4515e705214144 100644 --- a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfClassCache.java +++ b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfClassCache.java @@ -1,19 +1,41 @@ -package org.apache.doris.common.jni.utils; +// 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 org.apache.doris.thrift.TFunction; +package org.apache.doris.common.jni.utils; import com.esotericsoftware.reflectasm.MethodAccess; import java.lang.reflect.Method; +/** + * This class is used for caching the class of UDF. + */ public class UdfClassCache { - public Class c; + public Class udfClass; + // the index of evaluate() method in the class public MethodAccess methodAccess; public int evaluateIndex; + // the method of evaluate() in udf + public Method method; + // the method of prepare() in udf + public Method prepareMethod; + // the argument and return's JavaUdfDataType of evaluate() method. public JavaUdfDataType[] argTypes; public JavaUdfDataType retType; + // the class type of the arguments in evaluate() method public Class[] argClass; - public TFunction fn; - public Method method; - public Method prepareMethod; } diff --git a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java index 51bb1b59bc81f3..19a3e125bf8ad5 100644 --- a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java +++ b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java @@ -159,9 +159,9 @@ public UdfClassCache getClassCache(String className, String jarPath, String sign classLoader = UdfUtils.getClassLoader(jarPath, parent); ClassLoader loader = classLoader; cache = new UdfClassCache(); - cache.c = Class.forName(className, true, loader); - cache.methodAccess = MethodAccess.get(cache.c); - checkUdfClass(className, cache, funcRetType, parameterTypes); + cache.udfClass = Class.forName(className, true, loader); + cache.methodAccess = MethodAccess.get(cache.udfClass); + checkAndCacheUdfClass(className, cache, funcRetType, parameterTypes); if (isStaticLoad) { ScannerLoader.cacheClassLoader(signature, cache, expirationTime); } @@ -170,10 +170,10 @@ public UdfClassCache getClassCache(String className, String jarPath, String sign return cache; } - private void checkUdfClass(String className, UdfClassCache cache, Type funcRetType, Type... parameterTypes) + private void checkAndCacheUdfClass(String className, UdfClassCache cache, Type funcRetType, Type... parameterTypes) throws InternalException, UdfRuntimeException { ArrayList signatures = Lists.newArrayList(); - Class c = cache.c; + Class c = cache.udfClass; Method[] methods = c.getMethods(); Method prepareMethod = findPrepareMethod(methods); if (prepareMethod != null) { @@ -251,57 +251,13 @@ protected void init(TJavaUdfExecutorCtorParams request, String jarPath, Type fun } UdfClassCache cache = getClassCache(className, jarPath, request.getFn().getSignature(), expirationTime, funcRetType, parameterTypes); - Class c = cache.c; methodAccess = cache.methodAccess; - Constructor ctor = c.getConstructor(); + Constructor ctor = cache.udfClass.getConstructor(); udf = ctor.newInstance(); Method prepareMethod = cache.prepareMethod; if (prepareMethod != null) { prepareMethod.invoke(udf); } -<<<<<<< HEAD - for (Method m : methods) { - // By convention, the udf must contain the function "evaluate" - if (!m.getName().equals(UDF_FUNCTION_NAME)) { - continue; - } - signatures.add(m.toGenericString()); - argClass = m.getParameterTypes(); - - // Try to match the arguments - if (argClass.length != parameterTypes.length) { - continue; - } - method = m; - evaluateIndex = methodAccess.getIndex(UDF_FUNCTION_NAME, argClass); - Pair returnType; - if (argClass.length == 0 && parameterTypes.length == 0) { - // Special case where the UDF doesn't take any input args - returnType = UdfUtils.setReturnType(funcRetType, m.getReturnType()); - if (!returnType.first) { - continue; - } else { - retType = returnType.second; - } - argTypes = new JavaUdfDataType[0]; - return; - } - returnType = UdfUtils.setReturnType(funcRetType, m.getReturnType()); - if (!returnType.first) { - continue; - } else { - retType = returnType.second; - } - Pair inputType = UdfUtils.setArgTypes(parameterTypes, argClass, false); - if (!inputType.first) { - continue; - } else { - argTypes = inputType.second; - } - return; - } -======= ->>>>>>> 631326901a ([sf] refactor udf cache) argClass = cache.argClass; method = cache.method; From 91ea8fc5a9d137af33fd91ccbbc3388d3320405a Mon Sep 17 00:00:00 2001 From: morningman Date: Thu, 5 Sep 2024 13:58:04 +0800 Subject: [PATCH 3/5] 2 --- .../common/classloader/ScannerLoader.java | 2 +- .../org/apache/doris/udf/UdfExecutor.java | 29 ++++++++----------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/classloader/ScannerLoader.java b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/classloader/ScannerLoader.java index f5a987a3ebb376..0fb9cfd6d126d1 100644 --- a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/classloader/ScannerLoader.java +++ b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/classloader/ScannerLoader.java @@ -72,7 +72,7 @@ public static UdfClassCache getUdfClassLoader(String functionSignature) { public static synchronized void cacheClassLoader(String functionSignature, UdfClassCache classCache, long expirationTime) { - LOG.info("cacheClassLoader for: " + functionSignature); + LOG.info("Cache UDF for: " + functionSignature); udfLoadedClasses.put(functionSignature, classCache, expirationTime * 60 * 1000L); } diff --git a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java index 19a3e125bf8ad5..e4b970c314e7ac 100644 --- a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java +++ b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java @@ -147,24 +147,19 @@ public UdfClassCache getClassCache(String className, String jarPath, String sign throws MalformedURLException, FileNotFoundException, ClassNotFoundException, InternalException, UdfRuntimeException { UdfClassCache cache = null; - if (jarPath == null) { - // for test - // cache = ClassLoader.getSystemClassLoader(); - } else { + if (isStaticLoad) { + cache = ScannerLoader.getUdfClassLoader(signature); + } + if (cache == null) { + ClassLoader parent = getClass().getClassLoader(); + classLoader = UdfUtils.getClassLoader(jarPath, parent); + ClassLoader loader = classLoader; + cache = new UdfClassCache(); + cache.udfClass = Class.forName(className, true, loader); + cache.methodAccess = MethodAccess.get(cache.udfClass); + checkAndCacheUdfClass(className, cache, funcRetType, parameterTypes); if (isStaticLoad) { - cache = ScannerLoader.getUdfClassLoader(signature); - } - if (cache == null) { - ClassLoader parent = getClass().getClassLoader(); - classLoader = UdfUtils.getClassLoader(jarPath, parent); - ClassLoader loader = classLoader; - cache = new UdfClassCache(); - cache.udfClass = Class.forName(className, true, loader); - cache.methodAccess = MethodAccess.get(cache.udfClass); - checkAndCacheUdfClass(className, cache, funcRetType, parameterTypes); - if (isStaticLoad) { - ScannerLoader.cacheClassLoader(signature, cache, expirationTime); - } + ScannerLoader.cacheClassLoader(signature, cache, expirationTime); } } return cache; From 94777ab0a241a462c368011b3f48c0d1f6c9030c Mon Sep 17 00:00:00 2001 From: morningman Date: Thu, 5 Sep 2024 23:07:24 +0800 Subject: [PATCH 4/5] 3 --- .../javaudf/test_javaudf_string.groovy | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/regression-test/suites/nereids_p0/javaudf/test_javaudf_string.groovy b/regression-test/suites/nereids_p0/javaudf/test_javaudf_string.groovy index 4d1ecd4c109638..d88746f14f389f 100644 --- a/regression-test/suites/nereids_p0/javaudf/test_javaudf_string.groovy +++ b/regression-test/suites/nereids_p0/javaudf/test_javaudf_string.groovy @@ -71,6 +71,34 @@ suite("nereids_test_javaudf_string") { qt_select """ SELECT java_udf_string_test(string_col, 2, 3) result FROM ${tableName} ORDER BY result; """ qt_select """ SELECT java_udf_string_test('abcdef', 2, 3), java_udf_string_test('abcdefg', 2, 3) result FROM ${tableName} ORDER BY result; """ + // test multi thread + Thread thread1 = new Thread(() -> { + try { + for (int ii = 0; ii < 100; ii++) { + sql """ SELECT java_udf_string_test(varchar_col, 2, 3) result FROM ${tableName} ORDER BY result; """ + } + } catch (Exception e) { + log.info(e.getMessage()) + Assert.fail(); + } + }) + + Thread thread2 = new Thread(() -> { + try { + for (int ii = 0; ii < 100; ii++) { + sql """ SELECT java_udf_string_test(string_col, 2, 3) result FROM ${tableName} ORDER BY result; """ + } + } catch (Exception e) { + log.info(e.getMessage()) + Assert.fail(); + } + }) + sleep(1000L) + thread1.start() + thread2.start() + + thread1.join() + thread2.join() } finally { try_sql("DROP FUNCTION IF EXISTS java_udf_string_test(string, int, int);") From 8a0d7048d6b260d2f3049908008de06fef735241 Mon Sep 17 00:00:00 2001 From: morningman Date: Tue, 24 Sep 2024 18:36:31 +0800 Subject: [PATCH 5/5] 3 --- .../java/org/apache/doris/udf/UdfExecutor.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java index e4b970c314e7ac..685e20de843869 100644 --- a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java +++ b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java @@ -31,6 +31,7 @@ import com.esotericsoftware.reflectasm.MethodAccess; import com.google.common.base.Joiner; +import com.google.common.base.Strings; import com.google.common.collect.Lists; import org.apache.log4j.Logger; @@ -151,9 +152,17 @@ public UdfClassCache getClassCache(String className, String jarPath, String sign cache = ScannerLoader.getUdfClassLoader(signature); } if (cache == null) { - ClassLoader parent = getClass().getClassLoader(); - classLoader = UdfUtils.getClassLoader(jarPath, parent); - ClassLoader loader = classLoader; + ClassLoader loader; + if (Strings.isNullOrEmpty(jarPath)) { + // if jarPath is empty, which means the UDF jar is located in custom_lib + // and already be loaded when BE start. + // so here we use system class loader to load UDF class. + loader = ClassLoader.getSystemClassLoader(); + } else { + ClassLoader parent = getClass().getClassLoader(); + classLoader = UdfUtils.getClassLoader(jarPath, parent); + loader = classLoader; + } cache = new UdfClassCache(); cache.udfClass = Class.forName(className, true, loader); cache.methodAccess = MethodAccess.get(cache.udfClass); @@ -277,3 +286,4 @@ protected void init(TJavaUdfExecutorCtorParams request, String jarPath, Type fun } } +