diff --git a/docs/operations/api-reference.md b/docs/operations/api-reference.md index c184e8daf971..085a571b456a 100644 --- a/docs/operations/api-reference.md +++ b/docs/operations/api-reference.md @@ -45,7 +45,7 @@ An endpoint that always returns a boolean "true" value with a 200 OK response, u Returns the current configuration properties of the process. -* `/status/selfDiscoveredStatus` +* `/status/selfDiscovered/status` Returns a JSON map of the form `{"selfDiscovered": true/false}`, indicating whether the node has received a confirmation from the central node discovery mechanism (currently ZooKeeper) of the Druid cluster that the node has been added to the @@ -60,7 +60,7 @@ nodes will be discovered by this node timely from this point. * `/status/selfDiscovered` -Similar to `/status/selfDiscoveredStatus`, but returns 200 OK response with empty body if the node has discovered itself +Similar to `/status/selfDiscovered/status`, but returns 200 OK response with empty body if the node has discovered itself and 503 SERVICE UNAVAILABLE if the node hasn't discovered itself yet. This endpoint might be useful because some monitoring checks such as AWS load balancer health checks are not able to look at the response body. diff --git a/server/src/main/java/org/apache/druid/server/http/SelfDiscoveryResource.java b/server/src/main/java/org/apache/druid/server/http/SelfDiscoveryResource.java index 46d04a9006f7..13b7bded496e 100644 --- a/server/src/main/java/org/apache/druid/server/http/SelfDiscoveryResource.java +++ b/server/src/main/java/org/apache/druid/server/http/SelfDiscoveryResource.java @@ -19,6 +19,7 @@ package org.apache.druid.server.http; +import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Singleton; import com.sun.jersey.spi.container.ResourceFilters; @@ -36,6 +37,8 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.Collections; +import java.util.List; +import java.util.Set; import java.util.function.BooleanSupplier; /** @@ -44,58 +47,68 @@ * DI configuration phase. */ @Singleton +@Path("/status/selfDiscovered") public class SelfDiscoveryResource { - private BooleanSupplier selfDiscovered; + private final List selfDiscoveredRoles; @Inject public SelfDiscoveryResource( @Self DruidNode thisDruidNode, - @Self NodeRole thisNodeRole, + @Self Set thisNodeRoles, DruidNodeDiscoveryProvider nodeDiscoveryProvider, Lifecycle lifecycle ) { - Lifecycle.Handler selfDiscoveryListenerRegistrator = new Lifecycle.Handler() - { - @Override - public void start() - { - selfDiscovered = nodeDiscoveryProvider.getForNode(thisDruidNode, thisNodeRole); - } + selfDiscoveredRoles = Lists.newArrayListWithExpectedSize(thisNodeRoles.size()); + thisNodeRoles.forEach( + thisNodeRole -> { + Lifecycle.Handler selfDiscoveryListenerRegistrator = new Lifecycle.Handler() + { + @Override + public void start() + { + selfDiscoveredRoles.add(nodeDiscoveryProvider.getForNode(thisDruidNode, thisNodeRole)); + } - @Override - public void stop() - { - // do nothing - } - }; - // Using Lifecycle.Stage.SERVER because DruidNodeDiscoveryProvider should be already started when - // selfDiscoveryListenerRegistrator.start() is called. - lifecycle.addHandler(selfDiscoveryListenerRegistrator, Lifecycle.Stage.SERVER); + @Override + public void stop() + { + // do nothing + } + }; + // Using Lifecycle.Stage.SERVER because DruidNodeDiscoveryProvider should be already started when + // selfDiscoveryListenerRegistrator.start() is called. + lifecycle.addHandler(selfDiscoveryListenerRegistrator, Lifecycle.Stage.SERVER); + } + ); } /** See the description of this endpoint in api-reference.md. */ @GET - @Path("/status/selfDiscoveredStatus") + @Path("/status") @Produces(MediaType.APPLICATION_JSON) @ResourceFilters(StateResourceFilter.class) public Response getSelfDiscoveredStatus() { - return Response.ok(Collections.singletonMap("selfDiscovered", selfDiscovered.getAsBoolean())).build(); + return Response.ok(Collections.singletonMap("selfDiscovered", isDiscoveredAllRoles())).build(); } /** See the description of this endpoint in api-reference.md. */ @GET - @Path("/status/selfDiscovered") @Produces(MediaType.APPLICATION_JSON) @ResourceFilters(StateResourceFilter.class) public Response getSelfDiscovered() { - if (selfDiscovered.getAsBoolean()) { + if (isDiscoveredAllRoles()) { return Response.ok().build(); } else { return Response.status(HttpStatus.SERVICE_UNAVAILABLE_503).build(); } } + + private boolean isDiscoveredAllRoles() + { + return selfDiscoveredRoles.stream().allMatch(BooleanSupplier::getAsBoolean); + } } diff --git a/server/src/test/java/org/apache/druid/server/http/security/SecurityResourceFilterTest.java b/server/src/test/java/org/apache/druid/server/http/security/SecurityResourceFilterTest.java index 344396c98b3b..e0a5ba13334d 100644 --- a/server/src/test/java/org/apache/druid/server/http/security/SecurityResourceFilterTest.java +++ b/server/src/test/java/org/apache/druid/server/http/security/SecurityResourceFilterTest.java @@ -53,7 +53,7 @@ public class SecurityResourceFilterTest extends ResourceFilterTestHelper { @Parameterized.Parameters(name = "{index}: requestPath={0}, requestMethod={1}, resourceFilter={2}") - public static Collection data() throws NoSuchMethodException + public static Collection data() { return ImmutableList.copyOf( Iterables.concat( @@ -70,8 +70,7 @@ public static Collection data() throws NoSuchMethodException getRequestPathsWithAuthorizer(CoordinatorDynamicConfigsResource.class), getRequestPathsWithAuthorizer(QueryResource.class), getRequestPathsWithAuthorizer(StatusResource.class), - getRequestPathsWithAuthorizer(SelfDiscoveryResource.class.getDeclaredMethod("getSelfDiscoveredStatus")), - getRequestPathsWithAuthorizer(SelfDiscoveryResource.class.getDeclaredMethod("getSelfDiscovered")), + getRequestPathsWithAuthorizer(SelfDiscoveryResource.class), getRequestPathsWithAuthorizer(BrokerQueryResource.class), getRequestPathsWithAuthorizer(RouterResource.class) ) diff --git a/services/src/main/java/org/apache/druid/cli/CliBroker.java b/services/src/main/java/org/apache/druid/cli/CliBroker.java index 45864f4e2e2d..aaf46b95d5d4 100644 --- a/services/src/main/java/org/apache/druid/cli/CliBroker.java +++ b/services/src/main/java/org/apache/druid/cli/CliBroker.java @@ -43,7 +43,6 @@ import org.apache.druid.guice.LifecycleModule; import org.apache.druid.guice.QueryRunnerFactoryModule; import org.apache.druid.guice.QueryableModule; -import org.apache.druid.guice.annotations.Self; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.query.QuerySegmentWalker; import org.apache.druid.query.RetryQueryRunnerConfig; @@ -123,9 +122,7 @@ protected List getModules() LifecycleModule.register(binder, Server.class); - binder.bind(NodeRole.class).annotatedWith(Self.class).toInstance(NodeRole.BROKER); - - bindAnnouncer( + bindNodeRoleAndAnnouncer( binder, DiscoverySideEffectsProvider .builder(NodeRole.BROKER) diff --git a/services/src/main/java/org/apache/druid/cli/CliCoordinator.java b/services/src/main/java/org/apache/druid/cli/CliCoordinator.java index bfe5ef7da48c..6e4c27fbc292 100644 --- a/services/src/main/java/org/apache/druid/cli/CliCoordinator.java +++ b/services/src/main/java/org/apache/druid/cli/CliCoordinator.java @@ -46,7 +46,6 @@ import org.apache.druid.guice.ManageLifecycle; import org.apache.druid.guice.annotations.CoordinatorIndexingServiceHelper; import org.apache.druid.guice.annotations.EscalatedGlobal; -import org.apache.druid.guice.annotations.Self; import org.apache.druid.guice.http.JettyHttpClientModule; import org.apache.druid.java.util.common.concurrent.Execs; import org.apache.druid.java.util.common.concurrent.ExecutorServices; @@ -239,9 +238,7 @@ public void configure(Binder binder) DruidCoordinatorCleanupPendingSegments.class ); - binder.bind(NodeRole.class).annotatedWith(Self.class).toInstance(NodeRole.COORDINATOR); - - bindAnnouncer( + bindNodeRoleAndAnnouncer( binder, Coordinator.class, DiscoverySideEffectsProvider.builder(NodeRole.COORDINATOR).build() diff --git a/services/src/main/java/org/apache/druid/cli/CliHistorical.java b/services/src/main/java/org/apache/druid/cli/CliHistorical.java index c1bc8baadc7d..b700d828d903 100644 --- a/services/src/main/java/org/apache/druid/cli/CliHistorical.java +++ b/services/src/main/java/org/apache/druid/cli/CliHistorical.java @@ -38,7 +38,6 @@ import org.apache.druid.guice.QueryRunnerFactoryModule; import org.apache.druid.guice.QueryableModule; import org.apache.druid.guice.ServerTypeConfig; -import org.apache.druid.guice.annotations.Self; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.query.QuerySegmentWalker; import org.apache.druid.query.lookup.LookupModule; @@ -104,9 +103,7 @@ protected List getModules() JsonConfigProvider.bind(binder, "druid.historical.cache", CacheConfig.class); binder.install(new CacheModule()); - binder.bind(NodeRole.class).annotatedWith(Self.class).toInstance(NodeRole.HISTORICAL); - - bindAnnouncer( + bindNodeRoleAndAnnouncer( binder, DiscoverySideEffectsProvider .builder(NodeRole.HISTORICAL) diff --git a/services/src/main/java/org/apache/druid/cli/CliIndexer.java b/services/src/main/java/org/apache/druid/cli/CliIndexer.java index 6e51c45c065d..5ffd2a83483a 100644 --- a/services/src/main/java/org/apache/druid/cli/CliIndexer.java +++ b/services/src/main/java/org/apache/druid/cli/CliIndexer.java @@ -149,7 +149,7 @@ public void configure(Binder binder) binder.bind(SegmentLoadDropHandler.class).toProvider(Providers.of(null)); - bindAnnouncer( + bindNodeRoleAndAnnouncer( binder, DiscoverySideEffectsProvider .builder(NodeRole.INDEXER) diff --git a/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java b/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java index b6ce582008ec..0eeb5484919d 100644 --- a/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java +++ b/services/src/main/java/org/apache/druid/cli/CliMiddleManager.java @@ -141,9 +141,7 @@ public void configure(Binder binder) LifecycleModule.register(binder, Server.class); - binder.bind(NodeRole.class).annotatedWith(Self.class).toInstance(NodeRole.MIDDLE_MANAGER); - - bindAnnouncer( + bindNodeRoleAndAnnouncer( binder, DiscoverySideEffectsProvider .builder(NodeRole.MIDDLE_MANAGER) diff --git a/services/src/main/java/org/apache/druid/cli/CliOverlord.java b/services/src/main/java/org/apache/druid/cli/CliOverlord.java index 6e00ba9d99a9..f84a5e18ee27 100644 --- a/services/src/main/java/org/apache/druid/cli/CliOverlord.java +++ b/services/src/main/java/org/apache/druid/cli/CliOverlord.java @@ -52,7 +52,6 @@ import org.apache.druid.guice.ManageLifecycle; import org.apache.druid.guice.PolyBind; import org.apache.druid.guice.annotations.Json; -import org.apache.druid.guice.annotations.Self; import org.apache.druid.indexing.common.actions.LocalTaskActionClientFactory; import org.apache.druid.indexing.common.actions.TaskActionClientFactory; import org.apache.druid.indexing.common.actions.TaskActionToolbox; @@ -248,9 +247,7 @@ public void configure(Binder binder) LifecycleModule.register(binder, Server.class); } - binder.bind(NodeRole.class).annotatedWith(Self.class).toInstance(NodeRole.OVERLORD); - - bindAnnouncer( + bindNodeRoleAndAnnouncer( binder, IndexingService.class, DiscoverySideEffectsProvider.builder(NodeRole.OVERLORD).build() diff --git a/services/src/main/java/org/apache/druid/cli/CliRouter.java b/services/src/main/java/org/apache/druid/cli/CliRouter.java index d945222bcbdf..21ab6308ab05 100644 --- a/services/src/main/java/org/apache/druid/cli/CliRouter.java +++ b/services/src/main/java/org/apache/druid/cli/CliRouter.java @@ -109,12 +109,7 @@ protected List getModules() LifecycleModule.register(binder, Server.class); DiscoveryModule.register(binder, Self.class); - binder.bind(NodeRole.class).annotatedWith(Self.class).toInstance(NodeRole.ROUTER); - - bindAnnouncer( - binder, - DiscoverySideEffectsProvider.builder(NodeRole.ROUTER).build() - ); + bindNodeRoleAndAnnouncer(binder, DiscoverySideEffectsProvider.builder(NodeRole.ROUTER).build()); Jerseys.addResource(binder, SelfDiscoveryResource.class); LifecycleModule.registerKey(binder, Key.get(SelfDiscoveryResource.class)); diff --git a/services/src/main/java/org/apache/druid/cli/ServerRunnable.java b/services/src/main/java/org/apache/druid/cli/ServerRunnable.java index 8ac9244d7d62..4beeab8f9fee 100644 --- a/services/src/main/java/org/apache/druid/cli/ServerRunnable.java +++ b/services/src/main/java/org/apache/druid/cli/ServerRunnable.java @@ -26,6 +26,7 @@ import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Provider; +import com.google.inject.multibindings.Multibinder; import org.apache.druid.curator.discovery.ServiceAnnouncer; import org.apache.druid.discovery.DiscoveryDruidNode; import org.apache.druid.discovery.DruidNodeAnnouncer; @@ -42,6 +43,7 @@ import java.util.List; /** + * */ public abstract class ServerRunnable extends GuiceRunnable { @@ -64,7 +66,34 @@ public void run() } } - public static void bindAnnouncer( + public static void bindNodeRoleAndAnnouncer(Binder binder, DiscoverySideEffectsProvider discoverySideEffectsProvider) + { + Multibinder selfBinder = Multibinder.newSetBinder(binder, NodeRole.class, Self.class); + selfBinder.addBinding().toInstance(discoverySideEffectsProvider.nodeRole); + + bindAnnouncer( + binder, + discoverySideEffectsProvider + ); + } + + public static void bindNodeRoleAndAnnouncer( + Binder binder, + Class annotation, + DiscoverySideEffectsProvider discoverySideEffectsProvider + ) + { + Multibinder selfBinder = Multibinder.newSetBinder(binder, NodeRole.class, Self.class); + selfBinder.addBinding().toInstance(discoverySideEffectsProvider.nodeRole); + + bindAnnouncer( + binder, + annotation, + discoverySideEffectsProvider + ); + } + + private static void bindAnnouncer( final Binder binder, final DiscoverySideEffectsProvider provider ) @@ -76,7 +105,7 @@ public static void bindAnnouncer( LifecycleModule.registerKey(binder, Key.get(DiscoverySideEffectsProvider.Child.class)); } - public static void bindAnnouncer( + private static void bindAnnouncer( final Binder binder, final Class annotation, final DiscoverySideEffectsProvider provider