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

jonwei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 97b6407  maintenance mode for Historical (#6349)
97b6407 is described below

commit 97b6407983f597fc039bb90339b41086bbaaea56
Author: Egor Riashin <[email protected]>
AuthorDate: Tue Feb 5 05:11:00 2019 +0300

    maintenance mode for Historical (#6349)
    
    * maintenance mode for Historical
    
    forbidden api fix, config deserialization fix
    
    logging fix, unit tests
    
    * addressed comments
    
    * addressed comments
    
    * a style fix
    
    * addressed comments
    
    * a unit-test fix due to recent code-refactoring
    
    * docs & refactoring
    
    * addressed comments
    
    * addressed a LoadRule drop flaw
    
    * post merge cleaning up
---
 docs/content/configuration/index.md                |   6 +-
 server/pom.xml                                     |   6 +
 .../coordinator/CoordinatorDynamicConfig.java      | 144 ++++++++---
 .../druid/server/coordinator/DruidCluster.java     |  14 +-
 .../druid/server/coordinator/DruidCoordinator.java |   9 +-
 .../DruidCoordinatorCleanupPendingSegments.java    |   2 +-
 .../coordinator/DruidCoordinatorRuntimeParams.java |  13 +-
 .../druid/server/coordinator/ServerHolder.java     |  18 ++
 .../helper/DruidCoordinatorBalancer.java           |  95 ++++---
 .../helper/DruidCoordinatorSegmentKiller.java      |   2 +-
 .../rules/BroadcastDistributionRule.java           |   5 +-
 .../druid/server/coordinator/rules/LoadRule.java   |  42 ++-
 .../http/CoordinatorDynamicConfigsResource.java    |  31 ++-
 .../DruidCoordinatorBalancerProfiler.java          |   7 +-
 .../coordinator/DruidCoordinatorBalancerTest.java  | 259 ++++++++++++++++++-
 .../DruidCoordinatorBalancerTester.java            |  14 +-
 .../DruidCoordinatorRuleRunnerTest.java            |   6 +-
 .../rules/BroadcastDistributionRuleTest.java       | 113 +++++++-
 .../server/coordinator/rules/LoadRuleTest.java     | 287 ++++++++++++++++++++-
 .../server/http/CoordinatorDynamicConfigTest.java  |  92 +++++--
 20 files changed, 1012 insertions(+), 153 deletions(-)

diff --git a/docs/content/configuration/index.md 
b/docs/content/configuration/index.md
index 221cccd..c63eb41 100644
--- a/docs/content/configuration/index.md
+++ b/docs/content/configuration/index.md
@@ -779,7 +779,9 @@ A sample Coordinator dynamic config JSON object is shown 
below:
   "replicantLifetime": 15,
   "replicationThrottleLimit": 10,
   "emitBalancingStats": false,
-  "killDataSourceWhitelist": ["wikipedia", "testDatasource"]
+  "killDataSourceWhitelist": ["wikipedia", "testDatasource"],
+  "historicalNodesInMaintenance": ["localhost:8182", "localhost:8282"],
+  "nodesInMaintenancePriority": 7
 }
 ```
 
@@ -799,6 +801,8 @@ Issuing a GET request at the same URL will return the spec 
that is currently in
 |`killAllDataSources`|Send kill tasks for ALL dataSources if property 
`druid.coordinator.kill.on` is true. If this is set to true then 
`killDataSourceWhitelist` must not be specified or be empty list.|false|
 |`killPendingSegmentsSkipList`|List of dataSources for which pendingSegments 
are _NOT_ cleaned up if property `druid.coordinator.kill.pendingSegments.on` is 
true. This can be a list of comma-separated dataSources or a JSON array.|none|
 |`maxSegmentsInNodeLoadingQueue`|The maximum number of segments that could be 
queued for loading to any given server. This parameter could be used to speed 
up segments loading process, especially if there are "slow" nodes in the 
cluster (with low loading speed) or if too much segments scheduled to be 
replicated to some particular node (faster loading could be preferred to better 
segments distribution). Desired value depends on segments loading speed, 
acceptable replication time and numbe [...]
+|`historicalNodesInMaintenance`| List of Historical nodes in maintenance mode. 
Coordinator doesn't assign new segments on those nodes and moves segments from 
the nodes according to a specified priority.|none|
+|`nodesInMaintenancePriority`| Priority of segments from servers in 
maintenance. Coordinator takes ceil(maxSegmentsToMove * (priority / 10)) from 
servers in maitenance during balancing phase, i.e.:<br>0 - no segments from 
servers in maintenance will be processed during balancing<br>5 - 50% segments 
from servers in maintenance<br>10 - 100% segments from servers in 
maintenance<br>By leveraging the priority an operator can prevent general nodes 
from overload or decrease maitenance time instead.|7|
 
 To view the audit history of Coordinator dynamic config issue a GET request to 
the URL -
 
diff --git a/server/pom.xml b/server/pom.xml
index b28013b..ab2ca24 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -192,6 +192,12 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-all</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>com.carrotsearch</groupId>
             <artifactId>junit-benchmarks</artifactId>
             <scope>test</scope>
diff --git 
a/server/src/main/java/org/apache/druid/server/coordinator/CoordinatorDynamicConfig.java
 
b/server/src/main/java/org/apache/druid/server/coordinator/CoordinatorDynamicConfig.java
index c51a04a..de034af 100644
--- 
a/server/src/main/java/org/apache/druid/server/coordinator/CoordinatorDynamicConfig.java
+++ 
b/server/src/main/java/org/apache/druid/server/coordinator/CoordinatorDynamicConfig.java
@@ -55,10 +55,12 @@ public class CoordinatorDynamicConfig
   private final int balancerComputeThreads;
   private final boolean emitBalancingStats;
   private final boolean killAllDataSources;
-  private final Set<String> killDataSourceWhitelist;
+  private final Set<String> killableDataSources;
+  private final Set<String> historicalNodesInMaintenance;
+  private final int nodesInMaintenancePriority;
 
   // The pending segments of the dataSources in this list are not killed.
-  private final Set<String> killPendingSegmentsSkipList;
+  private final Set<String> protectedPendingSegmentDatasources;
 
   /**
    * The maximum number of segments that could be queued for loading to any 
given server.
@@ -82,10 +84,12 @@ public class CoordinatorDynamicConfig
       // Type is Object here so that we can support both string and list as
       // coordinator console can not send array of strings in the update 
request.
       // See https://github.com/apache/incubator-druid/issues/3055
-      @JsonProperty("killDataSourceWhitelist") Object killDataSourceWhitelist,
+      @JsonProperty("killDataSourceWhitelist") Object killableDataSources,
       @JsonProperty("killAllDataSources") boolean killAllDataSources,
-      @JsonProperty("killPendingSegmentsSkipList") Object 
killPendingSegmentsSkipList,
-      @JsonProperty("maxSegmentsInNodeLoadingQueue") int 
maxSegmentsInNodeLoadingQueue
+      @JsonProperty("killPendingSegmentsSkipList") Object 
protectedPendingSegmentDatasources,
+      @JsonProperty("maxSegmentsInNodeLoadingQueue") int 
maxSegmentsInNodeLoadingQueue,
+      @JsonProperty("historicalNodesInMaintenance") Object 
historicalNodesInMaintenance,
+      @JsonProperty("nodesInMaintenancePriority") int 
nodesInMaintenancePriority
   )
   {
     this.millisToWaitBeforeDeleting = millisToWaitBeforeDeleting;
@@ -97,11 +101,17 @@ public class CoordinatorDynamicConfig
     this.balancerComputeThreads = Math.max(balancerComputeThreads, 1);
     this.emitBalancingStats = emitBalancingStats;
     this.killAllDataSources = killAllDataSources;
-    this.killDataSourceWhitelist = 
parseJsonStringOrArray(killDataSourceWhitelist);
-    this.killPendingSegmentsSkipList = 
parseJsonStringOrArray(killPendingSegmentsSkipList);
+    this.killableDataSources = parseJsonStringOrArray(killableDataSources);
+    this.protectedPendingSegmentDatasources = 
parseJsonStringOrArray(protectedPendingSegmentDatasources);
     this.maxSegmentsInNodeLoadingQueue = maxSegmentsInNodeLoadingQueue;
+    this.historicalNodesInMaintenance = 
parseJsonStringOrArray(historicalNodesInMaintenance);
+    Preconditions.checkArgument(
+        nodesInMaintenancePriority >= 0 && nodesInMaintenancePriority <= 10,
+        "nodesInMaintenancePriority should be in range [0, 10]"
+    );
+    this.nodesInMaintenancePriority = nodesInMaintenancePriority;
 
-    if (this.killAllDataSources && !this.killDataSourceWhitelist.isEmpty()) {
+    if (this.killAllDataSources && !this.killableDataSources.isEmpty()) {
       throw new IAE("can't have killAllDataSources and non-empty 
killDataSourceWhitelist");
     }
   }
@@ -188,10 +198,14 @@ public class CoordinatorDynamicConfig
     return balancerComputeThreads;
   }
 
-  @JsonProperty
-  public Set<String> getKillDataSourceWhitelist()
+  /**
+   * List of dataSources for which kill tasks are sent in
+   * {@link 
org.apache.druid.server.coordinator.helper.DruidCoordinatorSegmentKiller}.
+   */
+  @JsonProperty("killDataSourceWhitelist")
+  public Set<String> getKillableDataSources()
   {
-    return killDataSourceWhitelist;
+    return killableDataSources;
   }
 
   @JsonProperty
@@ -200,10 +214,14 @@ public class CoordinatorDynamicConfig
     return killAllDataSources;
   }
 
+  /**
+   * List of dataSources for which pendingSegments are NOT cleaned up
+   * in {@link DruidCoordinatorCleanupPendingSegments}.
+   */
   @JsonProperty
-  public Set<String> getKillPendingSegmentsSkipList()
+  public Set<String> getProtectedPendingSegmentDatasources()
   {
-    return killPendingSegmentsSkipList;
+    return protectedPendingSegmentDatasources;
   }
 
   @JsonProperty
@@ -212,6 +230,35 @@ public class CoordinatorDynamicConfig
     return maxSegmentsInNodeLoadingQueue;
   }
 
+  /**
+   * Historical nodes list in maintenance mode. Coordinator doesn't assign new 
segments on those nodes and moves
+   * segments from those nodes according to a specified priority.
+   *
+   * @return list of host:port entries
+   */
+  @JsonProperty
+  public Set<String> getHistoricalNodesInMaintenance()
+  {
+    return historicalNodesInMaintenance;
+  }
+
+  /**
+   * Priority of segments from servers in maintenance. Coordinator takes 
ceil(maxSegmentsToMove * (priority / 10))
+   * from servers in maitenance during balancing phase, i.e.:
+   * 0 - no segments from servers in maintenance will be processed during 
balancing
+   * 5 - 50% segments from servers in maintenance
+   * 10 - 100% segments from servers in maintenance
+   * By leveraging the priority an operator can prevent general nodes from 
overload or decrease maitenance time
+   * instead.
+   *
+   * @return number in range [0, 10]
+   */
+  @JsonProperty
+  public int getNodesInMaintenancePriority()
+  {
+    return nodesInMaintenancePriority;
+  }
+
   @Override
   public String toString()
   {
@@ -224,10 +271,12 @@ public class CoordinatorDynamicConfig
            ", replicationThrottleLimit=" + replicationThrottleLimit +
            ", balancerComputeThreads=" + balancerComputeThreads +
            ", emitBalancingStats=" + emitBalancingStats +
-           ", killDataSourceWhitelist=" + killDataSourceWhitelist +
            ", killAllDataSources=" + killAllDataSources +
-           ", killPendingSegmentsSkipList=" + killPendingSegmentsSkipList +
+           ", killDataSourceWhitelist=" + killableDataSources +
+           ", protectedPendingSegmentDatasources=" + 
protectedPendingSegmentDatasources +
            ", maxSegmentsInNodeLoadingQueue=" + maxSegmentsInNodeLoadingQueue +
+           ", historicalNodesInMaintenance=" + historicalNodesInMaintenance +
+           ", nodesInMaintenancePriority=" + nodesInMaintenancePriority +
            '}';
   }
 
@@ -273,10 +322,16 @@ public class CoordinatorDynamicConfig
     if (maxSegmentsInNodeLoadingQueue != that.maxSegmentsInNodeLoadingQueue) {
       return false;
     }
-    if (!Objects.equals(killDataSourceWhitelist, 
that.killDataSourceWhitelist)) {
+    if (!Objects.equals(killableDataSources, that.killableDataSources)) {
+      return false;
+    }
+    if (!Objects.equals(protectedPendingSegmentDatasources, 
that.protectedPendingSegmentDatasources)) {
+      return false;
+    }
+    if (!Objects.equals(historicalNodesInMaintenance, 
that.historicalNodesInMaintenance)) {
       return false;
     }
-    return Objects.equals(killPendingSegmentsSkipList, 
that.killPendingSegmentsSkipList);
+    return nodesInMaintenancePriority == that.nodesInMaintenancePriority;
   }
 
   @Override
@@ -293,8 +348,10 @@ public class CoordinatorDynamicConfig
         emitBalancingStats,
         killAllDataSources,
         maxSegmentsInNodeLoadingQueue,
-        killDataSourceWhitelist,
-        killPendingSegmentsSkipList
+        killableDataSources,
+        protectedPendingSegmentDatasources,
+        historicalNodesInMaintenance,
+        nodesInMaintenancePriority
     );
   }
 
@@ -315,6 +372,7 @@ public class CoordinatorDynamicConfig
     private static final boolean DEFAULT_EMIT_BALANCING_STATS = false;
     private static final boolean DEFAULT_KILL_ALL_DATA_SOURCES = false;
     private static final int DEFAULT_MAX_SEGMENTS_IN_NODE_LOADING_QUEUE = 0;
+    private static final int DEFAULT_MAINTENANCE_MODE_SEGMENTS_PRIORITY = 7;
 
     private Long millisToWaitBeforeDeleting;
     private Long mergeBytesLimit;
@@ -324,10 +382,12 @@ public class CoordinatorDynamicConfig
     private Integer replicationThrottleLimit;
     private Boolean emitBalancingStats;
     private Integer balancerComputeThreads;
-    private Object killDataSourceWhitelist;
+    private Object killableDataSources;
     private Boolean killAllDataSources;
     private Object killPendingSegmentsSkipList;
     private Integer maxSegmentsInNodeLoadingQueue;
+    private Object maintenanceList;
+    private Integer maintenanceModeSegmentsPriority;
 
     public Builder()
     {
@@ -343,10 +403,12 @@ public class CoordinatorDynamicConfig
         @JsonProperty("replicationThrottleLimit") @Nullable Integer 
replicationThrottleLimit,
         @JsonProperty("balancerComputeThreads") @Nullable Integer 
balancerComputeThreads,
         @JsonProperty("emitBalancingStats") @Nullable Boolean 
emitBalancingStats,
-        @JsonProperty("killDataSourceWhitelist") @Nullable Object 
killDataSourceWhitelist,
+        @JsonProperty("killDataSourceWhitelist") @Nullable Object 
killableDataSources,
         @JsonProperty("killAllDataSources") @Nullable Boolean 
killAllDataSources,
         @JsonProperty("killPendingSegmentsSkipList") @Nullable Object 
killPendingSegmentsSkipList,
-        @JsonProperty("maxSegmentsInNodeLoadingQueue") @Nullable Integer 
maxSegmentsInNodeLoadingQueue
+        @JsonProperty("maxSegmentsInNodeLoadingQueue") @Nullable Integer 
maxSegmentsInNodeLoadingQueue,
+        @JsonProperty("historicalNodesInMaintenance") @Nullable Object 
maintenanceList,
+        @JsonProperty("nodesInMaintenancePriority") @Nullable Integer 
maintenanceModeSegmentsPriority
     )
     {
       this.millisToWaitBeforeDeleting = millisToWaitBeforeDeleting;
@@ -358,9 +420,11 @@ public class CoordinatorDynamicConfig
       this.balancerComputeThreads = balancerComputeThreads;
       this.emitBalancingStats = emitBalancingStats;
       this.killAllDataSources = killAllDataSources;
-      this.killDataSourceWhitelist = killDataSourceWhitelist;
+      this.killableDataSources = killableDataSources;
       this.killPendingSegmentsSkipList = killPendingSegmentsSkipList;
       this.maxSegmentsInNodeLoadingQueue = maxSegmentsInNodeLoadingQueue;
+      this.maintenanceList = maintenanceList;
+      this.maintenanceModeSegmentsPriority = maintenanceModeSegmentsPriority;
     }
 
     public Builder withMillisToWaitBeforeDeleting(long 
millisToWaitBeforeDeleting)
@@ -413,7 +477,7 @@ public class CoordinatorDynamicConfig
 
     public Builder withKillDataSourceWhitelist(Set<String> 
killDataSourceWhitelist)
     {
-      this.killDataSourceWhitelist = killDataSourceWhitelist;
+      this.killableDataSources = killDataSourceWhitelist;
       return this;
     }
 
@@ -429,6 +493,18 @@ public class CoordinatorDynamicConfig
       return this;
     }
 
+    public Builder withMaintenanceList(Set<String> list)
+    {
+      this.maintenanceList = list;
+      return this;
+    }
+
+    public Builder withMaintenanceModeSegmentsPriority(Integer priority)
+    {
+      this.maintenanceModeSegmentsPriority = priority;
+      return this;
+    }
+
     public CoordinatorDynamicConfig build()
     {
       return new CoordinatorDynamicConfig(
@@ -440,12 +516,16 @@ public class CoordinatorDynamicConfig
           replicationThrottleLimit == null ? 
DEFAULT_REPLICATION_THROTTLE_LIMIT : replicationThrottleLimit,
           balancerComputeThreads == null ? DEFAULT_BALANCER_COMPUTE_THREADS : 
balancerComputeThreads,
           emitBalancingStats == null ? DEFAULT_EMIT_BALANCING_STATS : 
emitBalancingStats,
-          killDataSourceWhitelist,
+          killableDataSources,
           killAllDataSources == null ? DEFAULT_KILL_ALL_DATA_SOURCES : 
killAllDataSources,
           killPendingSegmentsSkipList,
           maxSegmentsInNodeLoadingQueue == null
           ? DEFAULT_MAX_SEGMENTS_IN_NODE_LOADING_QUEUE
-          : maxSegmentsInNodeLoadingQueue
+          : maxSegmentsInNodeLoadingQueue,
+          maintenanceList,
+          maintenanceModeSegmentsPriority == null
+          ? DEFAULT_MAINTENANCE_MODE_SEGMENTS_PRIORITY
+          : maintenanceModeSegmentsPriority
       );
     }
 
@@ -460,12 +540,18 @@ public class CoordinatorDynamicConfig
           replicationThrottleLimit == null ? 
defaults.getReplicationThrottleLimit() : replicationThrottleLimit,
           balancerComputeThreads == null ? 
defaults.getBalancerComputeThreads() : balancerComputeThreads,
           emitBalancingStats == null ? defaults.emitBalancingStats() : 
emitBalancingStats,
-          killDataSourceWhitelist == null ? 
defaults.getKillDataSourceWhitelist() : killDataSourceWhitelist,
+          killableDataSources == null ? defaults.getKillableDataSources() : 
killableDataSources,
           killAllDataSources == null ? defaults.isKillAllDataSources() : 
killAllDataSources,
-          killPendingSegmentsSkipList == null ? 
defaults.getKillPendingSegmentsSkipList() : killPendingSegmentsSkipList,
+          killPendingSegmentsSkipList == null
+          ? defaults.getProtectedPendingSegmentDatasources()
+          : killPendingSegmentsSkipList,
           maxSegmentsInNodeLoadingQueue == null
           ? defaults.getMaxSegmentsInNodeLoadingQueue()
-          : maxSegmentsInNodeLoadingQueue
+          : maxSegmentsInNodeLoadingQueue,
+          maintenanceList == null ? defaults.getHistoricalNodesInMaintenance() 
: maintenanceList,
+          maintenanceModeSegmentsPriority == null
+          ? defaults.getNodesInMaintenancePriority()
+          : maintenanceModeSegmentsPriority
       );
     }
   }
diff --git 
a/server/src/main/java/org/apache/druid/server/coordinator/DruidCluster.java 
b/server/src/main/java/org/apache/druid/server/coordinator/DruidCluster.java
index c5ba2a5..586f92b 100644
--- a/server/src/main/java/org/apache/druid/server/coordinator/DruidCluster.java
+++ b/server/src/main/java/org/apache/druid/server/coordinator/DruidCluster.java
@@ -34,6 +34,8 @@ import java.util.Map;
 import java.util.NavigableSet;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 /**
  * Contains a representation of the current state of the cluster by tier.
@@ -53,11 +55,19 @@ public class DruidCluster
   @VisibleForTesting
   public DruidCluster(
       @Nullable Set<ServerHolder> realtimes,
-      Map<String, NavigableSet<ServerHolder>> historicals
+      Map<String, Iterable<ServerHolder>> historicals
   )
   {
     this.realtimes = realtimes == null ? new HashSet<>() : new 
HashSet<>(realtimes);
-    this.historicals = historicals;
+    this.historicals = historicals
+        .entrySet()
+        .stream()
+        .collect(Collectors.toMap(
+            Map.Entry::getKey,
+            e -> StreamSupport
+                .stream(e.getValue().spliterator(), false)
+                .collect(Collectors.toCollection(() -> new 
TreeSet<>(Collections.reverseOrder())))
+        ));
   }
 
   public void add(ServerHolder serverHolder)
diff --git 
a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java
 
b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java
index 1ac6187e..b92effc 100644
--- 
a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java
+++ 
b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinator.java
@@ -694,6 +694,7 @@ public class DruidCoordinator
                 }
 
                 // Find all historical servers, group them by subType and sort 
by ascending usage
+                Set<String> nodesInMaintenance = 
params.getCoordinatorDynamicConfig().getHistoricalNodesInMaintenance();
                 final DruidCluster cluster = new DruidCluster();
                 for (ImmutableDruidServer server : servers) {
                   if (!loadManagementPeons.containsKey(server.getName())) {
@@ -704,7 +705,13 @@ public class DruidCoordinator
                     loadManagementPeons.put(server.getName(), loadQueuePeon);
                   }
 
-                  cluster.add(new ServerHolder(server, 
loadManagementPeons.get(server.getName())));
+                  cluster.add(
+                      new ServerHolder(
+                          server,
+                          loadManagementPeons.get(server.getName()),
+                          nodesInMaintenance.contains(server.getHost())
+                      )
+                  );
                 }
 
                 segmentReplicantLookup = SegmentReplicantLookup.make(cluster);
diff --git 
a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorCleanupPendingSegments.java
 
b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorCleanupPendingSegments.java
index 6ccc933..a25ea07 100644
--- 
a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorCleanupPendingSegments.java
+++ 
b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorCleanupPendingSegments.java
@@ -89,7 +89,7 @@ public class DruidCoordinatorCleanupPendingSegments 
implements DruidCoordinatorH
     // (DateTimes.nowUtc() - KEEP_PENDING_SEGMENTS_OFFSET).
     final DateTime pendingSegmentsCleanupEndTime = 
createdTimes.get(0).minus(KEEP_PENDING_SEGMENTS_OFFSET);
     for (String dataSource : params.getDataSources().keySet()) {
-      if 
(!params.getCoordinatorDynamicConfig().getKillPendingSegmentsSkipList().contains(dataSource))
 {
+      if 
(!params.getCoordinatorDynamicConfig().getProtectedPendingSegmentDatasources().contains(dataSource))
 {
         log.info(
             "Killed [%d] pendingSegments created until [%s] for 
dataSource[%s]",
             indexingServiceClient.killPendingSegments(dataSource, 
pendingSegmentsCleanupEndTime),
diff --git 
a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java
 
b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java
index c99438c..0839038 100644
--- 
a/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java
+++ 
b/server/src/main/java/org/apache/druid/server/coordinator/DruidCoordinatorRuntimeParams.java
@@ -19,6 +19,7 @@
 
 package org.apache.druid.server.coordinator;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.apache.druid.client.ImmutableDruidDataSource;
 import org.apache.druid.java.util.common.DateTimes;
 import org.apache.druid.java.util.emitter.service.ServiceEmitter;
@@ -27,6 +28,7 @@ import org.apache.druid.timeline.DataSegment;
 import org.apache.druid.timeline.VersionedIntervalTimeline;
 import org.joda.time.DateTime;
 
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -344,9 +346,16 @@ public class DruidCoordinatorRuntimeParams
       return this;
     }
 
-    public Builder withAvailableSegments(Collection<DataSegment> 
availableSegmentsCollection)
+    @VisibleForTesting
+    public Builder withAvailableSegments(DataSegment... availableSegments)
     {
-      
availableSegments.addAll(Collections.unmodifiableCollection(availableSegmentsCollection));
+      this.availableSegments.addAll(Arrays.asList(availableSegments));
+      return this;
+    }
+
+    public Builder withAvailableSegments(Collection<DataSegment> 
availableSegments)
+    {
+      
this.availableSegments.addAll(Collections.unmodifiableCollection(availableSegments));
       return this;
     }
 
diff --git 
a/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java 
b/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java
index fd1370a..c7d7a86 100644
--- a/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java
+++ b/server/src/main/java/org/apache/druid/server/coordinator/ServerHolder.java
@@ -32,11 +32,18 @@ public class ServerHolder implements 
Comparable<ServerHolder>
   private static final Logger log = new Logger(ServerHolder.class);
   private final ImmutableDruidServer server;
   private final LoadQueuePeon peon;
+  private final boolean inMaintenance;
 
   public ServerHolder(ImmutableDruidServer server, LoadQueuePeon peon)
   {
+    this(server, peon, false);
+  }
+
+  public ServerHolder(ImmutableDruidServer server, LoadQueuePeon peon, boolean 
inMaintenance)
+  {
     this.server = server;
     this.peon = peon;
+    this.inMaintenance = inMaintenance;
   }
 
   public ImmutableDruidServer getServer()
@@ -74,6 +81,17 @@ public class ServerHolder implements Comparable<ServerHolder>
     return (100.0 * getSizeUsed()) / getMaxSize();
   }
 
+  /**
+   * Historical nodes can be placed in maintenance mode, which instructs 
Coordinator to move segments from them
+   * according to a specified priority. The mechanism allows to drain segments 
from nodes which are planned for
+   * replacement.
+   * @return true if the node is in maitenance mode
+   */
+  public boolean isInMaintenance()
+  {
+    return inMaintenance;
+  }
+
   public long getAvailableSize()
   {
     long maxSize = getMaxSize();
diff --git 
a/server/src/main/java/org/apache/druid/server/coordinator/helper/DruidCoordinatorBalancer.java
 
b/server/src/main/java/org/apache/druid/server/coordinator/helper/DruidCoordinatorBalancer.java
index 60fc376..10499ac 100644
--- 
a/server/src/main/java/org/apache/druid/server/coordinator/helper/DruidCoordinatorBalancer.java
+++ 
b/server/src/main/java/org/apache/druid/server/coordinator/helper/DruidCoordinatorBalancer.java
@@ -21,6 +21,7 @@ package org.apache.druid.server.coordinator.helper;
 
 import com.google.common.collect.Lists;
 import org.apache.druid.client.ImmutableDruidServer;
+import org.apache.druid.java.util.common.Pair;
 import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.java.util.emitter.EmittingLogger;
 import org.apache.druid.server.coordinator.BalancerSegmentHolder;
@@ -34,7 +35,6 @@ import org.apache.druid.server.coordinator.ServerHolder;
 import org.apache.druid.timeline.DataSegment;
 import org.apache.druid.timeline.SegmentId;
 
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -48,8 +48,6 @@ import java.util.stream.Collectors;
  */
 public class DruidCoordinatorBalancer implements DruidCoordinatorHelper
 {
-  public static final Comparator<ServerHolder> percentUsedComparator =
-      Comparator.comparing(ServerHolder::getPercentUsed).reversed();
 
   protected static final EmittingLogger log = new 
EmittingLogger(DruidCoordinatorBalancer.class);
 
@@ -108,27 +106,73 @@ public class DruidCoordinatorBalancer implements 
DruidCoordinatorHelper
       return;
     }
 
-    final List<ServerHolder> toMoveFrom = Lists.newArrayList(servers);
-    final List<ServerHolder> toMoveTo = Lists.newArrayList(servers);
+    /*
+      Take as much segments from maintenance servers as priority allows and 
find the best location for them on
+      available servers. After that, balance segments within available servers 
pool.
+     */
+    Map<Boolean, List<ServerHolder>> partitions =
+        
servers.stream().collect(Collectors.partitioningBy(ServerHolder::isInMaintenance));
+    final List<ServerHolder> maintenanceServers = partitions.get(true);
+    final List<ServerHolder> availableServers = partitions.get(false);
+    log.info(
+        "Found %d servers in maintenance, %d available servers servers",
+        maintenanceServers.size(),
+        availableServers.size()
+    );
 
-    if (toMoveTo.size() <= 1) {
-      log.info("[%s]: One or fewer servers found.  Cannot balance.", tier);
-      return;
+    if (maintenanceServers.isEmpty()) {
+      if (availableServers.size() <= 1) {
+        log.info("[%s]: %d available servers servers found.  Cannot balance.", 
tier, availableServers.size());
+      }
+    } else if (availableServers.isEmpty()) {
+      log.info("[%s]: no available servers servers found during maintenance.  
Cannot balance.", tier);
     }
 
     int numSegments = 0;
-    for (ServerHolder sourceHolder : toMoveFrom) {
+    for (ServerHolder sourceHolder : servers) {
       numSegments += sourceHolder.getServer().getSegments().size();
     }
 
-
     if (numSegments == 0) {
       log.info("No segments found.  Cannot balance.");
       return;
     }
 
-    final BalancerStrategy strategy = params.getBalancerStrategy();
     final int maxSegmentsToMove = 
Math.min(params.getCoordinatorDynamicConfig().getMaxSegmentsToMove(), 
numSegments);
+    int priority = 
params.getCoordinatorDynamicConfig().getNodesInMaintenancePriority();
+    int maxMaintenanceSegmentsToMove = (int) Math.ceil(maxSegmentsToMove * 
priority / 10.0);
+    log.info("Processing %d segments from servers in maintenance mode", 
maxMaintenanceSegmentsToMove);
+    Pair<Integer, Integer> maintenanceResult =
+        balanceServers(params, maintenanceServers, availableServers, 
maxMaintenanceSegmentsToMove);
+    int maxGeneralSegmentsToMove = maxSegmentsToMove - maintenanceResult.lhs;
+    log.info("Processing %d segments from servers in general mode", 
maxGeneralSegmentsToMove);
+    Pair<Integer, Integer> generalResult =
+        balanceServers(params, availableServers, availableServers, 
maxGeneralSegmentsToMove);
+
+    int moved = generalResult.lhs + maintenanceResult.lhs;
+    int unmoved = generalResult.rhs + maintenanceResult.rhs;
+    if (unmoved == maxSegmentsToMove) {
+      // Cluster should be alive and constantly adjusting
+      log.info("No good moves found in tier [%s]", tier);
+    }
+    stats.addToTieredStat("unmovedCount", tier, unmoved);
+    stats.addToTieredStat("movedCount", tier, moved);
+
+    if (params.getCoordinatorDynamicConfig().emitBalancingStats()) {
+      final BalancerStrategy strategy = params.getBalancerStrategy();
+      strategy.emitStats(tier, stats, Lists.newArrayList(servers));
+    }
+    log.info("[%s]: Segments Moved: [%d] Segments Let Alone: [%d]", tier, 
moved, unmoved);
+  }
+
+  private Pair<Integer, Integer> balanceServers(
+      DruidCoordinatorRuntimeParams params,
+      List<ServerHolder> toMoveFrom,
+      List<ServerHolder> toMoveTo,
+      int maxSegmentsToMove
+  )
+  {
+    final BalancerStrategy strategy = params.getBalancerStrategy();
     final int maxIterations = 2 * maxSegmentsToMove;
     final int maxToLoad = 
params.getCoordinatorDynamicConfig().getMaxSegmentsInNodeLoadingQueue();
     int moved = 0, unmoved = 0;
@@ -136,7 +180,6 @@ public class DruidCoordinatorBalancer implements 
DruidCoordinatorHelper
     //noinspection ForLoopThatDoesntUseLoopVariable
     for (int iter = 0; (moved + unmoved) < maxSegmentsToMove; ++iter) {
       final BalancerSegmentHolder segmentToMoveHolder = 
strategy.pickSegmentToMove(toMoveFrom);
-
       if (segmentToMoveHolder != null && 
params.getAvailableSegments().contains(segmentToMoveHolder.getSegment())) {
         final DataSegment segmentToMove = segmentToMoveHolder.getSegment();
         final ImmutableDruidServer fromServer = 
segmentToMoveHolder.getFromServer();
@@ -154,8 +197,11 @@ public class DruidCoordinatorBalancer implements 
DruidCoordinatorHelper
               strategy.findNewSegmentHomeBalancer(segmentToMove, 
toMoveToWithLoadQueueCapacityAndNotServingSegment);
 
           if (destinationHolder != null && 
!destinationHolder.getServer().equals(fromServer)) {
-            moveSegment(segmentToMoveHolder, destinationHolder.getServer(), 
params);
-            moved++;
+            if (moveSegment(segmentToMoveHolder, 
destinationHolder.getServer(), params)) {
+              moved++;
+            } else {
+              unmoved++;
+            }
           } else {
             log.debug("Segment [%s] is 'optimally' placed.", 
segmentToMove.getId());
             unmoved++;
@@ -174,25 +220,10 @@ public class DruidCoordinatorBalancer implements 
DruidCoordinatorHelper
         break;
       }
     }
-
-    if (unmoved == maxSegmentsToMove) {
-      // Cluster should be alive and constantly adjusting
-      log.info("No good moves found in tier [%s]", tier);
-    }
-    stats.addToTieredStat("unmovedCount", tier, unmoved);
-    stats.addToTieredStat("movedCount", tier, moved);
-    if (params.getCoordinatorDynamicConfig().emitBalancingStats()) {
-      strategy.emitStats(tier, stats, toMoveFrom);
-    }
-    log.info(
-        "[%s]: Segments Moved: [%d] Segments Let Alone: [%d]",
-        tier,
-        moved,
-        unmoved
-    );
+    return new Pair<>(moved, unmoved);
   }
 
-  protected void moveSegment(
+  protected boolean moveSegment(
       final BalancerSegmentHolder segment,
       final ImmutableDruidServer toServer,
       final DruidCoordinatorRuntimeParams params
@@ -221,6 +252,7 @@ public class DruidCoordinatorBalancer implements 
DruidCoordinatorHelper
             segmentToMove,
             callback
         );
+        return true;
       }
       catch (Exception e) {
         log.makeAlert(e, StringUtils.format("[%s] : Moving exception", 
segmentId)).emit();
@@ -229,5 +261,6 @@ public class DruidCoordinatorBalancer implements 
DruidCoordinatorHelper
         }
       }
     }
+    return false;
   }
 }
diff --git 
a/server/src/main/java/org/apache/druid/server/coordinator/helper/DruidCoordinatorSegmentKiller.java
 
b/server/src/main/java/org/apache/druid/server/coordinator/helper/DruidCoordinatorSegmentKiller.java
index b907e75..3f719d3 100644
--- 
a/server/src/main/java/org/apache/druid/server/coordinator/helper/DruidCoordinatorSegmentKiller.java
+++ 
b/server/src/main/java/org/apache/druid/server/coordinator/helper/DruidCoordinatorSegmentKiller.java
@@ -83,7 +83,7 @@ public class DruidCoordinatorSegmentKiller implements 
DruidCoordinatorHelper
   public DruidCoordinatorRuntimeParams run(DruidCoordinatorRuntimeParams 
params)
   {
     boolean killAllDataSources = 
params.getCoordinatorDynamicConfig().isKillAllDataSources();
-    Collection<String> whitelist = 
params.getCoordinatorDynamicConfig().getKillDataSourceWhitelist();
+    Collection<String> whitelist = 
params.getCoordinatorDynamicConfig().getKillableDataSources();
 
     if (killAllDataSources && whitelist != null && !whitelist.isEmpty()) {
       log.error("killAllDataSources can't be true when killDataSourceWhitelist 
is non-empty, No kill tasks are scheduled.");
diff --git 
a/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java
 
b/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java
index 2173593..b28f569 100644
--- 
a/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java
+++ 
b/server/src/main/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRule.java
@@ -46,8 +46,9 @@ public abstract class BroadcastDistributionRule implements 
Rule
     } else {
       params.getDruidCluster().getAllServers().forEach(
           eachHolder -> {
-            if (colocatedDataSources.stream()
-                                    .anyMatch(source -> 
eachHolder.getServer().getDataSource(source) != null)) {
+            if (!eachHolder.isInMaintenance()
+                && colocatedDataSources.stream()
+                                       .anyMatch(source -> 
eachHolder.getServer().getDataSource(source) != null)) {
               loadServerHolders.add(eachHolder);
             } else if (eachHolder.isServingSegment(segment)) {
               if (!eachHolder.getPeon().getSegmentsToDrop().contains(segment)) 
{
diff --git 
a/server/src/main/java/org/apache/druid/server/coordinator/rules/LoadRule.java 
b/server/src/main/java/org/apache/druid/server/coordinator/rules/LoadRule.java
index 41f6141..3de93cf 100644
--- 
a/server/src/main/java/org/apache/druid/server/coordinator/rules/LoadRule.java
+++ 
b/server/src/main/java/org/apache/druid/server/coordinator/rules/LoadRule.java
@@ -153,8 +153,8 @@ public abstract class LoadRule implements Rule
       log.makeAlert("Tier[%s] has no servers! Check your cluster 
configuration!", tier).emit();
       return Collections.emptyList();
     }
-
-    return queue.stream().filter(predicate).collect(Collectors.toList());
+    Predicate<ServerHolder> isNotInMaintenance = s -> !s.isInMaintenance();
+    return 
queue.stream().filter(isNotInMaintenance.and(predicate)).collect(Collectors.toList());
   }
 
   /**
@@ -382,21 +382,38 @@ public abstract class LoadRule implements Rule
       final BalancerStrategy balancerStrategy
   )
   {
-    int numDropped = 0;
-
-    final NavigableSet<ServerHolder> isServingSubset =
-        holdersInTier.stream().filter(s -> 
s.isServingSegment(segment)).collect(Collectors.toCollection(TreeSet::new));
+    Map<Boolean, TreeSet<ServerHolder>> holders = holdersInTier.stream()
+                                                               .filter(s -> 
s.isServingSegment(segment))
+                                                               
.collect(Collectors.partitioningBy(
+                                                                   
ServerHolder::isInMaintenance,
+                                                                   
Collectors.toCollection(TreeSet::new)
+                                                               ));
+    TreeSet<ServerHolder> maintenanceServers = holders.get(true);
+    TreeSet<ServerHolder> availableServers = holders.get(false);
+    int left = dropSegmentFromServers(balancerStrategy, segment, 
maintenanceServers, numToDrop);
+    if (left > 0) {
+      left = dropSegmentFromServers(balancerStrategy, segment, 
availableServers, left);
+    }
+    if (left != 0) {
+      log.warn("Wtf, holder was null?  I have no servers serving [%s]?", 
segment.getId());
+    }
+    return numToDrop - left;
+  }
 
-    final Iterator<ServerHolder> iterator = 
balancerStrategy.pickServersToDrop(segment, isServingSubset);
+  private static int dropSegmentFromServers(
+      BalancerStrategy balancerStrategy,
+      DataSegment segment,
+      NavigableSet<ServerHolder> holders, int numToDrop
+  )
+  {
+    final Iterator<ServerHolder> iterator = 
balancerStrategy.pickServersToDrop(segment, holders);
 
-    while (numDropped < numToDrop) {
+    while (numToDrop > 0) {
       if (!iterator.hasNext()) {
-        log.warn("Wtf, holder was null?  I have no servers serving [%s]?", 
segment.getId());
         break;
       }
 
       final ServerHolder holder = iterator.next();
-
       if (holder.isServingSegment(segment)) {
         log.info(
             "Dropping segment [%s] on server [%s] in tier [%s]",
@@ -405,7 +422,7 @@ public abstract class LoadRule implements Rule
             holder.getServer().getTier()
         );
         holder.getPeon().dropSegment(segment, null);
-        ++numDropped;
+        numToDrop--;
       } else {
         log.warn(
             "Server [%s] is no longer serving segment [%s], skipping drop.",
@@ -414,8 +431,7 @@ public abstract class LoadRule implements Rule
         );
       }
     }
-
-    return numDropped;
+    return numToDrop;
   }
 
   protected static void validateTieredReplicants(final Map<String, Integer> 
tieredReplicants)
diff --git 
a/server/src/main/java/org/apache/druid/server/http/CoordinatorDynamicConfigsResource.java
 
b/server/src/main/java/org/apache/druid/server/http/CoordinatorDynamicConfigsResource.java
index 85bdd33..488b0e7 100644
--- 
a/server/src/main/java/org/apache/druid/server/http/CoordinatorDynamicConfigsResource.java
+++ 
b/server/src/main/java/org/apache/druid/server/http/CoordinatorDynamicConfigsResource.java
@@ -19,12 +19,12 @@
 
 package org.apache.druid.server.http;
 
-import com.google.common.collect.ImmutableMap;
 import com.sun.jersey.spi.container.ResourceFilters;
 import org.apache.druid.audit.AuditInfo;
 import org.apache.druid.audit.AuditManager;
 import org.apache.druid.common.config.ConfigManager.SetResult;
 import org.apache.druid.common.config.JacksonConfigManager;
+import org.apache.druid.common.utils.ServletResourceUtils;
 import org.apache.druid.java.util.common.Intervals;
 import org.apache.druid.server.coordinator.CoordinatorDynamicConfig;
 import org.apache.druid.server.http.security.ConfigResourceFilter;
@@ -80,19 +80,26 @@ public class CoordinatorDynamicConfigsResource
       @Context HttpServletRequest req
   )
   {
-    CoordinatorDynamicConfig current = 
CoordinatorDynamicConfig.current(manager);
+    try {
+      CoordinatorDynamicConfig current = 
CoordinatorDynamicConfig.current(manager);
 
-    final SetResult setResult = manager.set(
-        CoordinatorDynamicConfig.CONFIG_KEY,
-        dynamicConfigBuilder.build(current),
-        new AuditInfo(author, comment, req.getRemoteAddr())
-    );
+      final SetResult setResult = manager.set(
+          CoordinatorDynamicConfig.CONFIG_KEY,
+          dynamicConfigBuilder.build(current),
+          new AuditInfo(author, comment, req.getRemoteAddr())
+      );
 
-    if (setResult.isOk()) {
-      return Response.ok().build();
-    } else {
+      if (setResult.isOk()) {
+        return Response.ok().build();
+      } else {
+        return Response.status(Response.Status.BAD_REQUEST)
+                       
.entity(ServletResourceUtils.sanitizeException(setResult.getException()))
+                       .build();
+      }
+    }
+    catch (IllegalArgumentException e) {
       return Response.status(Response.Status.BAD_REQUEST)
-                     .entity(ImmutableMap.of("error", 
setResult.getException()))
+                     .entity(ServletResourceUtils.sanitizeException(e))
                      .build();
     }
   }
@@ -119,7 +126,7 @@ public class CoordinatorDynamicConfigsResource
       }
       catch (IllegalArgumentException e) {
         return Response.status(Response.Status.BAD_REQUEST)
-                       .entity(ImmutableMap.<String, Object>of("error", 
e.getMessage()))
+                       .entity(ServletResourceUtils.sanitizeException(e))
                        .build();
       }
     }
diff --git 
a/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerProfiler.java
 
b/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerProfiler.java
index 8e325a3..719a2b4 100644
--- 
a/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerProfiler.java
+++ 
b/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerProfiler.java
@@ -27,7 +27,6 @@ import org.apache.druid.java.util.common.DateTimes;
 import org.apache.druid.java.util.emitter.EmittingLogger;
 import org.apache.druid.java.util.emitter.service.ServiceEmitter;
 import org.apache.druid.metadata.MetadataRuleManager;
-import org.apache.druid.server.coordinator.helper.DruidCoordinatorBalancer;
 import org.apache.druid.server.coordinator.helper.DruidCoordinatorRuleRunner;
 import org.apache.druid.server.coordinator.rules.PeriodLoadRule;
 import org.apache.druid.server.coordinator.rules.Rule;
@@ -139,7 +138,7 @@ public class DruidCoordinatorBalancerProfiler
                                             serverHolderList.stream().collect(
                                                 Collectors.toCollection(
                                                     () -> new TreeSet<>(
-                                                        
DruidCoordinatorBalancer.percentUsedComparator
+                                                        
DruidCoordinatorBalancerTester.percentUsedComparator
                                                     )
                                                 )
                                             )
@@ -170,7 +169,7 @@ public class DruidCoordinatorBalancerProfiler
                                                 
serverHolderList.stream().collect(
                                                     Collectors.toCollection(
                                                         () -> new TreeSet<>(
-                                                            
DruidCoordinatorBalancer.percentUsedComparator
+                                                            
DruidCoordinatorBalancerTester.percentUsedComparator
                                                         )
                                                     )
                                                 )
@@ -232,7 +231,7 @@ public class DruidCoordinatorBalancerProfiler
                                             ).collect(
                                                 Collectors.toCollection(
                                                     () -> new TreeSet<>(
-                                                        
DruidCoordinatorBalancer.percentUsedComparator
+                                                        
DruidCoordinatorBalancerTester.percentUsedComparator
                                                     )
                                                 )
                                             )
diff --git 
a/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerTest.java
 
b/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerTest.java
index 02c3540..67521e3 100644
--- 
a/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerTest.java
+++ 
b/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerTest.java
@@ -21,11 +21,12 @@ package org.apache.druid.server.coordinator;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 import org.apache.druid.client.ImmutableDruidServer;
 import org.apache.druid.java.util.common.DateTimes;
-import org.apache.druid.server.coordinator.helper.DruidCoordinatorBalancer;
+import org.apache.druid.server.coordination.ServerType;
 import org.apache.druid.timeline.DataSegment;
 import org.apache.druid.timeline.partition.NoneShardSpec;
 import org.easymock.EasyMock;
@@ -37,15 +38,20 @@ import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
-import java.util.TreeSet;
 import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+
 /**
  */
 public class DruidCoordinatorBalancerTest
@@ -193,6 +199,187 @@ public class DruidCoordinatorBalancerTest
     Assert.assertEquals(2, 
params.getCoordinatorStats().getTieredStat("movedCount", "normal"));
   }
 
+  /**
+   * Server 1 has 2 segments.
+   * Server 2 (maintenance) has 2 segments.
+   * Server 3 is empty.
+   * Maintenance has priority 7.
+   * Max segments to move is 3.
+   * 2 (of 2) segments should be moved from Server 2 and 1 (of 2) from Server 
1.
+   */
+  @Test
+  public void testMoveMaintenancePriority()
+  {
+    mockDruidServer(druidServer1, "1", "normal", 30L, 100L, 
Arrays.asList(segment1, segment2));
+    mockDruidServer(druidServer2, "2", "normal", 30L, 100L, 
Arrays.asList(segment3, segment4));
+    mockDruidServer(druidServer3, "3", "normal", 0L, 100L, 
Collections.emptyList());
+
+    EasyMock.replay(druidServer4);
+
+    mockCoordinator(coordinator);
+
+    BalancerStrategy strategy = EasyMock.createMock(BalancerStrategy.class);
+    EasyMock.expect(strategy.pickSegmentToMove(ImmutableList.of(new 
ServerHolder(druidServer2, peon2, false))))
+            .andReturn(new BalancerSegmentHolder(druidServer2, segment3))
+            .andReturn(new BalancerSegmentHolder(druidServer2, segment4));
+    EasyMock.expect(strategy.pickSegmentToMove(anyObject()))
+            .andReturn(new BalancerSegmentHolder(druidServer1, segment1))
+            .andReturn(new BalancerSegmentHolder(druidServer1, segment2));
+
+    EasyMock.expect(strategy.findNewSegmentHomeBalancer(anyObject(), 
anyObject()))
+            .andReturn(new ServerHolder(druidServer3, peon3))
+            .anyTimes();
+    replay(strategy);
+
+    DruidCoordinatorRuntimeParams params = defaultRuntimeParamsBuilder(
+        ImmutableList.of(druidServer1, druidServer2, druidServer3),
+        ImmutableList.of(peon1, peon2, peon3),
+        ImmutableList.of(false, true, false)
+    )
+        .withDynamicConfigs(
+            CoordinatorDynamicConfig.builder()
+                                    .withMaxSegmentsToMove(3)
+                                    .withMaintenanceModeSegmentsPriority(6)
+                                    .build() // ceil(3 * 0.6) = 2 segments 
from servers in maintenance
+        )
+        .withBalancerStrategy(strategy)
+        .build();
+
+    params = new DruidCoordinatorBalancerTester(coordinator).run(params);
+    Assert.assertEquals(3L, 
params.getCoordinatorStats().getTieredStat("movedCount", "normal"));
+    Assert.assertThat(peon3.getSegmentsToLoad(), 
is(equalTo(ImmutableSet.of(segment1, segment3, segment4))));
+  }
+
+  @Test
+  public void testZeroMaintenancePriority()
+  {
+    DruidCoordinatorRuntimeParams params = 
setupParamsForMaintenancePriority(0);
+    params = new DruidCoordinatorBalancerTester(coordinator).run(params);
+    Assert.assertEquals(1L, 
params.getCoordinatorStats().getTieredStat("movedCount", "normal"));
+    Assert.assertThat(peon3.getSegmentsToLoad(), 
is(equalTo(ImmutableSet.of(segment1))));
+  }
+
+  @Test
+  public void testMaxMaintenancePriority()
+  {
+    DruidCoordinatorRuntimeParams params = 
setupParamsForMaintenancePriority(10);
+    params = new DruidCoordinatorBalancerTester(coordinator).run(params);
+    Assert.assertEquals(1L, 
params.getCoordinatorStats().getTieredStat("movedCount", "normal"));
+    Assert.assertThat(peon3.getSegmentsToLoad(), 
is(equalTo(ImmutableSet.of(segment2))));
+  }
+
+  /**
+   * Should balance segments as usual (ignoring priority) with empty 
maintenanceList.
+   */
+  @Test
+  public void testMoveMaintenancePriorityWithNoMaintenance()
+  {
+    mockDruidServer(druidServer1, "1", "normal", 30L, 100L, 
Arrays.asList(segment1, segment2));
+    mockDruidServer(druidServer2, "2", "normal", 0L, 100L, 
Arrays.asList(segment3, segment4));
+    mockDruidServer(druidServer3, "3", "normal", 0L, 100L, 
Collections.emptyList());
+
+    EasyMock.replay(druidServer4);
+
+    mockCoordinator(coordinator);
+
+    BalancerStrategy strategy = EasyMock.createMock(BalancerStrategy.class);
+    EasyMock.expect(strategy.pickSegmentToMove(anyObject()))
+        .andReturn(new BalancerSegmentHolder(druidServer1, segment1))
+        .andReturn(new BalancerSegmentHolder(druidServer1, segment2))
+        .andReturn(new BalancerSegmentHolder(druidServer2, segment3))
+        .andReturn(new BalancerSegmentHolder(druidServer2, segment4));
+
+    EasyMock.expect(strategy.findNewSegmentHomeBalancer(anyObject(), 
anyObject()))
+            .andReturn(new ServerHolder(druidServer3, peon3))
+            .anyTimes();
+    replay(strategy);
+
+    DruidCoordinatorRuntimeParams params = defaultRuntimeParamsBuilder(
+        ImmutableList.of(druidServer1, druidServer2, druidServer3),
+        ImmutableList.of(peon1, peon2, peon3),
+        ImmutableList.of(false, false, false)
+    )
+        .withDynamicConfigs(
+            
CoordinatorDynamicConfig.builder().withMaxSegmentsToMove(3).withMaintenanceModeSegmentsPriority(9).build()
+        )
+        .withBalancerStrategy(strategy)
+        .build();
+
+    params = new DruidCoordinatorBalancerTester(coordinator).run(params);
+    Assert.assertEquals(3L, 
params.getCoordinatorStats().getTieredStat("movedCount", "normal"));
+    Assert.assertThat(peon3.getSegmentsToLoad(), 
is(equalTo(ImmutableSet.of(segment1, segment2, segment3))));
+  }
+
+  /**
+   * Shouldn't move segments to a server in maintenance mode.
+   */
+  @Test
+  public void testMoveToServerInMaintenance()
+  {
+    mockDruidServer(druidServer1, "1", "normal", 30L, 100L, segments);
+    mockDruidServer(druidServer2, "2", "normal", 0L, 100L, 
Collections.emptyList());
+
+    replay(druidServer3);
+    replay(druidServer4);
+
+    mockCoordinator(coordinator);
+
+    BalancerStrategy strategy = EasyMock.createMock(BalancerStrategy.class);
+    EasyMock.expect(strategy.pickSegmentToMove(anyObject()))
+            .andReturn(new BalancerSegmentHolder(druidServer1, segment1))
+            .anyTimes();
+    EasyMock.expect(strategy.findNewSegmentHomeBalancer(anyObject(), 
anyObject())).andAnswer(() -> {
+      List<ServerHolder> holders = (List<ServerHolder>) 
EasyMock.getCurrentArguments()[1];
+      return holders.get(0);
+    }).anyTimes();
+    replay(strategy);
+
+    DruidCoordinatorRuntimeParams params = defaultRuntimeParamsBuilder(
+        ImmutableList.of(druidServer1, druidServer2),
+        ImmutableList.of(peon1, peon2),
+        ImmutableList.of(false, true)
+    )
+        .withBalancerStrategy(strategy)
+        .build();
+
+    params = new DruidCoordinatorBalancerTester(coordinator).run(params);
+    Assert.assertEquals(0, 
params.getCoordinatorStats().getTieredStat("movedCount", "normal"));
+  }
+
+  @Test
+  public void testMoveFromServerInMaintenance()
+  {
+    mockDruidServer(druidServer1, "1", "normal", 30L, 100L, segments);
+    mockDruidServer(druidServer2, "2", "normal", 0L, 100L, 
Collections.emptyList());
+
+    replay(druidServer3);
+    replay(druidServer4);
+
+    mockCoordinator(coordinator);
+
+    ServerHolder holder2 = new ServerHolder(druidServer2, peon2, false);
+    BalancerStrategy strategy = EasyMock.createMock(BalancerStrategy.class);
+    EasyMock.expect(strategy.pickSegmentToMove(anyObject()))
+            .andReturn(new BalancerSegmentHolder(druidServer1, segment1))
+            .once();
+    EasyMock.expect(strategy.findNewSegmentHomeBalancer(anyObject(), 
anyObject())).andReturn(holder2).once();
+    replay(strategy);
+
+    DruidCoordinatorRuntimeParams params = defaultRuntimeParamsBuilder(
+        ImmutableList.of(druidServer1, druidServer2),
+        ImmutableList.of(peon1, peon2),
+        ImmutableList.of(true, false)
+    )
+        
.withDynamicConfigs(CoordinatorDynamicConfig.builder().withMaxSegmentsToMove(1).build())
+        .withBalancerStrategy(strategy)
+        .build();
+
+    params = new DruidCoordinatorBalancerTester(coordinator).run(params);
+    Assert.assertEquals(1, 
params.getCoordinatorStats().getTieredStat("movedCount", "normal"));
+    Assert.assertEquals(0, peon1.getNumberOfSegmentsInQueue());
+    Assert.assertEquals(1, peon2.getNumberOfSegmentsInQueue());
+  }
+
   @Test
   public void testMoveMaxLoadQueueServerBalancer()
   {
@@ -315,6 +502,19 @@ public class DruidCoordinatorBalancerTest
       List<LoadQueuePeon> peons
   )
   {
+    return defaultRuntimeParamsBuilder(
+        druidServers,
+        peons,
+        druidServers.stream().map(s -> false).collect(Collectors.toList())
+    );
+  }
+
+  private DruidCoordinatorRuntimeParams.Builder defaultRuntimeParamsBuilder(
+      List<ImmutableDruidServer> druidServers,
+      List<LoadQueuePeon> peons,
+      List<Boolean> maintenance
+  )
+  {
     return DruidCoordinatorRuntimeParams
         .newBuilder()
         .withDruidCluster(
@@ -324,12 +524,8 @@ public class DruidCoordinatorBalancerTest
                     "normal",
                     IntStream
                         .range(0, druidServers.size())
-                        .mapToObj(i -> new ServerHolder(druidServers.get(i), 
peons.get(i)))
-                        .collect(
-                            Collectors.toCollection(
-                                () -> new 
TreeSet<>(DruidCoordinatorBalancer.percentUsedComparator)
-                            )
-                        )
+                        .mapToObj(i -> new ServerHolder(druidServers.get(i), 
peons.get(i), maintenance.get(i)))
+                        .collect(Collectors.toSet())
                 )
             )
         )
@@ -349,7 +545,7 @@ public class DruidCoordinatorBalancerTest
         .withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"));
   }
 
-  private void mockDruidServer(
+  private static void mockDruidServer(
       ImmutableDruidServer druidServer,
       String name,
       String tier,
@@ -363,17 +559,18 @@ public class DruidCoordinatorBalancerTest
     
EasyMock.expect(druidServer.getCurrSize()).andReturn(currentSize).atLeastOnce();
     EasyMock.expect(druidServer.getMaxSize()).andReturn(maxSize).atLeastOnce();
     EasyMock.expect(druidServer.getSegments()).andReturn(segments).anyTimes();
+    EasyMock.expect(druidServer.getHost()).andReturn(name).anyTimes();
+    
EasyMock.expect(druidServer.getType()).andReturn(ServerType.HISTORICAL).anyTimes();
     if (!segments.isEmpty()) {
       segments.forEach(
           s -> 
EasyMock.expect(druidServer.getSegment(s.getId())).andReturn(s).anyTimes()
       );
-    } else {
-      
EasyMock.expect(druidServer.getSegment(EasyMock.anyObject())).andReturn(null).anyTimes();
     }
-    EasyMock.replay(druidServer);
+    
EasyMock.expect(druidServer.getSegment(anyObject())).andReturn(null).anyTimes();
+    replay(druidServer);
   }
 
-  private void mockCoordinator(DruidCoordinator coordinator)
+  private static void mockCoordinator(DruidCoordinator coordinator)
   {
     coordinator.moveSegment(
         EasyMock.anyObject(),
@@ -424,4 +621,40 @@ public class DruidCoordinatorBalancerTest
       delegate.emitStats(tier, stats, serverHolderList);
     }
   }
+
+  private DruidCoordinatorRuntimeParams setupParamsForMaintenancePriority(int 
priority)
+  {
+    mockDruidServer(druidServer1, "1", "normal", 30L, 100L, 
Arrays.asList(segment1, segment3));
+    mockDruidServer(druidServer2, "2", "normal", 30L, 100L, 
Arrays.asList(segment2, segment3));
+    mockDruidServer(druidServer3, "3", "normal", 0L, 100L, 
Collections.emptyList());
+
+    EasyMock.replay(druidServer4);
+
+    mockCoordinator(coordinator);
+
+    // either maintenance servers list or general ones (ie servers list is [2] 
or [1, 3])
+    BalancerStrategy strategy = EasyMock.createMock(BalancerStrategy.class);
+    EasyMock.expect(strategy.pickSegmentToMove(ImmutableList.of(new 
ServerHolder(druidServer2, peon2, true))))
+            .andReturn(new BalancerSegmentHolder(druidServer2, segment2));
+    EasyMock.expect(strategy.pickSegmentToMove(anyObject()))
+            .andReturn(new BalancerSegmentHolder(druidServer1, segment1));
+    EasyMock.expect(strategy.findNewSegmentHomeBalancer(anyObject(), 
anyObject()))
+            .andReturn(new ServerHolder(druidServer3, peon3))
+            .anyTimes();
+    replay(strategy);
+
+    return defaultRuntimeParamsBuilder(
+        ImmutableList.of(druidServer1, druidServer2, druidServer3),
+        ImmutableList.of(peon1, peon2, peon3),
+        ImmutableList.of(false, true, false)
+    )
+        .withDynamicConfigs(
+            CoordinatorDynamicConfig.builder()
+                                    .withMaxSegmentsToMove(1)
+                                    
.withMaintenanceModeSegmentsPriority(priority)
+                                    .build()
+        )
+        .withBalancerStrategy(strategy)
+        .build();
+  }
 }
diff --git 
a/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerTester.java
 
b/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerTester.java
index 3a96d4b..fad85d0 100644
--- 
a/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerTester.java
+++ 
b/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorBalancerTester.java
@@ -25,15 +25,25 @@ import 
org.apache.druid.server.coordinator.helper.DruidCoordinatorBalancer;
 import org.apache.druid.timeline.DataSegment;
 import org.apache.druid.timeline.SegmentId;
 
+import java.util.Comparator;
+
 public class DruidCoordinatorBalancerTester extends DruidCoordinatorBalancer
 {
+  public static final Comparator<ServerHolder> percentUsedComparator = 
(ServerHolder a, ServerHolder b) -> {
+    int c = Double.compare(a.getPercentUsed(), b.getPercentUsed());
+    if (c == 0) {
+      return a.getServer().getName().compareTo(b.getServer().getName());
+    }
+    return c;
+  };
+
   public DruidCoordinatorBalancerTester(DruidCoordinator coordinator)
   {
     super(coordinator);
   }
 
   @Override
-  protected void moveSegment(
+  protected boolean moveSegment(
       final BalancerSegmentHolder segment,
       final ImmutableDruidServer toServer,
       final DruidCoordinatorRuntimeParams params
@@ -64,10 +74,12 @@ public class DruidCoordinatorBalancerTester extends 
DruidCoordinatorBalancer
         dropPeon.markSegmentToDrop(segment.getSegment());
 
         currentlyMovingSegments.get("normal").put(segmentId, segment);
+        return true;
       }
       catch (Exception e) {
         log.info(e, StringUtils.format("[%s] : Moving exception", segmentId));
       }
     }
+    return false;
   }
 }
diff --git 
a/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorRuleRunnerTest.java
 
b/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorRuleRunnerTest.java
index 0996058..80dfd82 100644
--- 
a/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorRuleRunnerTest.java
+++ 
b/server/src/test/java/org/apache/druid/server/coordinator/DruidCoordinatorRuleRunnerTest.java
@@ -946,9 +946,9 @@ public class DruidCoordinatorRuleRunnerTest
         ImmutableMap.of(
             "normal",
             Stream.of(
-                new ServerHolder(server1.toImmutableDruidServer(), mockPeon),
-                new ServerHolder(server2.toImmutableDruidServer(), 
anotherMockPeon),
-                new ServerHolder(server3.toImmutableDruidServer(), 
anotherMockPeon)
+                new ServerHolder(server1.toImmutableDruidServer(), mockPeon, 
false),
+                new ServerHolder(server2.toImmutableDruidServer(), 
anotherMockPeon, false),
+                new ServerHolder(server3.toImmutableDruidServer(), 
anotherMockPeon, false)
             ).collect(Collectors.toCollection(() -> new 
TreeSet<>(Collections.reverseOrder())))
         )
     );
diff --git 
a/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleTest.java
 
b/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleTest.java
index 045b160..359fb68 100644
--- 
a/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleTest.java
+++ 
b/server/src/test/java/org/apache/druid/server/coordinator/rules/BroadcastDistributionRuleTest.java
@@ -21,7 +21,6 @@ package org.apache.druid.server.coordinator.rules;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
 import org.apache.druid.client.DruidServer;
 import org.apache.druid.java.util.common.DateTimes;
 import org.apache.druid.java.util.common.Intervals;
@@ -58,6 +57,10 @@ public class BroadcastDistributionRuleTest
   private final List<DataSegment> largeSegments = new ArrayList<>();
   private final List<DataSegment> largeSegments2 = new ArrayList<>();
   private DataSegment smallSegment;
+  private DruidCluster secondCluster;
+  private ServerHolder generalServer;
+  private ServerHolder maintenanceServer2;
+  private ServerHolder maintenanceServer1;
 
   @Before
   public void setUp()
@@ -197,6 +200,50 @@ public class BroadcastDistributionRuleTest
         )
     );
 
+    generalServer = new ServerHolder(
+        new DruidServer(
+            "general",
+            "host1",
+            null,
+            100,
+            ServerType.HISTORICAL,
+            "tier1",
+            0
+        ).addDataSegment(largeSegments.get(0))
+         .toImmutableDruidServer(),
+        new LoadQueuePeonTester()
+    );
+
+    maintenanceServer1 = new ServerHolder(
+        new DruidServer(
+            "maintenance1",
+            "host2",
+            null,
+            100,
+            ServerType.HISTORICAL,
+            "tier1",
+            0
+        ).addDataSegment(smallSegment)
+         .toImmutableDruidServer(),
+        new LoadQueuePeonTester(),
+        true
+    );
+
+    maintenanceServer2 = new ServerHolder(
+        new DruidServer(
+            "maintenance2",
+            "host3",
+            null,
+            100,
+            ServerType.HISTORICAL,
+            "tier1",
+            0
+        ).addDataSegment(largeSegments.get(1))
+         .toImmutableDruidServer(),
+        new LoadQueuePeonTester(),
+        true
+    );
+
     druidCluster = new DruidCluster(
         null,
         ImmutableMap.of(
@@ -214,6 +261,18 @@ public class BroadcastDistributionRuleTest
             ).collect(Collectors.toCollection(() -> new 
TreeSet<>(Collections.reverseOrder())))
         )
     );
+
+    secondCluster = new DruidCluster(
+        null,
+        ImmutableMap.of(
+            "tier1",
+            Stream.of(
+                generalServer,
+                maintenanceServer1,
+                maintenanceServer2
+            ).collect(Collectors.toCollection(() -> new 
TreeSet<>(Collections.reverseOrder())))
+        )
+    );
   }
 
   @Test
@@ -227,14 +286,14 @@ public class BroadcastDistributionRuleTest
                                      .withDruidCluster(druidCluster)
                                      
.withSegmentReplicantLookup(SegmentReplicantLookup.make(druidCluster))
                                      
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
-                                     .withAvailableSegments(Lists.newArrayList(
+                                     .withAvailableSegments(
                                          smallSegment,
                                          largeSegments.get(0),
                                          largeSegments.get(1),
                                          largeSegments.get(2),
                                          largeSegments2.get(0),
                                          largeSegments2.get(1)
-                                     )).build(),
+                                     ).build(),
         smallSegment
     );
 
@@ -254,6 +313,46 @@ public class BroadcastDistributionRuleTest
     
assertFalse(holderOfSmallSegment.getPeon().getSegmentsToLoad().contains(smallSegment));
   }
 
+  /**
+   * Servers:
+   * name         | segments
+   * -------------+--------------
+   * general      | large segment
+   * maintenance1 | small segment
+   * maintenance2 | large segment
+   *
+   * After running the rule for the small segment:
+   * general      | large & small segments
+   * maintenance1 |
+   * maintenance2 | large segment
+   */
+  @Test
+  public void testBroadcastWithMaintenance()
+  {
+    final ForeverBroadcastDistributionRule rule = new 
ForeverBroadcastDistributionRule(ImmutableList.of("large_source"));
+
+    CoordinatorStats stats = rule.run(
+        null,
+        DruidCoordinatorRuntimeParams.newBuilder()
+                                     .withDruidCluster(secondCluster)
+                                     
.withSegmentReplicantLookup(SegmentReplicantLookup.make(secondCluster))
+                                     
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
+                                     .withAvailableSegments(
+                                         smallSegment,
+                                         largeSegments.get(0),
+                                         largeSegments.get(1)
+                                     ).build(),
+        smallSegment
+    );
+
+    assertEquals(1L, stats.getGlobalStat(LoadRule.ASSIGNED_COUNT));
+    assertEquals(false, stats.hasPerTierStats());
+
+    assertEquals(1, generalServer.getPeon().getSegmentsToLoad().size());
+    assertEquals(1, maintenanceServer1.getPeon().getSegmentsToDrop().size());
+    assertEquals(0, maintenanceServer2.getPeon().getSegmentsToLoad().size());
+  }
+
   @Test
   public void testBroadcastToMultipleDataSources()
   {
@@ -267,14 +366,14 @@ public class BroadcastDistributionRuleTest
                                      .withDruidCluster(druidCluster)
                                      
.withSegmentReplicantLookup(SegmentReplicantLookup.make(druidCluster))
                                      
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
-                                     .withAvailableSegments(Lists.newArrayList(
+                                     .withAvailableSegments(
                                          smallSegment,
                                          largeSegments.get(0),
                                          largeSegments.get(1),
                                          largeSegments.get(2),
                                          largeSegments2.get(0),
                                          largeSegments2.get(1)
-                                     )).build(),
+                                     ).build(),
         smallSegment
     );
 
@@ -305,14 +404,14 @@ public class BroadcastDistributionRuleTest
                                      .withDruidCluster(druidCluster)
                                      
.withSegmentReplicantLookup(SegmentReplicantLookup.make(druidCluster))
                                      
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
-                                     .withAvailableSegments(Lists.newArrayList(
+                                     .withAvailableSegments(
                                          smallSegment,
                                          largeSegments.get(0),
                                          largeSegments.get(1),
                                          largeSegments.get(2),
                                          largeSegments2.get(0),
                                          largeSegments2.get(1)
-                                     )).build(),
+                                     ).build(),
         smallSegment
     );
 
diff --git 
a/server/src/test/java/org/apache/druid/server/coordinator/rules/LoadRuleTest.java
 
b/server/src/test/java/org/apache/druid/server/coordinator/rules/LoadRuleTest.java
index 262b635..9a9bcb1 100644
--- 
a/server/src/test/java/org/apache/druid/server/coordinator/rules/LoadRuleTest.java
+++ 
b/server/src/test/java/org/apache/druid/server/coordinator/rules/LoadRuleTest.java
@@ -65,6 +65,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -181,7 +182,7 @@ public class LoadRuleTest
                                      .withReplicationManager(throttler)
                                      
.withBalancerStrategy(mockBalancerStrategy)
                                      
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
-                                     
.withAvailableSegments(Collections.singletonList(segment)).build(),
+                                     .withAvailableSegments(segment).build(),
         segment
     );
 
@@ -252,7 +253,7 @@ public class LoadRuleTest
                                      .withReplicationManager(throttler)
                                      
.withBalancerStrategy(mockBalancerStrategy)
                                      
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
-                                     
.withAvailableSegments(Collections.singletonList(segment)).build(),
+                                     .withAvailableSegments(segment).build(),
         segment
     );
 
@@ -302,7 +303,7 @@ public class LoadRuleTest
                                      .withReplicationManager(throttler)
                                      
.withBalancerStrategy(mockBalancerStrategy)
                                      
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
-                                     
.withAvailableSegments(Collections.singletonList(segment)).build(),
+                                     .withAvailableSegments(segment).build(),
         segment
     );
 
@@ -392,7 +393,7 @@ public class LoadRuleTest
                                      .withReplicationManager(throttler)
                                      
.withBalancerStrategy(mockBalancerStrategy)
                                      
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
-                                     
.withAvailableSegments(Collections.singletonList(segment)).build(),
+                                     .withAvailableSegments(segment).build(),
         segment
     );
 
@@ -410,7 +411,7 @@ public class LoadRuleTest
     EasyMock.expectLastCall().atLeastOnce();
     
EasyMock.expect(mockBalancerStrategy.pickServersToDrop(EasyMock.anyObject(), 
EasyMock.anyObject()))
             .andDelegateTo(balancerStrategy)
-            .times(2);
+            .times(4);
     EasyMock.replay(throttler, mockPeon, mockBalancerStrategy);
 
     LoadRule rule = createLoadRule(ImmutableMap.of(
@@ -481,7 +482,7 @@ public class LoadRuleTest
                                      .withReplicationManager(throttler)
                                      
.withBalancerStrategy(mockBalancerStrategy)
                                      
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
-                                     
.withAvailableSegments(Collections.singletonList(segment)).build(),
+                                     .withAvailableSegments(segment).build(),
         segment
     );
 
@@ -540,7 +541,7 @@ public class LoadRuleTest
                                      .withReplicationManager(throttler)
                                      
.withBalancerStrategy(mockBalancerStrategy)
                                      
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
-                                     
.withAvailableSegments(Collections.singletonList(segment)).build(),
+                                     .withAvailableSegments(segment).build(),
         segment
     );
 
@@ -557,7 +558,7 @@ public class LoadRuleTest
     EasyMock.expectLastCall().atLeastOnce();
     
EasyMock.expect(mockBalancerStrategy.pickServersToDrop(EasyMock.anyObject(), 
EasyMock.anyObject()))
             .andDelegateTo(balancerStrategy)
-            .times(1);
+            .times(2);
     EasyMock.replay(throttler, mockPeon, mockBalancerStrategy);
 
     LoadRule rule = createLoadRule(ImmutableMap.of(
@@ -613,7 +614,7 @@ public class LoadRuleTest
                                      .withReplicationManager(throttler)
                                      
.withBalancerStrategy(mockBalancerStrategy)
                                      
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
-                                     
.withAvailableSegments(Collections.singletonList(segment)).build(),
+                                     .withAvailableSegments(segment).build(),
         segment
     );
 
@@ -670,7 +671,7 @@ public class LoadRuleTest
             .withReplicationManager(throttler)
             .withBalancerStrategy(mockBalancerStrategy)
             .withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
-            .withAvailableSegments(Arrays.asList(dataSegment1, dataSegment2, 
dataSegment3))
+            .withAvailableSegments(dataSegment1, dataSegment2, dataSegment3)
             
.withDynamicConfigs(CoordinatorDynamicConfig.builder().withMaxSegmentsInNodeLoadingQueue(2).build())
             .build();
 
@@ -685,6 +686,239 @@ public class LoadRuleTest
     EasyMock.verify(throttler, mockBalancerStrategy);
   }
 
+  /**
+   * 2 servers in different tiers, the first is in maitenance mode.
+   * Should not load a segment to the server in maintenance mode.
+   */
+  @Test
+  public void testLoadDuringMaitenance()
+  {
+    final LoadQueuePeon mockPeon1 = createEmptyPeon();
+    final LoadQueuePeon mockPeon2 = createOneCallPeonMock();
+
+    LoadRule rule = createLoadRule(ImmutableMap.of(
+        "tier1", 1,
+        "tier2", 1
+    ));
+
+    final DataSegment segment = createDataSegment("foo");
+
+    
EasyMock.expect(mockBalancerStrategy.findNewSegmentHomeReplicator(EasyMock.anyObject(),
 EasyMock.anyObject()))
+            .andDelegateTo(balancerStrategy)
+            .times(1);
+
+    EasyMock.replay(mockPeon1, mockPeon2, mockBalancerStrategy);
+
+
+    DruidCluster druidCluster = new DruidCluster(
+        null,
+        ImmutableMap.of(
+            "tier1",
+            Collections.singleton(createServerHolder("tier1", mockPeon1, 
true)),
+            "tier2",
+            Collections.singleton(createServerHolder("tier2", mockPeon2, 
false))
+        )
+    );
+
+    CoordinatorStats stats = rule.run(
+        null,
+        DruidCoordinatorRuntimeParams.newBuilder()
+                                     .withDruidCluster(druidCluster)
+                                     
.withSegmentReplicantLookup(SegmentReplicantLookup.make(druidCluster))
+                                     .withReplicationManager(throttler)
+                                     
.withBalancerStrategy(mockBalancerStrategy)
+                                     
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
+                                     .withAvailableSegments(segment).build(),
+        segment
+    );
+
+    Assert.assertEquals(1L, stats.getTieredStat(LoadRule.ASSIGNED_COUNT, 
"tier2"));
+    EasyMock.verify(mockPeon1, mockPeon2, mockBalancerStrategy);
+  }
+
+  /**
+   * 2 tiers, 2 servers each, 1 server of the second tier is in maintenance.
+   * Should not load a segment to the server in maintenance mode.
+   */
+  @Test
+  public void testLoadReplicaDuringMaitenance()
+  {
+    
EasyMock.expect(throttler.canCreateReplicant(EasyMock.anyString())).andReturn(true).anyTimes();
+
+    final LoadQueuePeon mockPeon1 = createEmptyPeon();
+    final LoadQueuePeon mockPeon2 = createOneCallPeonMock();
+    final LoadQueuePeon mockPeon3 = createOneCallPeonMock();
+    final LoadQueuePeon mockPeon4 = createOneCallPeonMock();
+
+    LoadRule rule = createLoadRule(ImmutableMap.of("tier1", 2, "tier2", 2));
+
+    final DataSegment segment = createDataSegment("foo");
+
+    throttler.registerReplicantCreation(EasyMock.eq("tier2"), 
EasyMock.anyObject(), EasyMock.anyObject());
+    EasyMock.expectLastCall().times(2);
+
+    ServerHolder holder1 = createServerHolder("tier1", mockPeon1, true);
+    ServerHolder holder2 = createServerHolder("tier1", mockPeon2, false);
+    ServerHolder holder3 = createServerHolder("tier2", mockPeon3, false);
+    ServerHolder holder4 = createServerHolder("tier2", mockPeon4, false);
+
+    EasyMock.expect(mockBalancerStrategy.findNewSegmentHomeReplicator(segment, 
ImmutableList.of(holder2)))
+            .andReturn(holder2);
+    EasyMock.expect(mockBalancerStrategy.findNewSegmentHomeReplicator(segment, 
ImmutableList.of(holder4, holder3)))
+            .andReturn(holder3);
+    EasyMock.expect(mockBalancerStrategy.findNewSegmentHomeReplicator(segment, 
ImmutableList.of(holder4)))
+            .andReturn(holder4);
+
+    EasyMock.replay(throttler, mockPeon1, mockPeon2, mockPeon3, mockPeon4, 
mockBalancerStrategy);
+
+
+    DruidCluster druidCluster = new DruidCluster(
+        null,
+        ImmutableMap.of("tier1", Arrays.asList(holder1, holder2), "tier2", 
Arrays.asList(holder3, holder4))
+    );
+
+    CoordinatorStats stats = rule.run(
+        null,
+        DruidCoordinatorRuntimeParams.newBuilder()
+                                     .withDruidCluster(druidCluster)
+                                     
.withSegmentReplicantLookup(SegmentReplicantLookup.make(druidCluster))
+                                     .withReplicationManager(throttler)
+                                     
.withBalancerStrategy(mockBalancerStrategy)
+                                     
.withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
+                                     .withAvailableSegments(segment).build(),
+        segment
+    );
+
+    Assert.assertEquals(1L, stats.getTieredStat(LoadRule.ASSIGNED_COUNT, 
"tier1"));
+    Assert.assertEquals(2L, stats.getTieredStat(LoadRule.ASSIGNED_COUNT, 
"tier2"));
+
+    EasyMock.verify(throttler, mockPeon1, mockPeon2, mockPeon3, mockPeon4, 
mockBalancerStrategy);
+  }
+
+  /**
+   * 2 servers with a segment, one server in maintenance mode.
+   * Should drop a segment from both.
+   */
+  @Test
+  public void testDropDuringMaintenance()
+  {
+    final LoadQueuePeon mockPeon = createEmptyPeon();
+    mockPeon.dropSegment(EasyMock.anyObject(), EasyMock.anyObject());
+    EasyMock.expectLastCall().times(2);
+    
EasyMock.expect(mockBalancerStrategy.pickServersToDrop(EasyMock.anyObject(), 
EasyMock.anyObject()))
+            .andDelegateTo(balancerStrategy)
+            .times(4);
+    EasyMock.replay(throttler, mockPeon, mockBalancerStrategy);
+
+    LoadRule rule = createLoadRule(ImmutableMap.of("tier1", 0));
+
+    final DataSegment segment1 = createDataSegment("foo1");
+    final DataSegment segment2 = createDataSegment("foo2");
+
+    DruidServer server1 = createServer("tier1");
+    server1.addDataSegment(segment1);
+    DruidServer server2 = createServer("tier1");
+    server2.addDataSegment(segment2);
+
+    DruidCluster druidCluster = new DruidCluster(
+        null,
+        ImmutableMap.of(
+            "tier1",
+            Arrays.asList(
+                new ServerHolder(server1.toImmutableDruidServer(), mockPeon, 
true),
+                new ServerHolder(server2.toImmutableDruidServer(), mockPeon, 
false)
+            )
+        )
+    );
+
+    DruidCoordinatorRuntimeParams params = DruidCoordinatorRuntimeParams
+        .newBuilder()
+        .withDruidCluster(druidCluster)
+        .withSegmentReplicantLookup(SegmentReplicantLookup.make(druidCluster))
+        .withReplicationManager(throttler)
+        .withBalancerStrategy(mockBalancerStrategy)
+        .withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
+        .withAvailableSegments(segment1, segment2)
+        .build();
+    CoordinatorStats stats = rule.run(
+        null,
+        params,
+        segment1
+    );
+    Assert.assertEquals(1L, stats.getTieredStat("droppedCount", "tier1"));
+    stats = rule.run(
+        null,
+        params,
+        segment2
+    );
+    Assert.assertEquals(1L, stats.getTieredStat("droppedCount", "tier1"));
+
+
+    EasyMock.verify(throttler, mockPeon);
+  }
+
+  /**
+   * 3 servers hosting 3 replicas of the segment.
+   * 1 servers is in maitenance.
+   * 1 replica is redundant.
+   * Should drop from the server in maintenance.
+   */
+  @Test
+  public void testRedundantReplicaDropDuringMaintenance()
+  {
+    final LoadQueuePeon mockPeon1 = new LoadQueuePeonTester();
+    final LoadQueuePeon mockPeon2 = new LoadQueuePeonTester();
+    final LoadQueuePeon mockPeon3 = new LoadQueuePeonTester();
+    
EasyMock.expect(mockBalancerStrategy.pickServersToDrop(EasyMock.anyObject(), 
EasyMock.anyObject()))
+            .andDelegateTo(balancerStrategy)
+            .times(4);
+    EasyMock.replay(throttler, mockBalancerStrategy);
+
+    LoadRule rule = createLoadRule(ImmutableMap.of("tier1", 2));
+
+    final DataSegment segment1 = createDataSegment("foo1");
+
+    DruidServer server1 = createServer("tier1");
+    server1.addDataSegment(segment1);
+    DruidServer server2 = createServer("tier1");
+    server2.addDataSegment(segment1);
+    DruidServer server3 = createServer("tier1");
+    server3.addDataSegment(segment1);
+
+    DruidCluster druidCluster = new DruidCluster(
+        null,
+        ImmutableMap.of(
+            "tier1",
+            Arrays.asList(
+                new ServerHolder(server1.toImmutableDruidServer(), mockPeon1, 
false),
+                new ServerHolder(server2.toImmutableDruidServer(), mockPeon2, 
true),
+                new ServerHolder(server3.toImmutableDruidServer(), mockPeon3, 
false)
+            )
+        )
+    );
+
+    DruidCoordinatorRuntimeParams params = DruidCoordinatorRuntimeParams
+        .newBuilder()
+        .withDruidCluster(druidCluster)
+        .withSegmentReplicantLookup(SegmentReplicantLookup.make(druidCluster))
+        .withReplicationManager(throttler)
+        .withBalancerStrategy(mockBalancerStrategy)
+        .withBalancerReferenceTimestamp(DateTimes.of("2013-01-01"))
+        .withAvailableSegments(segment1)
+        .build();
+    CoordinatorStats stats = rule.run(
+        null,
+        params,
+        segment1
+    );
+    Assert.assertEquals(1L, stats.getTieredStat("droppedCount", "tier1"));
+    Assert.assertEquals(0, mockPeon1.getSegmentsToDrop().size());
+    Assert.assertEquals(1, mockPeon2.getSegmentsToDrop().size());
+    Assert.assertEquals(0, mockPeon3.getSegmentsToDrop().size());
+
+    EasyMock.verify(throttler);
+  }
+
   private DataSegment createDataSegment(String dataSource)
   {
     return new DataSegment(
@@ -760,4 +994,37 @@ public class LoadRuleTest
 
     return mockPeon;
   }
+
+  private static final AtomicInteger serverId = new AtomicInteger();
+
+  private static DruidServer createServer(String tier)
+  {
+    int serverId = LoadRuleTest.serverId.incrementAndGet();
+    return new DruidServer(
+        "server" + serverId,
+        "127.0.0.1:800" + serverId,
+        null,
+        1000,
+        ServerType.HISTORICAL,
+        tier,
+        0
+    );
+  }
+
+  private static LoadQueuePeon createOneCallPeonMock()
+  {
+    final LoadQueuePeon mockPeon2 = createEmptyPeon();
+    mockPeon2.loadSegment(EasyMock.anyObject(), EasyMock.anyObject());
+    EasyMock.expectLastCall().once();
+    return mockPeon2;
+  }
+
+  private static ServerHolder createServerHolder(String tier, LoadQueuePeon 
mockPeon1, boolean maintenance)
+  {
+    return new ServerHolder(
+        createServer(tier).toImmutableDruidServer(),
+        mockPeon1,
+        maintenance
+    );
+  }
 }
diff --git 
a/server/src/test/java/org/apache/druid/server/http/CoordinatorDynamicConfigTest.java
 
b/server/src/test/java/org/apache/druid/server/http/CoordinatorDynamicConfigTest.java
index 002e197..e097925 100644
--- 
a/server/src/test/java/org/apache/druid/server/http/CoordinatorDynamicConfigTest.java
+++ 
b/server/src/test/java/org/apache/druid/server/http/CoordinatorDynamicConfigTest.java
@@ -49,6 +49,44 @@ public class CoordinatorDynamicConfigTest
                      + "  \"balancerComputeThreads\": 2, \n"
                      + "  \"emitBalancingStats\": true,\n"
                      + "  \"killDataSourceWhitelist\": 
[\"test1\",\"test2\"],\n"
+                     + "  \"maxSegmentsInNodeLoadingQueue\": 1,\n"
+                     + "  \"historicalNodesInMaintenance\": [\"host1\", 
\"host2\"],\n"
+                     + "  \"nodesInMaintenancePriority\": 9\n"
+                     + "}\n";
+
+    CoordinatorDynamicConfig actual = mapper.readValue(
+        mapper.writeValueAsString(
+            mapper.readValue(
+                jsonStr,
+                CoordinatorDynamicConfig.class
+            )
+        ),
+        CoordinatorDynamicConfig.class
+    );
+    ImmutableSet<String> maintenance = ImmutableSet.of("host1", "host2");
+    ImmutableSet<String> whitelist = ImmutableSet.of("test1", "test2");
+    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, whitelist, false, 1, 
maintenance, 9);
+
+    actual = 
CoordinatorDynamicConfig.builder().withMaintenanceList(ImmutableSet.of("host1")).build(actual);
+    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, whitelist, false, 1, 
ImmutableSet.of("host1"), 9);
+
+    actual = 
CoordinatorDynamicConfig.builder().withMaintenanceModeSegmentsPriority(5).build(actual);
+    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, whitelist, false, 1, 
ImmutableSet.of("host1"), 5);
+  }
+
+  @Test
+  public void testMaintenanceParametersBackwardCompatibility() throws Exception
+  {
+    String jsonStr = "{\n"
+                     + "  \"millisToWaitBeforeDeleting\": 1,\n"
+                     + "  \"mergeBytesLimit\": 1,\n"
+                     + "  \"mergeSegmentsLimit\" : 1,\n"
+                     + "  \"maxSegmentsToMove\": 1,\n"
+                     + "  \"replicantLifetime\": 1,\n"
+                     + "  \"replicationThrottleLimit\": 1,\n"
+                     + "  \"balancerComputeThreads\": 2, \n"
+                     + "  \"emitBalancingStats\": true,\n"
+                     + "  \"killDataSourceWhitelist\": 
[\"test1\",\"test2\"],\n"
                      + "  \"maxSegmentsInNodeLoadingQueue\": 1\n"
                      + "}\n";
 
@@ -61,7 +99,15 @@ public class CoordinatorDynamicConfigTest
         ),
         CoordinatorDynamicConfig.class
     );
-    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, ImmutableSet.of("test1", 
"test2"), false, 1);
+    ImmutableSet<String> maintenance = ImmutableSet.of();
+    ImmutableSet<String> whitelist = ImmutableSet.of("test1", "test2");
+    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, whitelist, false, 1, 
maintenance, 0);
+
+    actual = 
CoordinatorDynamicConfig.builder().withMaintenanceList(ImmutableSet.of("host1")).build(actual);
+    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, whitelist, false, 1, 
ImmutableSet.of("host1"), 0);
+
+    actual = 
CoordinatorDynamicConfig.builder().withMaintenanceModeSegmentsPriority(5).build(actual);
+    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, whitelist, false, 1, 
ImmutableSet.of("host1"), 5);
   }
 
   @Test
@@ -89,7 +135,7 @@ public class CoordinatorDynamicConfigTest
         ),
         CoordinatorDynamicConfig.class
     );
-    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, ImmutableSet.of("test1", 
"test2"), false, 1);
+    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, ImmutableSet.of("test1", 
"test2"), false, 1, ImmutableSet.of(), 0);
   }
 
   @Test
@@ -118,7 +164,7 @@ public class CoordinatorDynamicConfigTest
         CoordinatorDynamicConfig.class
     );
 
-    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, ImmutableSet.of(), true, 
1);
+    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, ImmutableSet.of(), true, 
1, ImmutableSet.of(), 0);
 
     //ensure whitelist is empty when killAllDataSources is true
     try {
@@ -163,15 +209,15 @@ public class CoordinatorDynamicConfigTest
         CoordinatorDynamicConfig.class
     );
 
-    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, ImmutableSet.of(), true, 
0);
+    assertConfig(actual, 1, 1, 1, 1, 1, 1, 2, true, ImmutableSet.of(), true, 
0, ImmutableSet.of(), 0);
   }
 
   @Test
   public void testBuilderDefaults()
   {
-
     CoordinatorDynamicConfig defaultConfig = 
CoordinatorDynamicConfig.builder().build();
-    assertConfig(defaultConfig, 900000, 524288000, 100, 5, 15, 10, 1, false, 
ImmutableSet.of(), false, 0);
+    ImmutableSet<String> emptyList = ImmutableSet.of();
+    assertConfig(defaultConfig, 900000, 524288000, 100, 5, 15, 10, 1, false, 
emptyList, false, 0, emptyList, 7);
   }
 
   @Test
@@ -184,7 +230,7 @@ public class CoordinatorDynamicConfigTest
     Assert.assertEquals(
         current,
         new CoordinatorDynamicConfig
-            .Builder(null, null, null, null, null, null, null, null, null, 
null, null, null)
+            .Builder(null, null, null, null, null, null, null, null, null, 
null, null, null, null, null)
             .build(current)
     );
   }
@@ -198,18 +244,22 @@ public class CoordinatorDynamicConfigTest
     Assert.assertEquals(config1.hashCode(), config2.hashCode());
   }
 
-  private void assertConfig(CoordinatorDynamicConfig config,
-                            long expectedMillisToWaitBeforeDeleting,
-                            long expectedMergeBytesLimit,
-                            int expectedMergeSegmentsLimit,
-                            int expectedMaxSegmentsToMove,
-                            int expectedReplicantLifetime,
-                            int expectedReplicationThrottleLimit,
-                            int expectedBalancerComputeThreads,
-                            boolean expectedEmitingBalancingStats,
-                            Set<String> expectedKillDataSourceWhitelist,
-                            boolean expectedKillAllDataSources,
-                            int expectedMaxSegmentsInNodeLoadingQueue)
+  private void assertConfig(
+      CoordinatorDynamicConfig config,
+      long expectedMillisToWaitBeforeDeleting,
+      long expectedMergeBytesLimit,
+      int expectedMergeSegmentsLimit,
+      int expectedMaxSegmentsToMove,
+      int expectedReplicantLifetime,
+      int expectedReplicationThrottleLimit,
+      int expectedBalancerComputeThreads,
+      boolean expectedEmitingBalancingStats,
+      Set<String> expectedKillableDatasources,
+      boolean expectedKillAllDataSources,
+      int expectedMaxSegmentsInNodeLoadingQueue,
+      Set<String> maintenanceList,
+      int maintenancePriority
+  )
   {
     Assert.assertEquals(expectedMillisToWaitBeforeDeleting, 
config.getMillisToWaitBeforeDeleting());
     Assert.assertEquals(expectedMergeBytesLimit, config.getMergeBytesLimit());
@@ -219,8 +269,10 @@ public class CoordinatorDynamicConfigTest
     Assert.assertEquals(expectedReplicationThrottleLimit, 
config.getReplicationThrottleLimit());
     Assert.assertEquals(expectedBalancerComputeThreads, 
config.getBalancerComputeThreads());
     Assert.assertEquals(expectedEmitingBalancingStats, 
config.emitBalancingStats());
-    Assert.assertEquals(expectedKillDataSourceWhitelist, 
config.getKillDataSourceWhitelist());
+    Assert.assertEquals(expectedKillableDatasources, 
config.getKillableDataSources());
     Assert.assertEquals(expectedKillAllDataSources, 
config.isKillAllDataSources());
     Assert.assertEquals(expectedMaxSegmentsInNodeLoadingQueue, 
config.getMaxSegmentsInNodeLoadingQueue());
+    Assert.assertEquals(maintenanceList, 
config.getHistoricalNodesInMaintenance());
+    Assert.assertEquals(maintenancePriority, 
config.getNodesInMaintenancePriority());
   }
 }


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

Reply via email to