From 63b84190f1d81ff386263bd4942c66a7424612bd Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Fri, 14 Aug 2026 16:30:43 +0530 Subject: [PATCH 1/2] Add configurable virtual thread executor to gRPC service framework --- .../serviceframework/grpc/ExecutorType.java | 6 ++++++ .../grpc/GrpcPlatformServerDefinition.java | 1 + .../grpc/GrpcPlatformServiceContainer.java | 21 +++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/ExecutorType.java diff --git a/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/ExecutorType.java b/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/ExecutorType.java new file mode 100644 index 0000000..624b2e7 --- /dev/null +++ b/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/ExecutorType.java @@ -0,0 +1,6 @@ +package org.hypertrace.core.serviceframework.grpc; + +public enum ExecutorType { + PLATFORM, + VIRTUAL +} diff --git a/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServerDefinition.java b/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServerDefinition.java index 37e8946..f81f14b 100644 --- a/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServerDefinition.java +++ b/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServerDefinition.java @@ -20,6 +20,7 @@ public class GrpcPlatformServerDefinition { @Builder.Default int maxRstPerMinute = 500; @Builder.Default Duration maxConnectionAge = Duration.ZERO; @Builder.Default Duration maxConnectionAgeGrace = Duration.ZERO; + @Builder.Default ExecutorType executorType = ExecutorType.PLATFORM; @Singular Collection serviceFactories; @Singular List serverInterceptors; } diff --git a/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServiceContainer.java b/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServiceContainer.java index 435ea0f..97f7786 100644 --- a/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServiceContainer.java +++ b/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServiceContainer.java @@ -18,6 +18,8 @@ import io.micrometer.core.instrument.binder.grpc.MetricCollectingClientInterceptor; import io.micrometer.core.instrument.binder.grpc.MetricCollectingServerInterceptor; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.ArrayList; @@ -26,6 +28,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -302,9 +305,27 @@ private ServerBuilder initializeBuilder(GrpcPlatformServerDefinition serverDe Status.Code.OK)); serverDefinition.getServerInterceptors().forEach(builder::intercept); + + if (serverDefinition.getExecutorType() == ExecutorType.VIRTUAL) { + log.info("Server [{}] using virtual thread executor", serverDefinition.getName()); + builder.executor(createVirtualThreadExecutor()); + } + return builder; } + private static ExecutorService createVirtualThreadExecutor() { + try { + final Method method = Executors.class.getMethod("newVirtualThreadPerTaskExecutor"); + return (ExecutorService) method.invoke(null); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + log.warn( + "Virtual threads not available (requires JDK 21+), falling back to cached thread pool", + e); + return Executors.newCachedThreadPool(); + } + } + @Value private static class ConstructedServer { From 7f87ca5841194ce80aba98b96a4da4f02762e7e8 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Fri, 14 Aug 2026 22:09:26 +0530 Subject: [PATCH 2/2] Review comments : Accept optional Executor in gRPC server definition --- .../serviceframework/grpc/ExecutorType.java | 6 ------ .../grpc/GrpcPlatformServerDefinition.java | 3 ++- .../grpc/GrpcPlatformServiceContainer.java | 21 +++---------------- 3 files changed, 5 insertions(+), 25 deletions(-) delete mode 100644 platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/ExecutorType.java diff --git a/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/ExecutorType.java b/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/ExecutorType.java deleted file mode 100644 index 624b2e7..0000000 --- a/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/ExecutorType.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.hypertrace.core.serviceframework.grpc; - -public enum ExecutorType { - PLATFORM, - VIRTUAL -} diff --git a/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServerDefinition.java b/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServerDefinition.java index f81f14b..9e3489a 100644 --- a/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServerDefinition.java +++ b/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServerDefinition.java @@ -4,6 +4,7 @@ import java.time.Duration; import java.util.Collection; import java.util.List; +import java.util.concurrent.Executor; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -20,7 +21,7 @@ public class GrpcPlatformServerDefinition { @Builder.Default int maxRstPerMinute = 500; @Builder.Default Duration maxConnectionAge = Duration.ZERO; @Builder.Default Duration maxConnectionAgeGrace = Duration.ZERO; - @Builder.Default ExecutorType executorType = ExecutorType.PLATFORM; + @Builder.Default Executor executor = null; @Singular Collection serviceFactories; @Singular List serverInterceptors; } diff --git a/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServiceContainer.java b/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServiceContainer.java index 97f7786..ad2e789 100644 --- a/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServiceContainer.java +++ b/platform-grpc-service-framework/src/main/java/org/hypertrace/core/serviceframework/grpc/GrpcPlatformServiceContainer.java @@ -18,8 +18,6 @@ import io.micrometer.core.instrument.binder.grpc.MetricCollectingClientInterceptor; import io.micrometer.core.instrument.binder.grpc.MetricCollectingServerInterceptor; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.ArrayList; @@ -28,7 +26,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -306,26 +303,14 @@ private ServerBuilder initializeBuilder(GrpcPlatformServerDefinition serverDe serverDefinition.getServerInterceptors().forEach(builder::intercept); - if (serverDefinition.getExecutorType() == ExecutorType.VIRTUAL) { - log.info("Server [{}] using virtual thread executor", serverDefinition.getName()); - builder.executor(createVirtualThreadExecutor()); + if (serverDefinition.getExecutor() != null) { + log.info("Server [{}] using custom executor", serverDefinition.getName()); + builder.executor(serverDefinition.getExecutor()); } return builder; } - private static ExecutorService createVirtualThreadExecutor() { - try { - final Method method = Executors.class.getMethod("newVirtualThreadPerTaskExecutor"); - return (ExecutorService) method.invoke(null); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - log.warn( - "Virtual threads not available (requires JDK 21+), falling back to cached thread pool", - e); - return Executors.newCachedThreadPool(); - } - } - @Value private static class ConstructedServer {