This is an automated email from the ASF dual-hosted git repository.
siddteotia pushed a commit to branch release-0.11.0
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/release-0.11.0 by this push:
new dcedf480af Fix the race condition of reflection scanning classes
(#9167) (#9175)
dcedf480af is described below
commit dcedf480af9fb63fb66b07bdbef9a099c653dbdd
Author: Rong Rong <[email protected]>
AuthorDate: Fri Aug 5 10:38:04 2022 -0700
Fix the race condition of reflection scanning classes (#9167) (#9175)
Co-authored-by: Xiaotian (Jackie) Jiang
<[email protected]>
---
.../broker/broker/BrokerAdminApiApplication.java | 4 +-
.../pinot/common/function/FunctionRegistry.java | 14 +---
.../api/ControllerAdminApiApplication.java | 15 ++--
.../controller/tuner/TableConfigTunerRegistry.java | 27 ++-----
.../pinot/minion/MinionAdminApiApplication.java | 4 +-
.../spi/loader/SegmentDirectoryLoaderRegistry.java | 20 ++---
.../server/starter/helix/AdminApiApplication.java | 12 ++-
.../pinot/spi/utils/PinotReflectionUtils.java | 91 +++++++++++++++++++---
.../PinotServiceManagerAdminApiApplication.java | 4 +-
9 files changed, 109 insertions(+), 82 deletions(-)
diff --git
a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/BrokerAdminApiApplication.java
b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/BrokerAdminApiApplication.java
index cfc5d55928..e98420a112 100644
---
a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/BrokerAdminApiApplication.java
+++
b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/BrokerAdminApiApplication.java
@@ -80,9 +80,7 @@ public class BrokerAdminApiApplication extends ResourceConfig
{
} catch (IOException e) {
throw new RuntimeException("Failed to start http server", e);
}
- synchronized (PinotReflectionUtils.getReflectionLock()) {
- setupSwagger();
- }
+ PinotReflectionUtils.runWithLock(this::setupSwagger);
}
private void setupSwagger() {
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 db8864aed7..7633e2391c 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
@@ -27,11 +27,7 @@ import java.util.Set;
import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.apache.pinot.spi.annotations.ScalarFunction;
-import org.reflections.Reflections;
-import org.reflections.scanners.MethodAnnotationsScanner;
-import org.reflections.util.ClasspathHelper;
-import org.reflections.util.ConfigurationBuilder;
-import org.reflections.util.FilterBuilder;
+import org.apache.pinot.spi.utils.PinotReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,12 +50,8 @@ public class FunctionRegistry {
*/
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) {
+ Set<Method> methods =
PinotReflectionUtils.getMethodsThroughReflection(".*\\.function\\..*",
ScalarFunction.class);
+ for (Method method : methods) {
if (!Modifier.isPublic(method.getModifiers())) {
continue;
}
diff --git
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java
index fa95790ad9..7946bbcd44 100644
---
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java
+++
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/ControllerAdminApiApplication.java
@@ -40,12 +40,9 @@ import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
public class ControllerAdminApiApplication extends ResourceConfig {
- private static final Logger LOGGER =
LoggerFactory.getLogger(ControllerAdminApiApplication.class);
public static final String PINOT_CONFIGURATION = "pinotConfiguration";
private final String _controllerResourcePackages;
@@ -86,9 +83,7 @@ public class ControllerAdminApiApplication extends
ResourceConfig {
} catch (IOException e) {
throw new RuntimeException("Failed to start http server", e);
}
- synchronized (PinotReflectionUtils.getReflectionLock()) {
- setupSwagger(_httpServer);
- }
+ PinotReflectionUtils.runWithLock(this::setupSwagger);
ClassLoader classLoader =
ControllerAdminApiApplication.class.getClassLoader();
@@ -105,7 +100,7 @@ public class ControllerAdminApiApplication extends
ResourceConfig {
_httpServer.getServerConfiguration().addHttpHandler(new
CLStaticHttpHandler(classLoader, "/webapp/js/"), "/js/");
}
- private void setupSwagger(HttpServer httpServer) {
+ private void setupSwagger() {
BeanConfig beanConfig = new BeanConfig();
beanConfig.setTitle("Pinot Controller API");
beanConfig.setDescription("APIs for accessing Pinot Controller
information");
@@ -123,12 +118,12 @@ public class ControllerAdminApiApplication extends
ResourceConfig {
ClassLoader loader = this.getClass().getClassLoader();
CLStaticHttpHandler apiStaticHttpHandler = new CLStaticHttpHandler(loader,
"/api/");
// map both /api and /help to swagger docs. /api because it looks nice.
/help for backward compatibility
- httpServer.getServerConfiguration().addHttpHandler(apiStaticHttpHandler,
"/api/");
- httpServer.getServerConfiguration().addHttpHandler(apiStaticHttpHandler,
"/help/");
+ _httpServer.getServerConfiguration().addHttpHandler(apiStaticHttpHandler,
"/api/");
+ _httpServer.getServerConfiguration().addHttpHandler(apiStaticHttpHandler,
"/help/");
URL swaggerDistLocation =
loader.getResource("META-INF/resources/webjars/swagger-ui/3.23.11/");
CLStaticHttpHandler swaggerDist = new CLStaticHttpHandler(new
URLClassLoader(new URL[]{swaggerDistLocation}));
- httpServer.getServerConfiguration().addHttpHandler(swaggerDist,
"/swaggerui-dist/");
+ _httpServer.getServerConfiguration().addHttpHandler(swaggerDist,
"/swaggerui-dist/");
}
public void stop() {
diff --git
a/pinot-controller/src/main/java/org/apache/pinot/controller/tuner/TableConfigTunerRegistry.java
b/pinot-controller/src/main/java/org/apache/pinot/controller/tuner/TableConfigTunerRegistry.java
index 2913e72d12..173069846b 100644
---
a/pinot-controller/src/main/java/org/apache/pinot/controller/tuner/TableConfigTunerRegistry.java
+++
b/pinot-controller/src/main/java/org/apache/pinot/controller/tuner/TableConfigTunerRegistry.java
@@ -19,25 +19,17 @@
package org.apache.pinot.controller.tuner;
import com.google.common.base.Preconditions;
-import java.net.URL;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import org.reflections.Reflections;
-import org.reflections.scanners.ResourcesScanner;
-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.apache.pinot.spi.utils.PinotReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Helper class to dynamically register all annotated {@link Tuner} methods
+ * Helper class to dynamically register all annotated {@link Tuner} classes.
*/
public class TableConfigTunerRegistry {
private TableConfigTunerRegistry() {
@@ -63,16 +55,9 @@ public class TableConfigTunerRegistry {
}
long startTime = System.currentTimeMillis();
- List<URL> urls = new ArrayList<>();
- for (String pack : packages) {
- urls.addAll(ClasspathHelper.forPackage(pack));
- }
-
- Reflections reflections = new Reflections(
- new ConfigurationBuilder().setUrls(urls).filterInputsBy(new
FilterBuilder.Include(".*\\.tuner\\..*"))
- .setScanners(new ResourcesScanner(), new TypeAnnotationsScanner(),
new SubTypesScanner()));
- Set<Class<?>> classes = reflections.getTypesAnnotatedWith(Tuner.class);
- classes.forEach(tunerClass -> {
+ Set<Class<?>> tunerClasses =
+ PinotReflectionUtils.getClassesThroughReflection(packages,
".*\\.tuner\\..*", Tuner.class);
+ for (Class<?> tunerClass : tunerClasses) {
Tuner tunerAnnotation = tunerClass.getAnnotation(Tuner.class);
if (tunerAnnotation.enabled()) {
if (tunerAnnotation.name().isEmpty()) {
@@ -88,7 +73,7 @@ public class TableConfigTunerRegistry {
}
}
}
- });
+ }
_init = true;
LOGGER.info("Initialized TableConfigTunerRegistry with {} tuners: {} in {}
ms", CONFIG_TUNER_MAP.size(),
diff --git
a/pinot-minion/src/main/java/org/apache/pinot/minion/MinionAdminApiApplication.java
b/pinot-minion/src/main/java/org/apache/pinot/minion/MinionAdminApiApplication.java
index 71f1ee6320..dc9c467502 100644
---
a/pinot-minion/src/main/java/org/apache/pinot/minion/MinionAdminApiApplication.java
+++
b/pinot-minion/src/main/java/org/apache/pinot/minion/MinionAdminApiApplication.java
@@ -74,9 +74,7 @@ public class MinionAdminApiApplication extends ResourceConfig
{
} catch (IOException e) {
throw new RuntimeException("Failed to start http server", e);
}
- synchronized (PinotReflectionUtils.getReflectionLock()) {
- setupSwagger();
- }
+ PinotReflectionUtils.runWithLock(this::setupSwagger);
}
private void setupSwagger() {
diff --git
a/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/loader/SegmentDirectoryLoaderRegistry.java
b/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/loader/SegmentDirectoryLoaderRegistry.java
index 407ef32419..0e43826fd0 100644
---
a/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/loader/SegmentDirectoryLoaderRegistry.java
+++
b/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/loader/SegmentDirectoryLoaderRegistry.java
@@ -21,13 +21,7 @@ package org.apache.pinot.segment.spi.loader;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import org.reflections.Reflections;
-import org.reflections.scanners.ResourcesScanner;
-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.apache.pinot.spi.utils.PinotReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,12 +40,10 @@ public class SegmentDirectoryLoaderRegistry {
static {
long startTime = System.currentTimeMillis();
- Reflections reflections = new Reflections(
- new
ConfigurationBuilder().setUrls(ClasspathHelper.forPackage("org.apache.pinot.segment"))
- .filterInputsBy(new FilterBuilder.Include(".*\\.loader\\..*"))
- .setScanners(new ResourcesScanner(), new TypeAnnotationsScanner(),
new SubTypesScanner()));
- Set<Class<?>> classes =
reflections.getTypesAnnotatedWith(SegmentLoader.class);
- classes.forEach(loaderClass -> {
+ Set<Class<?>> loaderClasses =
+
PinotReflectionUtils.getClassesThroughReflection("org.apache.pinot.segment",
".*\\.loader\\..*",
+ SegmentLoader.class);
+ for (Class<?> loaderClass : loaderClasses) {
SegmentLoader segmentLoaderAnnotation =
loaderClass.getAnnotation(SegmentLoader.class);
if (segmentLoaderAnnotation.enabled()) {
if (segmentLoaderAnnotation.name().isEmpty()) {
@@ -69,7 +61,7 @@ public class SegmentDirectoryLoaderRegistry {
}
}
}
- });
+ }
LOGGER.info("Initialized SegmentDirectoryLoaderRegistry with {}
segmentDirectoryLoaders: {} in {} ms",
SEGMENT_DIRECTORY_LOADER_MAP.size(),
SEGMENT_DIRECTORY_LOADER_MAP.keySet(),
diff --git
a/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/AdminApiApplication.java
b/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/AdminApiApplication.java
index edcd944c0c..0f1a3ddba7 100644
---
a/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/AdminApiApplication.java
+++
b/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/AdminApiApplication.java
@@ -101,15 +101,13 @@ public class AdminApiApplication extends ResourceConfig {
if
(pinotConfiguration.getProperty(CommonConstants.Server.CONFIG_OF_SWAGGER_SERVER_ENABLED,
CommonConstants.Server.DEFAULT_SWAGGER_SERVER_ENABLED)) {
LOGGER.info("Starting swagger for the Pinot server.");
- synchronized (PinotReflectionUtils.getReflectionLock()) {
- setupSwagger(_httpServer, pinotConfiguration);
- }
+ PinotReflectionUtils.runWithLock(() -> setupSwagger(pinotConfiguration));
}
_started = true;
return true;
}
- private void setupSwagger(HttpServer httpServer, PinotConfiguration
pinotConfiguration) {
+ private void setupSwagger(PinotConfiguration pinotConfiguration) {
BeanConfig beanConfig = new BeanConfig();
beanConfig.setTitle("Pinot Server API");
beanConfig.setDescription("APIs for accessing Pinot server information");
@@ -132,13 +130,13 @@ public class AdminApiApplication extends ResourceConfig {
CLStaticHttpHandler staticHttpHandler =
new CLStaticHttpHandler(AdminApiApplication.class.getClassLoader(),
"/api/");
// map both /api and /help to swagger docs. /api because it looks nice.
/help for backward compatibility
- httpServer.getServerConfiguration().addHttpHandler(staticHttpHandler,
"/api/");
- httpServer.getServerConfiguration().addHttpHandler(staticHttpHandler,
"/help/");
+ _httpServer.getServerConfiguration().addHttpHandler(staticHttpHandler,
"/api/");
+ _httpServer.getServerConfiguration().addHttpHandler(staticHttpHandler,
"/help/");
URL swaggerDistLocation =
AdminApiApplication.class.getClassLoader().getResource("META-INF/resources/webjars/swagger-ui/3.23.11/");
CLStaticHttpHandler swaggerDist = new CLStaticHttpHandler(new
URLClassLoader(new URL[]{swaggerDistLocation}));
- httpServer.getServerConfiguration().addHttpHandler(swaggerDist,
"/swaggerui-dist/");
+ _httpServer.getServerConfiguration().addHttpHandler(swaggerDist,
"/swaggerui-dist/");
}
public void stop() {
diff --git
a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/PinotReflectionUtils.java
b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/PinotReflectionUtils.java
index 20ee074320..bcc55cd530 100644
---
a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/PinotReflectionUtils.java
+++
b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/PinotReflectionUtils.java
@@ -19,28 +19,98 @@
package org.apache.pinot.spi.utils;
import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
import org.reflections.Reflections;
-import org.reflections.scanners.TypeAnnotationsScanner;
+import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class PinotReflectionUtils {
private PinotReflectionUtils() {
}
- private static final String PINOT_PACKAGE_PREFIX = "org.apache.pinot";
+ private static final Logger LOGGER =
LoggerFactory.getLogger(PinotReflectionUtils.class);
+ private static final String PINOT_PACKAGE_NAME = "org.apache.pinot";
+
+ // We use a lock to prevent multiple threads accessing the same jar in the
same time which can cause exception
+ // See https://github.com/ronmamo/reflections/issues/81 for more details
private static final Object REFLECTION_LOCK = new Object();
- public static Set<Class<?>> getClassesThroughReflection(final String
regexPattern,
- final Class<? extends Annotation> annotation) {
- synchronized (getReflectionLock()) {
- Reflections reflections = new Reflections(
- new
ConfigurationBuilder().setUrls(ClasspathHelper.forPackage(PINOT_PACKAGE_PREFIX))
- .filterInputsBy(new
FilterBuilder.Include(regexPattern)).setScanners(new TypeAnnotationsScanner()));
- return reflections.getTypesAnnotatedWith(annotation, true);
+ public static Set<Class<?>> getClassesThroughReflection(String regexPattern,
Class<? extends Annotation> annotation) {
+ return getClassesThroughReflection(PINOT_PACKAGE_NAME, regexPattern,
annotation);
+ }
+
+ public static Set<Class<?>> getClassesThroughReflection(String packageName,
String regexPattern,
+ Class<? extends Annotation> annotation) {
+ try {
+ synchronized (REFLECTION_LOCK) {
+ return new Reflections(new
ConfigurationBuilder().setUrls(ClasspathHelper.forPackage(packageName))
+ .filterInputsBy(new
FilterBuilder.Include(regexPattern))).getTypesAnnotatedWith(annotation);
+ }
+ } catch (Throwable t) {
+ // Log an error then re-throw it because this method is usually called
in a static block, where exception might
+ // not be properly handled
+ LOGGER.error("Error scanning classes within package: '{}' with regex
pattern: '{}', annotation: {}", packageName,
+ regexPattern, annotation.getSimpleName(), t);
+ throw t;
+ }
+ }
+
+ public static Set<Class<?>> getClassesThroughReflection(List<String>
packages, String regexPattern,
+ Class<? extends Annotation> annotation) {
+ try {
+ synchronized (REFLECTION_LOCK) {
+ List<URL> urls = new ArrayList<>();
+ for (String packageName : packages) {
+ urls.addAll(ClasspathHelper.forPackage(packageName));
+ }
+ return new Reflections(new ConfigurationBuilder().setUrls(urls)
+ .filterInputsBy(new
FilterBuilder.Include(regexPattern))).getTypesAnnotatedWith(annotation);
+ }
+ } catch (Throwable t) {
+ // Log an error then re-throw it because this method is usually called
in a static block, where exception might
+ // not be properly handled
+ LOGGER.error("Error scanning classes within packages: {} with regex
pattern: '{}', annotation: {}", packages,
+ regexPattern, annotation.getSimpleName(), t);
+ throw t;
+ }
+ }
+
+ public static Set<Method> getMethodsThroughReflection(String regexPattern,
Class<? extends Annotation> annotation) {
+ return getMethodsThroughReflection(PINOT_PACKAGE_NAME, regexPattern,
annotation);
+ }
+
+ public static Set<Method> getMethodsThroughReflection(String packageName,
String regexPattern,
+ Class<? extends Annotation> annotation) {
+ try {
+ synchronized (REFLECTION_LOCK) {
+ return new Reflections(new
ConfigurationBuilder().setUrls(ClasspathHelper.forPackage(packageName))
+ .filterInputsBy(new FilterBuilder.Include(regexPattern))
+ .setScanners(new
MethodAnnotationsScanner())).getMethodsAnnotatedWith(annotation);
+ }
+ } catch (Throwable t) {
+ // Log an error then re-throw it because this method is usually called
in a static block, where exception might
+ // not be properly handled
+ LOGGER.error("Error scanning methods within package: '{}' with regex
pattern: '{}', annotation: {}", packageName,
+ regexPattern, annotation.getSimpleName(), t);
+ throw t;
+ }
+ }
+
+ /**
+ * Executes the given runnable within the reflection lock.
+ */
+ public static void runWithLock(Runnable runnable) {
+ synchronized (REFLECTION_LOCK) {
+ runnable.run();
}
}
@@ -48,8 +118,9 @@ public class PinotReflectionUtils {
* Due to the multi-threading issue in org.reflections.vfs.ZipDir, we need
to put a lock before calling the
* reflection related methods.
*
- * @return
+ * Deprecated: use {@link #runWithLock(Runnable)} instead
*/
+ @Deprecated
public static Object getReflectionLock() {
return REFLECTION_LOCK;
}
diff --git
a/pinot-tools/src/main/java/org/apache/pinot/tools/service/PinotServiceManagerAdminApiApplication.java
b/pinot-tools/src/main/java/org/apache/pinot/tools/service/PinotServiceManagerAdminApiApplication.java
index c765187754..734cd3d55b 100644
---
a/pinot-tools/src/main/java/org/apache/pinot/tools/service/PinotServiceManagerAdminApiApplication.java
+++
b/pinot-tools/src/main/java/org/apache/pinot/tools/service/PinotServiceManagerAdminApiApplication.java
@@ -58,9 +58,7 @@ public class PinotServiceManagerAdminApiApplication extends
ResourceConfig {
Preconditions.checkArgument(httpPort > 0);
_baseUri = URI.create("http://0.0.0.0:" + httpPort + "/");
_httpServer = GrizzlyHttpServerFactory.createHttpServer(_baseUri, this);
- synchronized (PinotReflectionUtils.getReflectionLock()) {
- setupSwagger();
- }
+ PinotReflectionUtils.runWithLock(this::setupSwagger);
}
private void setupSwagger() {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]