Skip to content

Commit 5274f07

Browse files
imRishNpranikum
authored andcommitted
Add DecommissionService and helper to execute awareness attribute decommissioning (opensearch-project#4084)
* Add Executor to decommission node attribute * Decommission service implementation with cluster metadata * Master abdication changes to decommission local awareness leader * Update join validator changes to validate decommissioned node join request Signed-off-by: Rishab Nahata <[email protected]> Signed-off-by: pranikum <[email protected]>
1 parent e77e260 commit 5274f07

12 files changed

+614
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
4545
- [CCR] Add getHistoryOperationsFromTranslog method to fetch the history snapshot from translogs ([#3948](https://github.com/opensearch-project/OpenSearch/pull/3948))
4646
- [Remote Store] Change behaviour in replica recovery for remote translog enabled indices ([#4318](https://github.com/opensearch-project/OpenSearch/pull/4318))
4747
- Unmute test RelocationIT.testRelocationWhileIndexingRandom ([#4580](https://github.com/opensearch-project/OpenSearch/pull/4580))
48+
- Add DecommissionService and helper to execute awareness attribute decommissioning ([#4084](https://github.com/opensearch-project/OpenSearch/pull/4084))
49+
4850

4951
### Deprecated
5052

server/src/main/java/org/opensearch/OpenSearchException.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,18 @@ private enum OpenSearchExceptionHandle {
16081608
org.opensearch.index.shard.PrimaryShardClosedException::new,
16091609
162,
16101610
V_3_0_0
1611+
),
1612+
DECOMMISSIONING_FAILED_EXCEPTION(
1613+
org.opensearch.cluster.decommission.DecommissioningFailedException.class,
1614+
org.opensearch.cluster.decommission.DecommissioningFailedException::new,
1615+
163,
1616+
V_3_0_0
1617+
),
1618+
NODE_DECOMMISSIONED_EXCEPTION(
1619+
org.opensearch.cluster.decommission.NodeDecommissionedException.class,
1620+
org.opensearch.cluster.decommission.NodeDecommissionedException::new,
1621+
164,
1622+
V_3_0_0
16111623
);
16121624

16131625
final Class<? extends OpenSearchException> exceptionClass;

server/src/main/java/org/opensearch/cluster/ClusterModule.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.opensearch.cluster.action.index.MappingUpdatedAction;
3636
import org.opensearch.cluster.action.index.NodeMappingRefreshAction;
3737
import org.opensearch.cluster.action.shard.ShardStateAction;
38+
import org.opensearch.cluster.decommission.DecommissionAttributeMetadata;
3839
import org.opensearch.cluster.metadata.ComponentTemplateMetadata;
3940
import org.opensearch.cluster.metadata.ComposableIndexTemplateMetadata;
4041
import org.opensearch.cluster.metadata.DataStreamMetadata;
@@ -193,6 +194,12 @@ public static List<Entry> getNamedWriteables() {
193194
);
194195
registerMetadataCustom(entries, DataStreamMetadata.TYPE, DataStreamMetadata::new, DataStreamMetadata::readDiffFrom);
195196
registerMetadataCustom(entries, WeightedRoutingMetadata.TYPE, WeightedRoutingMetadata::new, WeightedRoutingMetadata::readDiffFrom);
197+
registerMetadataCustom(
198+
entries,
199+
DecommissionAttributeMetadata.TYPE,
200+
DecommissionAttributeMetadata::new,
201+
DecommissionAttributeMetadata::readDiffFrom
202+
);
196203
// Task Status (not Diffable)
197204
entries.add(new Entry(Task.Status.class, PersistentTasksNodeService.Status.NAME, PersistentTasksNodeService.Status::new));
198205
return entries;
@@ -283,6 +290,13 @@ public static List<NamedXContentRegistry.Entry> getNamedXWriteables() {
283290
WeightedRoutingMetadata::fromXContent
284291
)
285292
);
293+
entries.add(
294+
new NamedXContentRegistry.Entry(
295+
Metadata.Custom.class,
296+
new ParseField(DecommissionAttributeMetadata.TYPE),
297+
DecommissionAttributeMetadata::fromXContent
298+
)
299+
);
286300
return entries;
287301
}
288302

server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
import org.opensearch.cluster.ClusterStateTaskExecutor;
4040
import org.opensearch.cluster.NotClusterManagerException;
4141
import org.opensearch.cluster.block.ClusterBlocks;
42+
import org.opensearch.cluster.decommission.DecommissionAttribute;
43+
import org.opensearch.cluster.decommission.DecommissionAttributeMetadata;
44+
import org.opensearch.cluster.decommission.DecommissionStatus;
45+
import org.opensearch.cluster.decommission.NodeDecommissionedException;
4246
import org.opensearch.cluster.metadata.IndexMetadata;
4347
import org.opensearch.cluster.metadata.Metadata;
4448
import org.opensearch.cluster.node.DiscoveryNode;
@@ -358,6 +362,7 @@ public boolean runOnlyOnClusterManager() {
358362

359363
/**
360364
* a task indicates that the current node should become master
365+
*
361366
* @deprecated As of 2.0, because supporting inclusive language, replaced by {@link #newBecomeClusterManagerTask()}
362367
*/
363368
@Deprecated
@@ -384,8 +389,9 @@ public static Task newFinishElectionTask() {
384389
* Ensures that all indices are compatible with the given node version. This will ensure that all indices in the given metadata
385390
* will not be created with a newer version of opensearch as well as that all indices are newer or equal to the minimum index
386391
* compatibility version.
387-
* @see Version#minimumIndexCompatibilityVersion()
392+
*
388393
* @throws IllegalStateException if any index is incompatible with the given version
394+
* @see Version#minimumIndexCompatibilityVersion()
389395
*/
390396
public static void ensureIndexCompatibility(final Version nodeVersion, Metadata metadata) {
391397
Version supportedIndexVersion = nodeVersion.minimumIndexCompatibilityVersion();
@@ -415,14 +421,18 @@ public static void ensureIndexCompatibility(final Version nodeVersion, Metadata
415421
}
416422
}
417423

418-
/** ensures that the joining node has a version that's compatible with all current nodes*/
424+
/**
425+
* ensures that the joining node has a version that's compatible with all current nodes
426+
*/
419427
public static void ensureNodesCompatibility(final Version joiningNodeVersion, DiscoveryNodes currentNodes) {
420428
final Version minNodeVersion = currentNodes.getMinNodeVersion();
421429
final Version maxNodeVersion = currentNodes.getMaxNodeVersion();
422430
ensureNodesCompatibility(joiningNodeVersion, minNodeVersion, maxNodeVersion);
423431
}
424432

425-
/** ensures that the joining node has a version that's compatible with a given version range */
433+
/**
434+
* ensures that the joining node has a version that's compatible with a given version range
435+
*/
426436
public static void ensureNodesCompatibility(Version joiningNodeVersion, Version minClusterNodeVersion, Version maxClusterNodeVersion) {
427437
assert minClusterNodeVersion.onOrBefore(maxClusterNodeVersion) : minClusterNodeVersion + " > " + maxClusterNodeVersion;
428438
if (joiningNodeVersion.isCompatible(maxClusterNodeVersion) == false) {
@@ -466,13 +476,34 @@ public static void ensureMajorVersionBarrier(Version joiningNodeVersion, Version
466476
}
467477
}
468478

479+
public static void ensureNodeCommissioned(DiscoveryNode node, Metadata metadata) {
480+
DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.decommissionAttributeMetadata();
481+
if (decommissionAttributeMetadata != null) {
482+
DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute();
483+
DecommissionStatus status = decommissionAttributeMetadata.status();
484+
if (decommissionAttribute != null && status != null) {
485+
// We will let the node join the cluster if the current status is in FAILED state
486+
if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())
487+
&& status.equals(DecommissionStatus.FAILED) == false) {
488+
throw new NodeDecommissionedException(
489+
"node [{}] has decommissioned attribute [{}] with current status of decommissioning [{}]",
490+
node.toString(),
491+
decommissionAttribute.toString(),
492+
status.status()
493+
);
494+
}
495+
}
496+
}
497+
}
498+
469499
public static Collection<BiConsumer<DiscoveryNode, ClusterState>> addBuiltInJoinValidators(
470500
Collection<BiConsumer<DiscoveryNode, ClusterState>> onJoinValidators
471501
) {
472502
final Collection<BiConsumer<DiscoveryNode, ClusterState>> validators = new ArrayList<>();
473503
validators.add((node, state) -> {
474504
ensureNodesCompatibility(node.getVersion(), state.getNodes());
475505
ensureIndexCompatibility(node.getVersion(), state.getMetadata());
506+
ensureNodeCommissioned(node, state.getMetadata());
476507
});
477508
validators.addAll(onJoinValidators);
478509
return Collections.unmodifiableCollection(validators);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.cluster.decommission;
10+
11+
import org.opensearch.OpenSearchException;
12+
import org.opensearch.common.io.stream.StreamInput;
13+
14+
import java.io.IOException;
15+
16+
/**
17+
* This exception is thrown if the node is decommissioned by @{@link DecommissionService}
18+
* and this nodes needs to be removed from the cluster
19+
*
20+
* @opensearch.internal
21+
*/
22+
public class NodeDecommissionedException extends OpenSearchException {
23+
24+
public NodeDecommissionedException(String msg, Object... args) {
25+
super(msg, args);
26+
}
27+
28+
public NodeDecommissionedException(StreamInput in) throws IOException {
29+
super(in);
30+
}
31+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
/**
10+
* Decommission lifecycle classes
11+
*/
12+
package org.opensearch.cluster.decommission;

server/src/test/java/org/opensearch/ExceptionSerializationTests.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
import org.opensearch.cluster.block.ClusterBlockException;
5050
import org.opensearch.cluster.coordination.CoordinationStateRejectedException;
5151
import org.opensearch.cluster.coordination.NoClusterManagerBlockService;
52+
import org.opensearch.cluster.decommission.DecommissioningFailedException;
53+
import org.opensearch.cluster.decommission.NodeDecommissionedException;
5254
import org.opensearch.cluster.node.DiscoveryNode;
5355
import org.opensearch.cluster.routing.IllegalShardRoutingStateException;
5456
import org.opensearch.cluster.routing.ShardRouting;
@@ -860,6 +862,8 @@ public void testIds() {
860862
ids.put(160, NoSeedNodeLeftException.class);
861863
ids.put(161, ReplicationFailedException.class);
862864
ids.put(162, PrimaryShardClosedException.class);
865+
ids.put(163, DecommissioningFailedException.class);
866+
ids.put(164, NodeDecommissionedException.class);
863867

864868
Map<Class<? extends OpenSearchException>, Integer> reverse = new HashMap<>();
865869
for (Map.Entry<Integer, Class<? extends OpenSearchException>> entry : ids.entrySet()) {

server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,14 @@
3636
import org.opensearch.cluster.ClusterName;
3737
import org.opensearch.cluster.ClusterState;
3838
import org.opensearch.cluster.ClusterStateTaskExecutor;
39+
import org.opensearch.cluster.decommission.DecommissionAttribute;
40+
import org.opensearch.cluster.decommission.DecommissionAttributeMetadata;
41+
import org.opensearch.cluster.decommission.DecommissionStatus;
42+
import org.opensearch.cluster.decommission.NodeDecommissionedException;
3943
import org.opensearch.cluster.metadata.IndexMetadata;
4044
import org.opensearch.cluster.metadata.Metadata;
4145
import org.opensearch.cluster.node.DiscoveryNode;
46+
import org.opensearch.cluster.node.DiscoveryNodeRole;
4247
import org.opensearch.cluster.node.DiscoveryNodes;
4348
import org.opensearch.cluster.routing.RerouteService;
4449
import org.opensearch.cluster.routing.allocation.AllocationService;
@@ -48,7 +53,9 @@
4853
import org.opensearch.test.OpenSearchTestCase;
4954
import org.opensearch.test.VersionUtils;
5055

56+
import java.util.Collections;
5157
import java.util.HashSet;
58+
import java.util.Map;
5259

5360
import static org.hamcrest.Matchers.is;
5461
import static org.opensearch.test.VersionUtils.allVersions;
@@ -216,4 +223,67 @@ public void testIsBecomeClusterManagerTask() {
216223
JoinTaskExecutor.Task joinTaskOfClusterManager = JoinTaskExecutor.newBecomeClusterManagerTask();
217224
assertThat(joinTaskOfClusterManager.isBecomeClusterManagerTask(), is(true));
218225
}
226+
227+
public void testJoinClusterWithNoDecommission() {
228+
Settings.builder().build();
229+
Metadata.Builder metaBuilder = Metadata.builder();
230+
Metadata metadata = metaBuilder.build();
231+
DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-2"));
232+
JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata);
233+
}
234+
235+
public void testPreventJoinClusterWithDecommission() {
236+
Settings.builder().build();
237+
DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1");
238+
DecommissionStatus decommissionStatus = randomFrom(
239+
DecommissionStatus.INIT,
240+
DecommissionStatus.IN_PROGRESS,
241+
DecommissionStatus.SUCCESSFUL
242+
);
243+
DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata(
244+
decommissionAttribute,
245+
decommissionStatus
246+
);
247+
Metadata metadata = Metadata.builder().decommissionAttributeMetadata(decommissionAttributeMetadata).build();
248+
DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1"));
249+
expectThrows(NodeDecommissionedException.class, () -> JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata));
250+
}
251+
252+
public void testJoinClusterWithDifferentDecommission() {
253+
Settings.builder().build();
254+
DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1");
255+
DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.values());
256+
DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata(
257+
decommissionAttribute,
258+
decommissionStatus
259+
);
260+
Metadata metadata = Metadata.builder().decommissionAttributeMetadata(decommissionAttributeMetadata).build();
261+
262+
DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-2"));
263+
JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata);
264+
}
265+
266+
public void testJoinClusterWithDecommissionFailed() {
267+
Settings.builder().build();
268+
DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1");
269+
DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata(
270+
decommissionAttribute,
271+
DecommissionStatus.FAILED
272+
);
273+
Metadata metadata = Metadata.builder().decommissionAttributeMetadata(decommissionAttributeMetadata).build();
274+
275+
DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1"));
276+
JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata);
277+
}
278+
279+
private DiscoveryNode newDiscoveryNode(Map<String, String> attributes) {
280+
return new DiscoveryNode(
281+
randomAlphaOfLength(10),
282+
randomAlphaOfLength(10),
283+
buildNewFakeTransportAddress(),
284+
attributes,
285+
Collections.singleton(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE),
286+
Version.CURRENT
287+
);
288+
}
219289
}

0 commit comments

Comments
 (0)