This is an automated email from the ASF dual-hosted git repository.

ankitsultana pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new f3bef4caad0 [federation] Add warnings for unavailable remote clusters 
in BrokerResponse for multi-cluster routing (#17510)
f3bef4caad0 is described below

commit f3bef4caad0f426502a06ada6365f285070aeca2
Author: Shaurya Chaturvedi <[email protected]>
AuthorDate: Wed Jan 14 18:17:24 2026 -0800

    [federation] Add warnings for unavailable remote clusters in BrokerResponse 
for multi-cluster routing (#17510)
---
 .../broker/broker/helix/BaseBrokerStarter.java     |   7 +-
 .../helix/MultiClusterHelixBrokerStarter.java      |  19 +++-
 .../BaseSingleStageBrokerRequestHandler.java       |   9 ++
 .../MultiStageBrokerRequestHandler.java            |   9 ++
 .../core/routing/MultiClusterRoutingContext.java   |  31 ++++++-
 .../multicluster/MultiClusterIntegrationTest.java  | 103 +++++++++++++++++++--
 .../apache/pinot/spi/exception/QueryErrorCode.java |   1 +
 7 files changed, 163 insertions(+), 16 deletions(-)

diff --git 
a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java
 
b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java
index 8ab0498d5a7..6a5369dccfd 100644
--- 
a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java
+++ 
b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java
@@ -309,9 +309,6 @@ public abstract class BaseBrokerStarter implements 
ServiceStartable {
     _isStarting = true;
     Utils.logVersions();
 
-    LOGGER.info("Connecting spectator Helix manager");
-    initSpectatorHelixManager();
-
     LOGGER.info("Setting up broker request handler");
     // Set up metric registry and broker metrics
     _metricsRegistry = 
PinotMetricUtils.getPinotMetricsRegistry(_brokerConf.subset(Broker.METRICS_CONFIG_PREFIX));
@@ -328,6 +325,10 @@ public abstract class BaseBrokerStarter implements 
ServiceStartable {
         _brokerConf.getProperty(Broker.AdaptiveServerSelector.CONFIG_OF_TYPE,
             Broker.AdaptiveServerSelector.DEFAULT_TYPE), 1);
     BrokerMetrics.register(_brokerMetrics);
+
+    LOGGER.info("Connecting spectator Helix manager");
+    initSpectatorHelixManager();
+
     // Set up request handling classes
     _serverRoutingStatsManager = new ServerRoutingStatsManager(_brokerConf, 
_brokerMetrics);
     _serverRoutingStatsManager.init();
diff --git 
a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/MultiClusterHelixBrokerStarter.java
 
b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/MultiClusterHelixBrokerStarter.java
index a19454fec9b..b4ae2ba211b 100644
--- 
a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/MultiClusterHelixBrokerStarter.java
+++ 
b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/MultiClusterHelixBrokerStarter.java
@@ -22,8 +22,10 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import org.apache.helix.HelixConstants.ChangeType;
 import org.apache.helix.HelixManager;
 import org.apache.helix.HelixManagerFactory;
@@ -66,6 +68,9 @@ public class MultiClusterHelixBrokerStarter extends 
BaseBrokerStarter {
   protected MultiClusterRoutingContext _multiClusterRoutingContext;
   protected Map<String, ClusterChangeMediator> _remoteClusterChangeMediator;
 
+  // Tracks clusters that failed to connect (for adding warnings to query 
responses)
+  protected Set<String> _unavailableClusters;
+
   public MultiClusterHelixBrokerStarter() {
   }
 
@@ -121,6 +126,7 @@ public class MultiClusterHelixBrokerStarter extends 
BaseBrokerStarter {
   }
 
   private void initRemoteClusterSpectatorHelixManagers() throws Exception {
+    _unavailableClusters = new HashSet<>();
     if (_remoteZkServers == null || _remoteZkServers.isEmpty()) {
       LOGGER.info("[multi-cluster] No remote ZK servers configured - skipping 
spectator Helix manager init");
       return;
@@ -141,6 +147,7 @@ public class MultiClusterHelixBrokerStarter extends 
BaseBrokerStarter {
         LOGGER.info("[multi-cluster] Connected to remote cluster '{}' at ZK: 
{}", clusterName, zkServers);
       } catch (Exception e) {
         LOGGER.error("[multi-cluster] Failed to connect to cluster '{}' at ZK: 
{}", clusterName, zkServers, e);
+        _unavailableClusters.add(clusterName);
         
_brokerMetrics.addMeteredGlobalValue(BrokerMeter.MULTI_CLUSTER_BROKER_STARTUP_FAILURE,
 1);
       }
     }
@@ -152,6 +159,10 @@ public class MultiClusterHelixBrokerStarter extends 
BaseBrokerStarter {
       LOGGER.info("[multi-cluster] Connected to {}/{} remote clusters: {}", 
_remoteSpectatorHelixManager.size(),
         _remoteZkServers.size(), _remoteSpectatorHelixManager.keySet());
     }
+    if (!_unavailableClusters.isEmpty()) {
+      LOGGER.warn("[multi-cluster] The following clusters are unavailable and 
will generate warnings "
+          + "in query responses: {}", _unavailableClusters);
+    }
   }
 
   protected void stopRemoteClusterComponents() {
@@ -271,9 +282,11 @@ public class MultiClusterHelixBrokerStarter extends 
BaseBrokerStarter {
     }
 
     _multiClusterRoutingContext = new 
MultiClusterRoutingContext(tableCacheMap, _routingManager,
-        _multiClusterRoutingManager);
-    LOGGER.info("[multi-cluster] Created federation provider with {}/{} 
clusters (1 primary + {} remote)",
-        tableCacheMap.size(), _remoteSpectatorHelixManager.size() + 1, 
tableCacheMap.size() - 1);
+        _multiClusterRoutingManager, _unavailableClusters);
+    LOGGER.info("[multi-cluster] Created federation provider with {}/{} 
clusters (1 primary + {} remote), "
+            + "{} unavailable",
+        tableCacheMap.size(), _remoteSpectatorHelixManager.size() + 1, 
tableCacheMap.size() - 1,
+        _unavailableClusters.size());
   }
 
   private void initRemoteClusterRouting() {
diff --git 
a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java
 
b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java
index b578d29e716..140d22ae97f 100644
--- 
a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java
+++ 
b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandler.java
@@ -849,6 +849,15 @@ public abstract class BaseSingleStageBrokerRequestHandler 
extends BaseBrokerRequ
     for (QueryProcessingException errorMsg : errorMsgs) {
       brokerResponse.addException(errorMsg);
     }
+
+    // Add warnings for unavailable remote clusters in multi-cluster routing.
+    if (_multiClusterRoutingContext != null && 
QueryOptionsUtils.isMultiClusterRoutingEnabled(
+        pinotQuery.getQueryOptions(), false)) {
+      for (QueryProcessingException clusterException : 
_multiClusterRoutingContext.getUnavailableClusterExceptions()) {
+        brokerResponse.addException(clusterException);
+      }
+    }
+
     brokerResponse.setNumSegmentsPrunedByBroker(numPrunedSegmentsTotal);
     long executionEndTimeNs = System.nanoTime();
     _brokerMetrics.addPhaseTiming(rawTableName, 
BrokerQueryPhase.QUERY_EXECUTION,
diff --git 
a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/MultiStageBrokerRequestHandler.java
 
b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/MultiStageBrokerRequestHandler.java
index 717b8fb1430..f589d90ee96 100644
--- 
a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/MultiStageBrokerRequestHandler.java
+++ 
b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/MultiStageBrokerRequestHandler.java
@@ -678,6 +678,15 @@ public class MultiStageBrokerRequestHandler extends 
BaseBrokerRequestHandler {
       }
       requestContext.setNumUnavailableSegments(numUnavailableSegments);
 
+      // Add warnings for unavailable remote clusters in multi-cluster routing
+      if (_multiClusterRoutingContext != null && 
QueryOptionsUtils.isMultiClusterRoutingEnabled(query.getOptions(),
+          false)) {
+        for (QueryProcessingException clusterException
+            : _multiClusterRoutingContext.getUnavailableClusterExceptions()) {
+          brokerResponse.addException(clusterException);
+        }
+      }
+
       fillOldBrokerResponseStats(brokerResponse, queryResults.getQueryStats(), 
dispatchableSubPlan);
       long totalTimeMs = System.currentTimeMillis() - 
requestContext.getRequestArrivalTimeMillis();
       
_brokerMetrics.addTimedValue(BrokerTimer.MULTI_STAGE_QUERY_TOTAL_TIME_MS, 
totalTimeMs, TimeUnit.MILLISECONDS);
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/routing/MultiClusterRoutingContext.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/routing/MultiClusterRoutingContext.java
index c40f3aee6f5..48454c58a0b 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/routing/MultiClusterRoutingContext.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/routing/MultiClusterRoutingContext.java
@@ -18,10 +18,16 @@
  */
 package org.apache.pinot.core.routing;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javax.annotation.Nullable;
 import org.apache.pinot.common.config.provider.TableCache;
+import org.apache.pinot.common.response.broker.QueryProcessingException;
 import org.apache.pinot.common.utils.config.QueryOptionsUtils;
+import org.apache.pinot.spi.exception.QueryErrorCode;
 
 
 /**
@@ -40,18 +46,22 @@ public class MultiClusterRoutingContext {
   @Nullable
   private final RoutingManager _multiClusterRoutingManager;
 
+  // Set of cluster names that failed to connect (for warning in query 
responses)
+  private final Set<String> _unavailableClusters;
+
   /**
-   * Constructor for FederationProvider with routing managers.
+   * Constructor for FederationProvider with routing managers and unavailable 
clusters.
    *
    * @param tableCacheMap Map of cluster name to TableCache
    * @param localRoutingManager Local routing manager for non-federated queries
    * @param multiClusterRoutingManager Multi cluster routing manager for 
cross-cluster queries (can be null)
    */
   public MultiClusterRoutingContext(Map<String, TableCache> tableCacheMap, 
RoutingManager localRoutingManager,
-      @Nullable RoutingManager multiClusterRoutingManager) {
+      @Nullable RoutingManager multiClusterRoutingManager, Set<String> 
unavailableClusters) {
     _tableCacheMap = tableCacheMap;
     _localRoutingManager = localRoutingManager;
     _multiClusterRoutingManager = multiClusterRoutingManager;
+    _unavailableClusters = unavailableClusters != null ? unavailableClusters : 
Collections.emptySet();
   }
 
   public Map<String, TableCache> getTableCacheMap() {
@@ -81,4 +91,21 @@ public class MultiClusterRoutingContext {
   public RoutingManager getMultiClusterRoutingManager() {
     return _multiClusterRoutingManager;
   }
+
+  public boolean hasUnavailableClusters() {
+    return !_unavailableClusters.isEmpty();
+  }
+
+  public List<QueryProcessingException> getUnavailableClusterExceptions() {
+    if (_unavailableClusters.isEmpty()) {
+      return Collections.emptyList();
+    }
+    List<QueryProcessingException> exceptions = new ArrayList<>();
+    for (String clusterName : _unavailableClusters) {
+      String message = String.format("Remote cluster '%s' is not connected. "
+          + "Query results may be incomplete.", clusterName);
+      exceptions.add(new 
QueryProcessingException(QueryErrorCode.REMOTE_CLUSTER_UNAVAILABLE, message));
+    }
+    return exceptions;
+  }
 }
diff --git 
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/multicluster/MultiClusterIntegrationTest.java
 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/multicluster/MultiClusterIntegrationTest.java
index 7173e6f6008..dc434cfd75a 100644
--- 
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/multicluster/MultiClusterIntegrationTest.java
+++ 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/multicluster/MultiClusterIntegrationTest.java
@@ -65,6 +65,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
@@ -92,9 +93,12 @@ public class MultiClusterIntegrationTest extends ClusterTest 
{
   protected static final int TABLE_SIZE_CLUSTER_2 = 1000;
   protected static final int SEGMENTS_PER_CLUSTER = 3;
   protected static final String JOIN_COLUMN = "OriginCityName";
+  protected static final String UNAVAILABLE_CLUSTER_NAME = 
"UnavailableCluster";
+  protected static final String UNAVAILABLE_ZK_ADDRESS = "localhost:29999";
 
   protected ClusterComponents _cluster1;
   protected ClusterComponents _cluster2;
+  protected ClusterComponents _brokerWithUnavailableCluster;
   protected List<File> _cluster1AvroFiles;
   protected List<File> _cluster2AvroFiles;
 
@@ -122,9 +126,40 @@ public class MultiClusterIntegrationTest extends 
ClusterTest {
     startCluster(_cluster1, _cluster2, CLUSTER_1_CONFIG);
     startCluster(_cluster2, _cluster1, CLUSTER_2_CONFIG);
 
+    // Start an alternate broker with one valid and one unavailable remote 
cluster
+    startBrokerWithUnavailableCluster();
+
     LOGGER.info("MultiClusterIntegrationTest setup complete");
   }
 
+  /**
+   * Starts a broker configured with cluster2 (valid) and an unavailable 
cluster (invalid ZK).
+   */
+  private void startBrokerWithUnavailableCluster() throws Exception {
+    _brokerWithUnavailableCluster = new ClusterComponents();
+    _brokerWithUnavailableCluster._brokerPort = findAvailablePort(55000);
+
+    PinotConfiguration brokerConfig = new PinotConfiguration();
+    brokerConfig.setProperty(Helix.CONFIG_OF_ZOOKEEPER_SERVER, 
_cluster1._zkUrl);
+    brokerConfig.setProperty(Helix.CONFIG_OF_CLUSTER_NAME, CLUSTER_1_NAME);
+    brokerConfig.setProperty(Broker.CONFIG_OF_BROKER_HOSTNAME, 
ControllerTest.LOCAL_HOST);
+    brokerConfig.setProperty(Helix.KEY_OF_BROKER_QUERY_PORT, 
_brokerWithUnavailableCluster._brokerPort);
+    brokerConfig.setProperty(Broker.CONFIG_OF_BROKER_TIMEOUT_MS, 60 * 1000L);
+    brokerConfig.setProperty(Broker.CONFIG_OF_DELAY_SHUTDOWN_TIME_MS, 0);
+    brokerConfig.setProperty(CommonConstants.CONFIG_OF_TIMEZONE, "UTC");
+    brokerConfig.setProperty(Helix.CONFIG_OF_REMOTE_CLUSTER_NAMES,
+        CLUSTER_2_NAME + "," + UNAVAILABLE_CLUSTER_NAME);
+    
brokerConfig.setProperty(String.format(Helix.CONFIG_OF_REMOTE_ZOOKEEPER_SERVERS,
 CLUSTER_2_NAME),
+        _cluster2._zkUrl);
+    
brokerConfig.setProperty(String.format(Helix.CONFIG_OF_REMOTE_ZOOKEEPER_SERVERS,
 UNAVAILABLE_CLUSTER_NAME),
+        UNAVAILABLE_ZK_ADDRESS);
+
+    _brokerWithUnavailableCluster._brokerStarter = createBrokerStarter();
+    _brokerWithUnavailableCluster._brokerStarter.init(brokerConfig);
+    _brokerWithUnavailableCluster._brokerStarter.start();
+    LOGGER.info("Started broker with unavailable cluster on port {}", 
_brokerWithUnavailableCluster._brokerPort);
+  }
+
   // TODO: Add more tests for cross-cluster queries in subsequent iterations.
   @Test
   public void testMultiClusterBrokerStartsAndIsQueryable() throws Exception {
@@ -165,8 +200,11 @@ public class MultiClusterIntegrationTest extends 
ClusterTest {
     LOGGER.info("Multi-cluster broker test passed: both clusters started and 
queryable");
   }
 
-  @Test
-  public void testLogicalFederationTwoOfflineTablesSSE() throws Exception {
+  @Test(dataProvider = "brokerModes")
+  public void testLogicalFederationTwoOfflineTablesSSE(int brokerPort, boolean 
expectUnavailableException)
+      throws Exception {
+    LOGGER.info("Testing SSE on broker port {} 
(expectUnavailableException={})",
+        brokerPort, expectUnavailableException);
     dropLogicalTableIfExists(LOGICAL_TABLE_NAME, 
_cluster1._controllerBaseApiUrl);
     dropLogicalTableIfExists(LOGICAL_TABLE_NAME, 
_cluster2._controllerBaseApiUrl);
     dropLogicalTableIfExists(LOGICAL_TABLE_NAME_2, 
_cluster1._controllerBaseApiUrl);
@@ -180,12 +218,18 @@ public class MultiClusterIntegrationTest extends 
ClusterTest {
     loadDataIntoCluster(_cluster1AvroFiles, 
LOGICAL_FEDERATION_CLUSTER_1_TABLE, _cluster1);
     loadDataIntoCluster(_cluster2AvroFiles, 
LOGICAL_FEDERATION_CLUSTER_2_TABLE, _cluster2);
     long expectedTotal = TABLE_SIZE_CLUSTER_1 + TABLE_SIZE_CLUSTER_2;
-    assertEquals(getCount(LOGICAL_TABLE_NAME, _cluster1, true), expectedTotal);
-    assertEquals(getCount(LOGICAL_TABLE_NAME, _cluster2, true), expectedTotal);
+
+    String query = "SET enableMultiClusterRouting=true; SELECT COUNT(*) as 
count FROM " + LOGICAL_TABLE_NAME;
+    String result = executeQueryOnBrokerPort(query, brokerPort);
+    assertEquals(parseCountResult(result), expectedTotal);
+    verifyUnavailableClusterException(result, expectUnavailableException);
   }
 
-  @Test
-  public void testLogicalFederationTwoLogicalTablesMSE() throws Exception {
+  @Test(dataProvider = "brokerModes")
+  public void testLogicalFederationTwoLogicalTablesMSE(int brokerPort, boolean 
expectUnavailableException)
+      throws Exception {
+    LOGGER.info("Testing MSE on broker port {} 
(expectUnavailableException={})",
+        brokerPort, expectUnavailableException);
     dropLogicalTableIfExists(LOGICAL_TABLE_NAME, 
_cluster1._controllerBaseApiUrl);
     dropLogicalTableIfExists(LOGICAL_TABLE_NAME, 
_cluster2._controllerBaseApiUrl);
     dropLogicalTableIfExists(LOGICAL_TABLE_NAME_2, 
_cluster1._controllerBaseApiUrl);
@@ -207,10 +251,22 @@ public class MultiClusterIntegrationTest extends 
ClusterTest {
         + "SELECT t1." + JOIN_COLUMN + ", COUNT(*) as count FROM " + 
LOGICAL_TABLE_NAME + " t1 "
         + "JOIN " + LOGICAL_TABLE_NAME_2 + " t2 ON t1." + JOIN_COLUMN + " = 
t2." + JOIN_COLUMN + " "
         + "GROUP BY t1." + JOIN_COLUMN + " LIMIT 20";
-    String result = executeQuery(joinQuery, _cluster1);
+    String result = executeQueryOnBrokerPort(joinQuery, brokerPort);
     assertNotNull(result);
     assertTrue(result.contains("resultTable"));
     assertResultRows(result);
+    verifyUnavailableClusterException(result, expectUnavailableException);
+  }
+
+  /**
+   * Data provider for broker modes: normal broker vs broker with unavailable 
remote cluster.
+   */
+  @DataProvider(name = "brokerModes")
+  public Object[][] brokerModes() {
+    return new Object[][]{
+        {_cluster1._brokerPort, false},  // Normal broker - all clusters 
connected
+        {_brokerWithUnavailableCluster._brokerPort, true}  // Broker with 
unavailable cluster
+    };
   }
 
   @Override
@@ -497,11 +553,34 @@ public class MultiClusterIntegrationTest extends 
ClusterTest {
   }
 
   protected String executeQuery(String query, ClusterComponents cluster) 
throws Exception {
+    return executeQueryOnBrokerPort(query, cluster._brokerPort);
+  }
+
+  protected String executeQueryOnBrokerPort(String query, int brokerPort) 
throws Exception {
     Map<String, Object> payload = Map.of("sql", query);
-    String url = "http://localhost:"; + cluster._brokerPort + "/query/sql";
+    String url = "http://localhost:"; + brokerPort + "/query/sql";
     return ControllerTest.sendPostRequest(url, 
JsonUtils.objectToPrettyString(payload));
   }
 
+  protected void verifyUnavailableClusterException(String result, boolean 
expectException) throws Exception {
+    if (expectException) {
+      assertTrue(result.contains(UNAVAILABLE_CLUSTER_NAME),
+          "Response should mention unavailable cluster: " + 
UNAVAILABLE_CLUSTER_NAME);
+      JsonNode resultJson = JsonMapper.builder().build().readTree(result);
+      JsonNode exceptions = resultJson.get("exceptions");
+      assertNotNull(exceptions, "Exceptions array should exist");
+      boolean found = false;
+      for (JsonNode ex : exceptions) {
+        if (ex.get("errorCode").asInt() == 510
+            && ex.get("message").asText().contains(UNAVAILABLE_CLUSTER_NAME)) {
+          found = true;
+          break;
+        }
+      }
+      assertTrue(found, "Should find REMOTE_CLUSTER_UNAVAILABLE (510) 
exception");
+    }
+  }
+
   protected long parseCountResult(String result) {
     try {
       JsonNode rows = 
JsonMapper.builder().build().readTree(result).path("resultTable").path("rows");
@@ -525,6 +604,14 @@ public class MultiClusterIntegrationTest extends 
ClusterTest {
 
   @AfterClass
   public void tearDown() throws Exception {
+    // Stop the alternate broker with unavailable cluster
+    if (_brokerWithUnavailableCluster != null && 
_brokerWithUnavailableCluster._brokerStarter != null) {
+      try {
+        _brokerWithUnavailableCluster._brokerStarter.stop();
+      } catch (Exception e) {
+        LOGGER.warn("Error stopping broker with unavailable cluster", e);
+      }
+    }
     stopCluster(_cluster1);
     stopCluster(_cluster2);
   }
diff --git 
a/pinot-spi/src/main/java/org/apache/pinot/spi/exception/QueryErrorCode.java 
b/pinot-spi/src/main/java/org/apache/pinot/spi/exception/QueryErrorCode.java
index b7fac4e03fd..ea2629b7488 100644
--- a/pinot-spi/src/main/java/org/apache/pinot/spi/exception/QueryErrorCode.java
+++ b/pinot-spi/src/main/java/org/apache/pinot/spi/exception/QueryErrorCode.java
@@ -59,6 +59,7 @@ public enum QueryErrorCode {
   INTERNAL(450, "InternalError", Response.Status.INTERNAL_SERVER_ERROR),
   MERGE_RESPONSE(500, "MergeResponseError", 
Response.Status.INTERNAL_SERVER_ERROR),
   QUERY_CANCELLATION(503, "QueryCancellationError", 
Response.Status.SERVICE_UNAVAILABLE),
+  REMOTE_CLUSTER_UNAVAILABLE(510, "RemoteClusterUnavailable", 
Response.Status.SERVICE_UNAVAILABLE),
   /// Error detected at validation time. For example, type mismatch.
   QUERY_VALIDATION(700, "QueryValidationError", Response.Status.BAD_REQUEST),
   UNKNOWN_COLUMN(710, "UnknownColumnError", Response.Status.BAD_REQUEST),


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to