gortiz commented on code in PR #13921: URL: https://github.com/apache/pinot/pull/13921#discussion_r1740828771
########## pinot-spi/src/main/java/org/apache/pinot/spi/executor/ExecutorServiceUtils.java: ########## @@ -0,0 +1,133 @@ +/** + * 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.pinot.spi.executor; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import org.apache.pinot.spi.env.PinotConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * A utility class to create {@link ExecutorService} instances. + * + * In order to create a new executor, the {@code create} methods should be called. + * These methods take an executor type as an argument. + * + * Pinot includes two executor service plugins: + * <ul> + * <li>{@code cached}: creates a new cached thread pool</li> + * <li>{@code fixed}: creates a new fixed thread pool.</li> + * </ul> + * + * The cached executor service plugin is the default provider. It creates a new cached thread pool, which is the + * recommended executor service for cases where the tasks are short-lived and not CPU bound. + * If that is not the case, this executor may create a large number of threads that will be competing for CPU resources, + * which may lead to performance degradation and even system instability. + * + * The fixed executor service plugin creates a new fixed thread pool. The number of threads is defined in the + * configuration. This executor service is recommended for cases where the tasks are long-lived or CPU bound, + * but it may need changes to the code to avoid deadlocks. + * + * Other plugins can be added by implementing the {@link ExecutorServicePlugin} interface and declaring them as services + * in the classpath. Although it is not included in the Pinot distribution, a custom plugin can be added to create a + * virtual thread executor service, which in cases where threads are not pinned to a specific CPU core, can provide + * the same safety as the cached executor service, but without competing for CPU resources when the tasks are CPU bound. + * + * @see ServiceLoader + */ +public class ExecutorServiceUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(ExecutorServiceUtils.class); + private static final long DEFAULT_TERMINATION_MILLIS = 30_000; + + private static final String DEFAULT_PROVIDER; + private static final Map<String, ExecutorServiceProvider> PROVIDERS; + + static { + PROVIDERS = new HashMap<>(); + for (ExecutorServicePlugin plugin : ServiceLoader.load(ExecutorServicePlugin.class)) { + ExecutorServiceProvider provider = plugin.provider(); + ExecutorServiceProvider old = PROVIDERS.put(plugin.id(), provider); + if (old != null) { + LOGGER.warn("Duplicate provider for id '{}': {} and {}", plugin.id(), old, provider); + } + } + if (PROVIDERS.isEmpty()) { + throw new IllegalStateException("No executor service providers found"); + } else if (PROVIDERS.containsKey("cached")) { + DEFAULT_PROVIDER = "cached"; Review Comment: Having to move the default to QueryRunner is worse because in the future we expect to have more places using this API, which would end up having to provide the default everywhere. Anyway that can be done right now. We provide two create methods, one that includes a default type and one that does not. The later use the default calculated here. Is this default calculated in a unnecessary complex way? Totally. That was my bad. I've just picked the code from https://github.com/apache/pinot/pull/13619 and simplify it a bit instead of starting from scratch. Why the code in https://github.com/apache/pinot/pull/13619 is more complex? Because there I wanted to use virtual threads by default when using Java 21. I'm going to simplify the code because the idea of using virtual threads by default may not be the best idea ever given we are not sure if we are actually pinning the carrier somewhere or not. The ideal solution in the future would be to have an init() method in this class so we can decide which executor use by default depending on the configuration we receive at startup time. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org For additional commands, e-mail: commits-h...@pinot.apache.org